jsp编程语言自从推出之日起,由于它的快速、平台无关、可扩展、面向对象等特性得到了越来越广泛的应用,越来越多的厂家开发出了各种各样的支持平台如IBM 公司的WebSphere、BEA公司的WebLogic等等,也有越来越多的网站开始将自己的平台架构在jsp 环境中。
但是随之而来的就是一系列的安全漏洞问题,如源代码暴露漏洞、远程任意命令执行漏洞等等,更为头疼的是,随着jsp 的越来越广泛的应用,安全问题也越来越多了。截止到这篇文章为止,Internet上公开的关于jsp 的漏洞问题就多达二、三十条(还不包括未公开的)。(统计数据来源于http://www.securityfocus.com)
不要轻视这些问题,想象一下,你辛辛苦苦开发出来的jsp 代码就被别人这样轻而易举的获得了,更为重要的是,你公司网站的代码被人下载后,别有用意的人就会看你的代码,从中找到一些漏洞来攻击你的公司网站,所以这些问题不容忽视。作者在sohu 上搜索了一些用jsp做的国内网站,结果发现有一些网站确实存在各种各样的漏洞,可以轻松的下载jsp源代码。
本篇文章重点在于对jsp安全问题进行分类阐述和提出解决的建议,所以每种类型的安全问题只采用了一个例子,对于其它各种漏洞的具体细节如涉及到何种软件版本何种操作系统等就不一一进行阐述了,有兴趣的读者可以到我的网站jsp 爱好者(http://jspbbs.yeah.net)或者国外的安全站点(http://www.securityfocus.com)进行查看和参考。
根据目前已经发现的jsp安全问题,我们不妨将它们分为以下几类,源代码暴露类、远程程序执行类和其他类别, 下面来看看具体的东西吧。
一、源代码暴露类
源代码暴露类别主要指的是程序源代码会以明文的方式返回给访问者.
我们知道不管是jsp还是asp、php等动态程序都是在服务器端执行的,执行后只会返回给访问者标准的html 等代码。这是理论上的东西,实际运行起来由于服务器内部机制的问题就有可能引起源代码暴露的漏洞,简单的例子只要在程序文件名后加几个简单的字符就可能获得程序代码,如常见微软asp 的global.asa+.htr、XXXX.asp%81等等漏洞。
1、添加特殊后缀引起jsp源代码暴露
在jsp中也存在着和asp这些漏洞类似的问题,如IBM Websphere Application Server 3.0.21、BEA Systems Weblogic 4.5.1、Tomcat3.1等jsp文件后缀大写漏洞;jsp 文件后加特殊字符如Resin1.2的%82、../漏洞;ServletExec的%2E、+漏洞等等。
例子:举个老一点的JSP大写例子,Tomcat3.1下在浏览器中本来是http://localhost:8080/inde.jsp,可以正常解释执行,但是如果将inde.jsp改为inde.JSP或者inde.Jsp等等试试看,你会发现浏览器会提示你下载这个文件,下载后源代码可以看个一干二净。
原因:jsp是大小写敏感的,Tomcat只会将小写的jsp后缀的文件当作是正常的jsp文件来执行,如果大写了就会引起Tomcat将inde.JSP当作是一个可以下载的文件让客户下载。老版本的WebLogic、WebShpere等都存在这个问题,现在这些公司或者发布了新版本或者发布了补丁解决了这问题。
解决办法:一是在服务器软件的网站上下载补丁;因为笔者以前用过asp 一段时间,接触了很多IIS的漏洞,它的有效解决方法是去掉不必要的映射如htr、htx等,在jsp 中我们同样可以参考IIS的解决方法,不同的是不是去掉而是添加映射,方法为在服务器设置中添加一些映射如.JSP 、.Jsp、.jsp%2E等,将他们映射到一个自己写的servlet,这个Servlet的唯一功能就是将请求导向一个自定义的类似404 not found的出错页面,不同的服务器设置的地方也不同,请参考相应的文档。第二种解决方法可以在没有补丁的时候采用。
2、插入特殊字符串引起jsp源代码暴露
还有一种是插入特殊字符串引起的漏洞,BEA WebLogic Enterprise 5.1.
文件路径开头为 "/file/" 的漏洞、IBM WebSphere 3.0.2的"/servlet/file/"文件开头漏洞等等。
例子:如IBM WebSphere 3.0.2中,如果一个请求文件的 URL 为"login.jsp":http://site.running.websphere/login.jsp,那么访问http://site.running.websphere/servlet/file/login.jsp将看到这个文件的源代码。
原因:因为IBM WebSphere 3.0.2是调用不同的 servlets 对不同的页面进行处理,如果一个请求的文件是未进行注册管理的,WebSphere 会使用一个默认的 servlet 调用。如果文件路径以"/servlet/file/"作开头这个默认的 servlet 会被调用这个请求的文件会未被分析或编译就显示出来。
解决方法:在服务器软件的网站下载最新的补丁。
3、路径权限引起的文件jsp源代码暴露
这种漏洞在正常的jsp漏洞中没有反映出来,但是笔者在写jsp程序中曾经碰到过,头疼了一阵子,最后总算解决了。我们知道,大部分的jsp应用程序在当前目录下都会有一个WEB-INF目录,这个目录通常存放的是JavaBeans编译后的class 文件,如果不给这个目录设置正常的权限,所有的class就会曝光。
例子:笔者当时采用的是Apache1.3.12加上第三方jsp软件形式的WEB服务器,因为Apache1.3.12默认的设置是可以读取目录的,如果程序在http://site.running.websphere/login.jsp,只要修改一下http://site.running.websphere/WEB-INF/所有这个目录下以及这个目录下的子目录中的class文件可以看个一干二净的,还可以下载到本机上。
也许你会说class是经过编译的,就算被人下载也没有什么关系,但是现在class 反编译为java代码的软件也很多,笔者当时采用JAD软件对下载的class文件反编译了一下,居然和原始的java文件几乎一模一样,变量名都没有变,更惊奇的是还可以重新编译为class文件正常使用。
安全问题更大的就是,笔者开始把数据库的用户名密码都写在了java代码中,现在一反编译谁都能看到数据库的重要信息。通过数据库的远程连接功能,可以轻松的进入到你的数据库中,所有信息全部在他手中。附带说一句,如果用户能获得SQL Server的用户名口令(sa 的),进入数据库中可以执行任意的dos命令如查看c:\文件、建立和删除目录等,那样整个Windows系统都不安全了(此种方法危害性更大,恕作者不公布出来)。
作者曾经偶然在网上看到过国内某个大型网站有同样的问题,而且密码也是和笔者一样写在JavaBean中的,极不安全。
解决方法:IIS以前一个有效地解决asp的漏洞就是将asp程序单独放置一个目录,目录设置上用户权限只能执行不能读取。在jsp环境下同样可以通过设置服务器的环境来解决这个问题,简单的说就是将一些比较重要的目录如WEB-INF、classes等设置上访问的权限,不允许读而取只允许执行。以apache 下解决为例,可以在httpd.conf文件中添加一目录WEB-INF并设置Deny from all等属性。
另一种比较笨的解决方法就是在每个重要目录下添加一个默认起始页面如inde.htm等,这样读取目录就会返回给访问者这个文件而不是其它了。建议采用的一种方法。
更为重要的是密码的保存问题,笔者以前在asp 开发中是采用密码文件保存在系统目录如WINNT 下的,然后写了一个com来读取这个文件,这样就算看到了asp源代码也不知道数据库信息了。在jsp中我们也可以写一个property文件,放置在WINNT系统目录下,然后用Bean来读取数据库信息,这样通过源代码知道了数据库信息存在WINNT中的.property文件里面,但也很难访问它,这样就算源代码被人知道起码数据库是安全的。
4、文件不存在引起的绝对路径暴露问题
这个问题相信大家都比较熟悉了,因为微软IIS 中也有比较多的类似问题如微软IIS5.0中的*.idc暴露绝对路径漏洞。同样的这些问题现在也转到了jsp环境中,这个漏洞暴露了web程序的绝对硬盘地址,和其他漏洞结合就具有比较大的危害了,因为这个漏洞目前还没有在国外安全网站上看到,而站长也没有一一测试过所有的jsp服务器程序,所以没有办法告诉大家那些有这个漏洞了,大家可以在自己的web服务器上测试看看。
例子:在特定的服务器软件下,访问一个不存在的jsp文件如
http://localhost:8080/fdasfas.jsp,就会返回java.servlet.ServletEception: java.io.FileNotFoundEception: c:\web\app\fadssad.jsp (???????????)这样的错误,这样就可以知道你网站在c:\web\app目录下,也许一般人不太在意,但是对于一个黑客来说就是很有帮助了。
原因:负责jsp 执行的相关Servlet中处理异常的时候没有过滤掉这种情况。
解决方法:一是下载最新的补丁;由于笔者当时的web 服务器软件没有这个补丁,经过一段时间的痛苦煎熬后,总算找到了另外一种方法,就是找到服务器软件的jsp 执行映射Servlet文件(当然是class 后缀的),将它用JAD软件反编译,在反编译后的源代码中找到处理Eception的方法,然后将方法中的处理部分全部注释掉,并将请求导向到一个自定义的出错页面中,这样问题就解决了。