爱国设计
业内动态 | 技术文章 | 相关下载 | 设计素材 | 酷站欣赏 | 网站设计 | 娱乐新区 | 论坛交流
您的位置:首页 > 技术专栏 > WEB编程 > JSP技术 >  
JSP论坛安全之旅
作者: | 时间:2005-12-14 | 来自:
浏览数: | 文章有1条评论 | 发布者:makeday
                                                       
  JSP(Java Server Pages)是开发WEB动态网站快速有效的工具,是企业应用编程中的一部分,它基于强大的Java语言,具有良好的伸缩性,与Java Enterprise API紧密地集成在一起。由于JSP与平台无关的开发特性,在开发电子商务方面具有得天独厚的优势,基于Java平台构建电子商务平台已经成为当今IT领域新时尚。在网络开发编程领域中,它与ASP和PHP齐名,并随着Java技术的日益成熟和流行。本文正是试图向读者阐述建立JSP论坛的方方面面。

JSP论坛安全之旅

  作为建立动态网页的一种WEB编程技术,Java Server Page(JSP)开始得到越来越多的应用。不经意间,我们会发现很多动态网页的后缀变成了“.jsp”。秉承Java语言的各种优势,JSP也开始被很多程序员所青睐。这是一个Java软件的典型开发流程,从中我们可以得到不少关于JSP安全的启发:(1)编写源代码(*.java文件);
(2)源代码经过编译(javac.exe命令),产生类文件(*.class);
(3)一个大的软件将包括许多Class文件,为这些文件撰写一个清单文件(manifest.mf);
(4)一些的类文件和相应的Manifest.mf文件一起打包(jar.exe命令),生成包裹文件(*.jar);
(5)Java文件具有易于被反编译的性质,为了使你的心血不被“窃取”,最后还需要对完成的文件进行“混淆(obfuscate)”,这一过程需要使用混淆器来完成,混淆后的文件可以正常运行,但反编译时就不那么容易了。而这个典型的过程,则深刻的反映于我们常见的JSP论坛或者其他JSP应用程序中。

  如何来探索Java Servlet及JSP的安全之道呢?细心的朋友会发现,一个WEB应用程序可拥有多种资源,在没有保护措施的情况下,经常会有许多敏感的信息在开放性网络上传输。所以,这种环境下的许多WEB应用程序会有一定程度的安全性要求,为了达到这种安全要求,大多数的Servlet容器会有明确的机制和结构。下面,我们从一个测试的小故事入手。

  从一个小测试谈到一个小知识

  记得还是去年,刚到据说是高手云集的威威公司上班的时候,一个新到的同事给我讲他花了半天的时间写,并做了很长时间的实践,写了个关于攻击.jsp页面的程序。下面我把具体的实现过程和大家分享一下。测试平台是Tomcat,当然,版本有点低,他的目的只是想证实一下他的某些想法。首先,他在Tomcat的WEB目录下建立了一个Hello.jsp文件,内容是:

<%out.print(hello);%>

通过IE的正常请求地址为:
http://localhost:8080/examples/jsp/hello.jsp,显示结果为:hello。然后开始具体的攻击测试。测试时,发出的请求地址为:http://localhost:8080/examples/jsp/////////hello.jsp ,浏览器上显示编译错误,错误的原因是500 java.lang.NullPointerException。这个应该是比较常见的错误了。现在,恢复正常的请求http://localhost:8080/examples/jsp/hello.jsp,问题就出现了,即出错,而且所报的错误和刚才造成它错误的请求是一样的:“500 java.lang.NullPointerException”。难道是缓存在浏览器里了吗?换台机器访问http://192.168.10.188/examples/jsp/hello.jsp。问题依然如故,哎!可怜的Hello.jsp呀!

  虽然这个问题有些弱智,不过,他的目的也达到了,即找出“.jsp”流程中存在的一些问题。所以,JSP程序同ASP一样,还是存在着很多安全上的问题的。因此,对于一心研究论坛或者其他安全信息的朋友来说,要想发现JSP的BUG,了解一些JSP的工作原理是十分重要的。

  需要指出的是,虽然是一门网络编程语言,JSP和PHP、ASP的工作机制还存在很大的区别,首次调用JSP文件时,JSP页面在执行时是编译式,而不是解释式的。首次调用JSP文件其实是执行一个编译为Servlet的过程。当浏览器向服务器请求这一个JSP文件的时候,服务器将检查自上次编译后JSP文件是否有改变,如果没有改变,就直接执行Servlet,而不用再重新编译,这样,工作效率得到了明显提高。这也是目前JSP论坛开始逐渐风靡的一个重要原因。

  小提示:Servlet是用Java编写的Server端程序,它与协议和平台无关;Servlet运行于Java-enabled WEB Server中;Java Servlet可以动态地扩展Server的能力,并采用请求-响应模式提供WEB服务;最早支持Servlet技术的是JavaSoft的Java WEB Server;Servlet的主要功能在于交互式地浏览和修改数据,生成动态WEB内容。

  说到这里,我们自然就会关心一些JSP的安全问题。一般来说,常见的JSP安全问题有源代码暴露(包括程序源代码以明文的方式返回给访问者,如添加特殊后缀引起jsp源代码暴露;插入特殊字符串引起Jsp源代码暴露;路径权限引起的文件Jsp源代码暴露;文件不存在引起的绝对路径暴露问题等)、远程程序执行类、数据库如SQL Server、Oracle 、DB2等的漏洞,操作系统漏洞等。不过,为了突出Jsp的安全问题,本文将结合目前的一些比较流行的Jsp论坛分类阐述和提出解决的建议。为了讲解方便,本文还采用一些公开了原代码的论坛实例代码,至于安装软件版本、操作系统等,可以查看安装提示。

  论坛用户管理缺陷

  为了加强实战效果,我们可以到
http://down.chinaz.com/S/5819.asp这个地址下载一个典型的论坛代码,根据提示,数据源名称为yyForum,用户名为xyworker,密码:999。到baidu、Google等网站搜索一下,我们可以看到,安装这个代码的论坛不少。仔细分析后,可以发现,用户管理的页面是user_manager.jsp文件。首先,我们看看这个系统是如何加强它的代码安全性的。其中,在代码的开始部分有一个if限制条件,代码的第三行到第十行具体如下:

<%
if ((session.getValue(UserName)==null)||(session.getValue(UserClass)==null)||(!session.getValue(UserClass).equals(系统管理员)))

%>

  其中,Session.getValue表示检索出Session的值;sendRedirect()执行后,地址栏链接会改变,相当于客户端又重新发了一个get请求,要服务器传输另一个文件过来。

  下面,我们再来看看修改用户信息的文件modifyuser_manager.jsp。典型代码如下:

<
%@page contentType=text/html; charset=gb2312 language=java import=java.sql.*,java.util.*  %>
<jsp:useBean id=yy scope=page class=yy.jdbc/>
<%!String User_Name,User_Password,sql, User_Sign;%>
<%
User_Name=request.getParameter(name);

//out.println(User_Name);
User_Password=request.getParameter(password);
User_Password=yy.ex_chinese(User_Password);
……
User_Sign=request.getParameter(sign);
User_Sign=yy.ex_chinese(User_Sign);

Connection con=yy.getConn();
Statement  stmt=con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
sql=update 用户表 set 用户密码='+User_Password+',用户性别='+User_Sex+',用户邮箱='+User_Email+',居住地址='+User_Address+',手机号码='+User_Mobile+',Oicq='+User_Oicq+',出生日期='+User_Birthay+',用户等级='+User_Class+',签名='+User_Sign+' where 用户名='+User_Name+';
//out.println(sql);
stmt.executeUpdate(sql);
out.println(<font size=2 color=blue>正在处理你的用户信息,请稍后...</font><meta http-equiv='refresh' content='2;url=user_manager.jsp'>);
%>
<jsp:include page=inc/online.jsp flush=true/>

  看看这个文件,我们就好像看到了一个简单的教学文件。现在,假设管理员提交如下地址,即
http://www.51dz.net/bbs/modifyuser_manager.jsp?modifyid=51,需要查看、修改ID为51的用户的资料(管理员默认的用户ID为51)。问题就出来了。同样的,我们可以通过搜索引擎得到如下地址,如图1所示:



 
图1

    很明显,这个用户管理文件缺乏认证,即使是普通的用户,甚至包括我们这些搭不上边的“游客”,也可以直接提交上述请求,从而将其资料一览无余,更让人动心的是,密码也是明文存储的。
  
  
http://www.51dz.net/bbs/modifyuser_manager.jsp同样是大开山门,直到恶意用户把数据更新的操作执行完毕,重定向到user_manager.jsp的时候,管理员才会看见那个显示错误的页面,但这个时候为时已晚,更谈不上“亡羊补牢”了。类似的错误存在于很多JSP的站点上,面对这样的论坛,我们能够放心的说“安全”吗?解决之道有很多,不过,最基本的要求是为每个需要加身份认证的地方加上身份认证,如果借用别人的代码,一定要对涉及到用户管理、密码认证等重要文件修改一下,照搬虽然省事,但代码毫无安全性可言。

  再就是SQL注入的问题。比如,这个典型的问题:“昨天公司的数据库被人SQL注入,9万条记录都被update了,同事写了个JSP程序来把他改回来,可是这JSP没有一点信息返回,看不到进度,在运行些什么都不知道。”不过,这和JSP程序没有什么必然的联系,根据国情,国内的网站用ASP+Access或SQLServer的占70%以上,PHP+MySQL占20%,其它的不足10%。因此,ASP的SQL注入比较常见也不足为怪。不过,SQL注入漏洞可谓是“千里之堤,溃于蚁穴”,这种漏洞在网上极为普遍,即使是JSP程序也不能幸免。归根结底,通常是由于程序员对注入不了解,或者程序过滤不严格,或者某个参数忘记检查导致。看看这个教材式的JSP程序就可以窥见一般:

Statement stmt = conn.createStatement();
String checkUser = select * from login where username = ' + userName + ' and userpassword = ' + userPassword + ';
ResultSet rs = stmt.executeQuery(checkUser);
if(rs.next())
 response.sendRedirect(SuccessLogin.jsp);
else
 response.sendRedirect(FailureLogin.jsp);

  针对这种情况,如果数据库里存在一个名叫“Tom”的用户,那么在不知道密码的情况下至少有下面几种方法可以登录:
用户名:Tom            密码:' or 'a'='a
用户名:Tom            密码:' or 1=1/*
用户名:Tom' or 1=1/*     密码:(任意)

  用户退出的安全问题

  最近,在研究一些JSP论坛的代码时,我发现,在一个有密码保护的WEB应用中,正确处理用户退出过程并不仅仅只需调用HttpSession的invalidate()方法,这样做会存在一定的安全隐患!大家可以想象:大部分浏览器上都有后退和前进按钮,允许用户后退或前进到一个页面。如果在用户在退出一个WEB应用后,按了后退按钮,浏览器就把缓存中的页面呈现给用户,这会使用户产生疑惑,他们会开始担心他们的个人数据是否安全。这样的情形我在不少的自助银行的查询机上使用“Back Space”键时,就产生过类似的困惑。再者,许多WEB应用强迫用户退出时关闭整个浏览器,这样,用户就无法点击后退按钮了。这样的问题在论坛等JSP程序中,也十分常见,有什么好的解决方法吗?

  查阅并综合一些资料后,我对实际应用的解决方法进行了大致的了解。以下是一个完整的WEB应用:

1) 用户通过浏览器访问页面;
2) WEB应用展现一个登录页面要求用户输入有效的验证信息;
3) 用户输入了用户名和密码,此时我们假设用户提供的身份验证信息是正确的,经过了验证过程,WEB应用允许用户浏览他有权访问的区域;
4) 用户想退出时,点退出按钮,WEB应用要求用户确认是否则真的需要退出,如果用户确定退出,Session结束,WEB应用重新定位到登录页面,用户可以放心的离开而不用担心他的信息会泄露;
5) 另一个用户坐到了同一台电脑前,他点击后退按钮,WEB应用不应该出现上一个用户访问过的任何一个页面。事实上,WEB应用在第二个用户提供正确的验证信息之前应当一直停留在登录页面上。


  实际应用中,比较典型的例子是:login.jsp包含了用于用户输入用户名和密码的Form。logout.jsp页包含了要求用户确认是否退出的Form。loginAction.jsp和logoutAction.jsp作为控制器分别包含了登录和退出代码。典型的解决途径是引入自定义的安全实现机制。自定义的安全认证机制普遍采用的方法是从Form中获得用户输入的认证信息,然后到关系数据库的安全域中进行认证。如果用户提供的认证信息是有效的,登录动作往HttpSession对象中注入某个对象。HttpSession存在着注入的对象则表示用户已经登录。Logout action是退出动作,它就包含了简单的删除用户名以及对用户的HttpSession对象调用invalidate()方法。比较典型的代码如下:

//...
session.removeAttribute(User);
session.invalidate();
//...

  另外,为了使登录和退出动作真正发挥作用,所有受保护的JSP页面都应该首先验证HttpSession中是否包含了用户名以确认当前用户是否已经登录。如果HttpSession中包含了用户名,也就是说用户已经登录,WEB应用则将剩余的JSP页发送给浏览器,否则,JSP页将跳转到登录页login.jsp。

  阻止浏览器缓存

  使用上述方法后,还是存在一定的问题。当在浏览器上点击后退按钮时,默认情况下浏览器不是从WEB服务器上重新获取页面,而是从浏览器缓存中载入页面。基于Java的WEB应用并未限制这一功能,在基于PHP、ASP和.NET的WEB应用中也同样存在这一问题。在用户点击后退按钮后,“从浏览器到服务器再从服务器到浏览器”这个HTTP回路并没有建立,仅仅只是用户、浏览器和缓存进行了交互。如此看来,就好像一枚硬币有两面一样,缓存的确提供了一些便利,但通常只在使用静态的HTML页面或基于图形或影响的页面你才能感受到,而另一方面,WEB应用通常是基于数据的,数据通常是频繁更改的。与从缓存中读取并显示过期的数据相比,提供最新的数据才是更重要的!

  这就要看看怎么用缓存了。查阅资料,我看到,HTTP头信息“Expires”和“Cache-Control”为应用程序服务器提供了一个控制浏览器和代理服务器上缓存的机制。HTTP头信息Expires告诉代理服务器它的缓存页面何时将过期;HTTP1.1规范中新定义的头信息Cache-Control可以通知浏览器不缓存任何页面。当点击后退按钮时,浏览器重新访问服务器已获取页面。通过使用HTTP头信息的cache控制,我们可以对页面进行保护,将如下代码适当处理即可:

//...
response.setHeader(Cache-Control,no-cache); response.setHeader(Cache-Control,no-store);
response.setDateHeader(Expires, 0);
response.setHeader(Pragma,no-cache);
String userName = (String) session.getAttribute(User);
if (null == userName) {
 request.setAttribute(Error, Session has ended. Please login.);
 RequestDispatcher rd = request.getRequestDispatcher(login.jsp);
 rd.forward(request, response);
}
//...

上面代码的意思是:通过设置头信息和检查HttpSession中的用户名确保了浏览器不缓存页面。同时,如果用户没有登录,受保护的JSP页面将不会发送到浏览器,取而代之的将是登录页面login.jsp。实际应用中,如果要求特别高,还可以记录最后登录时间,即记录每个用户的最后登录时间。对于采用关系型数据库安全域来说,可以通过在某个表中加上lastLogin等字段轻松实现。此外,为了使代码更简练有效,一些冗余的代码可以剔除掉。比如把这些加强安全的代码写到一个单独的JSP页中,通过<jsp:include>标签在其他页面也可以引用。这样问题就可以比较简单地解决了。

  其它JSP安全编程知识

  当然,JSP开发中的安全解决办法还是很多的,需要我们根据实际情况来“量体裁衣”使用。为了扩大读者的视野,下面还列举了一些常用的安全性实现方式。

  
1.Declarative Security

  Declarative security指的是表达一个应用的安全结构,包括角色,存取控制,和在一个应用的外部表单所要求的验证。在WEB application中发布描述器是实施declarative security的一种主要的工具。发布者把Application所要求的逻辑完整性映射为在特定运行环境下的安全策略。在运行时,Servlet container使用这些策略来强迫验证。

  
2.Programmatic Security

  当Declarative Security不能够完全表达一个Spplication的安全模型时,就可以使用Programmatic Security。Programmatic Security包括HttpServletRequest接口的下列方法:getRemoteUser方法返回经过客户端验证的用户名。IsUserInRole向Container的安全机制询问一个特定的用户是否在一个给定的安全角色中。GetUserPrinciple方法返回一个Java.security.Pricipal对象。这些APIs根据远程用户的逻辑角色让Servlet去完成一些逻辑判断。它也可以让Servlet去决定当前用户的主要名字。如果getRemoteUser返回NULL值(这意味着没有用户被验证),那么isUserInRole就总会返回False,getUserPrinciple总会返回NULL。

  
3.Roles

  一个Roles就是由Application Developer和Assembler所定义的一个抽象的逻辑用户组。当一个Application被发布的时候,Deployer就把这些角色映射到在运行环境中的安全认证,例如组或规则。一个Servlet container能够为规则执行一些说明或编程安全,这些规则是与调用这些Principal的安全属性所输入的要求相联系的。例如:当Peployer把一个安全角色映射为操作环境下的一个用户组,调用Principle所属的用户组就从安全属性中获得。如果Principle的用户组与在操作环境下的用户组相匹配,那么Principle 就是一个安全角色;当Deployeer把一个安全角色映射为一个在安全方针域中的Principle名时,调用Principle的确Principle名就被从安全属性中提取出来。如果两者相同的话,调用的Principle就是安全的。

 
 4.Authentication

  一个WEB 客端能够使用下面的一种机制来对WEB 服务器验证一个用户:HTTP Digest Authentication;HTTPS Client Authentication;HTTP Basic Authentication;HTTP Based Authentication。

  5.HTTP Basic Authentication

  HTTP Basic Authentication是一个定义在HTTP/1.1规范中的验证机制。这种机制是以用户名和密码为基础的。一个WEB Server要求一个WEB Client去验证一个用户。作为Request的一部分,WEB Server 传递Realm的字符串,用户就是在它里面被验证的。

  小提示:Basic Authentication机制的Realm字符串不一定反映任何一种安全方针域。WEB Client得到这些用户名和密码,然后把它传递给WEB Server。WEB Server然后在一个特定的领域验证这些用户。由于密码是使用一种64位的编码来传递,而且目的Server没有验证,所以Basic Authentication不是一种安全的验证协议。

  6.HTTP Digest Authentication

  HTTP Digest Authentication根据用户名和密码验证一个用户,然而密码的传输是通过一种加密的形式进行的,这就比Basic Authentication所使用的64位编码形式传递要安全的多。由于Digest Authentication当前不被广泛使用,Servlet Containers不要求支持它但是鼓励去支持它。

  7.HTTPS Client Authentication

  使用HTTPS(HTTP over SSL)的终端用户验证是一种严格非验证机制。这种机制要求用户去处理公共密钥证明(Public Key Certification PKC)。当前,PKCs在e-commerce应用中是有用的。不适应J2EE的servlet containers不要求支持HTTPS协议。

  8.Server Tracking of Authentication InFormation

  就像映射在角色中的安全标识一样,运行环境是环境的说明而不是应用的说明。在WEB application的发布环境创建一个登录机制和策略;对发布在同一个Container的Application能够使用同样的验证信息来代表这些Application的Principal;仅仅当通过安全策略域时要求用户重新验证。因此,一个Servlet Container要求在Container层来跟踪这些验证信息,而不是在Application层。允许一个经验证的用户拒绝一个使用其它资源的Application,这些是通过受同样安全性标识限制的Container管理的。

  综上所述,JSP论坛的安全问题是一个没有止境的过程,随着JSP的蓬勃兴起,我们也将面对这些问题。对于JSP的安全爱好者来说,一个好的建议就是多看安全文章,这些安全文章一般都会有详细的信息如软件的版本号、漏洞原因等等,最重要的是还附带了解决办法或者是补丁的下载链接,如国内的安全站点
www.cnns.net或者国外的www.securityfocus.com站点;另外一个好的建议就是多装补丁程序,访问自己所使用的软件公司主页,从那上面获得最新的补丁程序。


相关链接
 ·JSP论坛安全之旅  [2005-12-14]
热门评论
验证码: 匿名
 信息检索
最新下载
最新素材
文章搜索
COPYRIGHT 2005-2006 BY EGZ'DESIGN ALL RIGHTS 程序支持 | 联系我们 | 信息反馈 | 在线留言 |


爱国设计
版权所有 陕ICP备05002767号 联系QQ

本站采用符合W3C标准的XHTML1.1代码格式编写  本站使用符合W3C标准的CSS+DIV格式布局排版    服务器端脚本采用PHP4编写  本站后台数据库采用MySQL