Web开发的若干安全问题小结

 万恶的项目终于要结题了…这时无论如何确该总结一下。实际上这次项目最大的收获有两部分,本文仅介绍其中之一—Web开发中的一些安全问题及其解决策略。

 项目的安全问题起源于首次测试版本提交。测试方使用了IBM Rational Appscan扫描提交的两部分应用程序,每个应用程序返回了将近二十个安全问题,具体列表这里先省略。Appscan是IBM旗下知名的web安全自动化测试工具,同类产品中较知名的还有HP的Webinspect。测试方回复的意思是说所有安全问题在项目验收前必须全部解决,而事实上本项目从设计阶段起就没有考虑过应用程序级的安全问题,对方也从未提出过此类要求。不过我们还是尝试先在逻辑上处理部分问题,自测后发现所有问题依然存在,包括众所周知且已有很多解决方案的Injection。

 纠结几日后大家终于决定重构,考虑在重构过程中再应用安全策略,于是又花了几周时间问题才得以基本解决。然而在解决web安全问题的过程中我们查阅了较多资料,由于缺乏经验,网上的许多解决方案基本都是无效的。当然也不能排除测试手段随着新的漏洞出现而加强的可能。这里我们仅就使用Appscan 7.8.0.2 Rules-Update 947的测试报告针对一些常见问题给出相应的解决办法。

 应当注意,目前Web安全领域拥有自己的国际性非盈利组织OWASP(Open Web Application Security Project),大部分Web安全问题的讨论都可以在https://www.owasp.org/index.php/Main_Page上找到相应的文章。如果读者对Web安全感兴趣,那么可以直接转向官网,本文接下来只考虑如何让应用程序通过Appscan 7.8的安全测试:)(如果仅仅是服务器或tomcat配置的问题,那么本文将不涉及)

 Injection

 SQL盲注问题十分普遍,一般考虑前端+后台验证。我们最初在后台对每个表单进行了关键词检测,然而测试依然不通过,而且仔细查阅资料后发现我们的过滤规则要比一般规则复杂得多,唯一问题在于处理策略不同:我们是直接给出错误提示,而大部分策略是将关键词截断后交付处理。郁闷的是改进后依然不能通过测试。于是开始求助,发现许多专业的JEE开发人员基本都直接使用一种servlet过滤器,项目地址http://securityfilter.sourceforge.net/,更神奇的是,配置该过滤器后问题的确不存在了,但是从表单直接进行关键词提交后台依然能收到完整语句,比较奇怪。此外,另一个问题Cross Site Scripting跨站点脚本攻击由于此过滤器的出现也不复存在了。

 Inadequate Account lockout

 不充分的账户封锁,这个问题看起来很简单,就是为了防止黑客暴力破解用户密码。尽管目前大多数站点使用的验证码技术已经足以克服此类问题,但Appscan测试对于验证码确实是无能为力的,因此测试方要求取消应用程序的验证码功能。后来使用了错误次数限制方案,如果某用户登录失败超过若干次,则直接禁止用户再尝试登录,直到申请管理员解封为止。

 Session Identifier Not Updated

 会话标识未更新,Appscan给出的描述是建议用户每次登录时需使用新的会话标识。网上给出了一种很简单的解决方案:

[c] session.invalidate();

 Cookie cookie = request.getCookies()[0];

 cookie.setMaxAge(0);[/c]

 上述代码是解决问题的核心,但不会使用照样无法解决任何问题。我们最终的解决方法是限制每次停留在登录页上的时间,如果超时提交登录则返回页面更新session后要求重新登录。另外,对于任何登录失败、用户注销、意外退出等情况都要彻底清除一遍sessionid。最后为了彻底解决这一问题,每次用户进入login页面后先检查sessionid是否更新,否则跳至过渡页清空sessionid后再返回login,问题解决。

 Unencrypted Login Request

 已解密的登录请求,要求就是数据加密传输。开始想当然地编写了一种简单的前端加密算法,再从后台进行解密,然而Appscan依然报此错(现在感觉是测试软件本身的问题,最后再谈)。于是决定转https,JEE平台转https的通行步骤如下:使用java keytool生成RSA密钥,打开tomcat的8443端口,并配置相应密钥即可,具体步骤网上很多,GOOGLE即可。更麻烦的问题随之而来,测试方认为尽管提供了8443的https访问,但仍然还可以通过8080使用普通的http访问,并要求禁止普通模式登录。

 但是由于要把8080端口提供给项目的WebService组件,不太可能完全禁掉。这里在项目web.xml里添加了一些配置解决:

[c]<security-constraint>

<!– Authorization setting for SSL –>

<web-resource-collection >

<web-resource-name >SSL</web-resource-name>

<url-pattern>*.jsp</url-pattern>

<url-pattern>*.action</url-pattern>

</web-resource-collection>

<user-data-constraint>

<transport-guarantee>CONFIDENTIAL</transport-guarantee>

</user-data-constraint>

</security-constraint>

<login-config>

<!– Authorization setting for SSL –>

<auth-method>CLIENT-CERT</auth-method>

<realm-name>Client Cert Users-only Area</realm-name>

</login-config>[/c]

 应注意,由于项目的一些组件无法通过https,因此url-pattern字段只对.jsp和.action进行了限制,如果不做特定限制,则系统默认是全部使用https传输。而且上述设置一旦在某个工程中出现,那么当前tomcat将全局采用这一配置。(这是实验结果,目前不评价其合理性)

 Cross-site request forgery

 跨站点请求伪造,网上研究文章一大堆,同时也是OWASP 2010 TOP 10级别。问题是很久都无法根本解决,经过两周研究后终于得以通过Appscan的CSRF检测。其实基本思路很简单,即在用户的提交的表单中加入一个隐藏域,存储一个唯一验证标识码:如sessionid。用户提交表单后由后台首先验证sessionid与提交标识是否一致,否则报错。

 然而要过Appscan的检测还是不太容易。首先是针对所有action的sessionid验证标识码检查,例如login,如果在session中始终保留一个标识,那么可能会报Session Identifier Not Updated错,解决方法是在登录成功后修改一下验证标识码。其实许多无法彻底解决的原因在于报错处理,起初我们将此类错误全部统一转向了一个安全报错页面,后来始终都无法解决这一问题。后来发现如果将错误情况转至首页则Appscan就不会报错,这可能是一个软件特征匹配的bug。

 除此之外,包括许多tomcat的配置问题这里就不再一一说明了。本次项目所引出的web安全问题确实需要得到重视,然而我们也应看到,过渡迷信流行的自动化测试工具可能会导致在某些问题上缘木求鱼,尤其是对于Appscan这种判断防御行为的复杂软件,仅靠有限的规则设置就当做是web安全的唯一标准——这也并非是聪明人的选择。