本文比较粗糙,作用是引领大家认识web安全漏洞并根据自己参与的安全加固工作提供一些解决思路。
Web应用的安全加固是从安全扫描报告(当然有经验的架构师和软件工程师可以在架构和编码时将漏洞消灭于萌芽),加固人员通常根据报告的内容针对性的完善程序漏洞。
目前一下是见过IBM Relational AppScan、Fortity、Acunetix Website Audit的安全扫描报告(由于涉及漏洞信息,具体的内容就不展示了)。
IBM Relational AppScan
Fortity
Acunetix Website Audit报告
如果能使用安全扫描工具进行扫描,那就更好了,毕竟报告是扫描工具生成的,扫描工具的结果更详细,更具参考性。如IBM Relational AppScan。
IBM Relational AppScan扫描结果
以IBM Relational AppScan的漏洞结果为例,我们常见的漏洞如下,大致分4个级别
Microsoft Windows MHTML 跨站点脚本编制【级别-高】
SQL 盲注【级别-高】
已解密的登录请求【级别-高】
跨站点请求伪造【级别-中】
链接注入【级别-中】
通过框架钓鱼【级别-中】
Autocomplete HTML Attribute Not Disabled for Password Field【级别-低】
Internal IP Disclosure Pattern Found in Parameter Value【级别-低】
检测到文件替代版本【级别-低】
临时文件下载【级别-低】
HTML敏感信息泄露【级别-参考】
发现电子邮件地址模式【级别-参考】
发现内部IP泄露模式【级别-参考】
潜在的文件上载【级别-参考】
注:为避免信息泄漏,请求中很多无关信息进行了修改,关键部分不会变更
攻击请求示例
/demo/page/city/cityAction.do?method=cityTree&uid=1378952840737&id=Content-Type:%20multipart/related;%20boundary=_AppScan%0d%0a--_AppScan%0d%0aContent-Location:foo%0d%0aContent-Transfer-Encoding:base64%0d%0a%0d%0aPGh0bWw%2bPHNjcmlwdD5hbGVydCgiWFNTIik8L3NjcmlwdD48L2h0bWw%2b%0d%0a&a_dhx_rSeed=1378952840737
攻击结果
<?xml version='1.0' encoding='utf-8'?> <tree id="Content-Type: multipart/related; boundary=_AppScan --_AppScan Content-Location:foo Content-Transfer-Encoding:base64 PGh0bWw+PHNjcmlwdD5hbGVydCgiWFNTIik8L3NjcmlwdD48L2h0bWw+ "></tree>
攻击说明
通俗的讲,这个漏洞就是攻击者将一段恶意脚本已参数的形式发往后台,若web应用没有进行防御将脚本返回前台,HTML将会将恶意代码嵌入页面,之后用户信息将暴漏给攻击者。
以上面的漏洞为例,这个请求是JavaScript控件生成的,请求将根据一个id查询其子节点数据并返回,由于树形控件需要将数据返回前台并拼接树,所以id属性会返回前台。
当攻击者使用HTTP请求分析工具(如HttpWatch、浏览器自带的开发人员工具)获取该请求地址后,可以修改id的属性。如果后台没有对id属性进行校验,那么攻击脚本将会显示在页面,用户的后续操作将暴漏给攻击者。
建议加固方案
启动校验框架,对请求参数进行校验,比对使用正则表达式对id的值进行校验——只能是字母和数组。
目前主流的框架如struts、spring都提供了丰富、灵活的校验框架,加上正则表达式的支持,安全加固工作可以快速的开展。
额外说明
a) 这个已经到http请求级别,javascript的校验框架早已跳过(不得不说javascript校验是纸老虎,限制一下合法用户的输入)
b) 有人会说我输入合法的id信息,但是不是这用户有权限管理的id,这个怎么处理?首先,这个不是安全漏洞,应当算作业务漏洞,说明程序在鉴权模块的设计不够完善;其次,安全漏洞不应当嵌入业务逻辑中进行,进行加固的最佳位置的进入“控制层”之前(所有的框架都是这样做的),进行业务校验的最佳位置是“业务层”之前(简单系统建议使用AOP的方式在业务代理层中实现)。
攻击请求示例
POST /demo/page/reportgroup/ReportGroupUpdate.do?method=add&name=1234%2F**%2Fand%2F**%2F7659%3D7659&parentId=&parentName=1234&addUserGroup=&delUserGroup=&delReportGroup=&reportList=&description=1234
攻击说明
非严谨的解释就是攻击者将恶意代码已请求参数的形式传入后台,若后台进行数据库查询,恶意代码嵌入到SQL语句,使攻击者可以获得额外信息。假设原SQL是select * from t_user t where t.id = ?,若攻击者注入* and 1=1,那么sql将是select * from t_user t where t.id = * and 1=1,这样攻击者就能看到所有用户的信息。
实际业务中的SQL远比以上复杂,但是攻击者的攻击方式也不是这么简单,因此要抓住要点进行防御。
建议加固方案
1. 使用PrepareStatement。预编译的SQL可以有效防御攻击者的脚本,如果你使用了ORM框架,不用担心,通常ORM框架都提供了类似Preparestatement的支持,比如ibatis/mybatis中,SQL中使用##进行参数站位将使用preparestatement。
2. 使用存储过程。存储过程转入参数时将会进行合法性校验,恶意SQL通常会视为异常参数而无法处理。
3. 启动校验框架,对请求参数进行校验,这个可以更加通用,更加高效的拦截恶意SQL。
攻击请求示例
POST /demo/page/user/editUser.jsp?userType=1&sccAdminName=1234&sccAdminPwd=1234&confirmSccAdminPwd=Acme-Hackme+Corp.&systemAdminName=1234&systemAdminPwd=1234&confirmSystemAdminPwd=Acme-Hackme+Corp.&description=1234
攻击说明
很好理解,你的请求中包含了明文密码。由于抓去http请求非常简单,将用户密码明文传输基本就是告诉攻击者我的密码是多少。处理密码,以下信息也需要进行加密处理:
- 用户名
- 密码
- 社会保险号码
- 信用卡号码
- 驾照号码
- 电子邮件地址
- 电话号码
- 邮政编码
建议加固方案
1. 前端加密,后台直接使用加密后的密码进行匹配。目前主流的方式是前段使用MD5加密,后台直接使用密文密码进行匹配。
2. 有些业务比较特别,可能需要明文密码进行处理,这时候设计前段加密,后端解密的处理,可是由于加密盐可能被JS暴漏,这个方案不是最好的选择。
攻击请求示例
POST /demo/checkkeystore.do HTTP/1.0
Cookie: JSESSIONID=00e66855f6cb368a2f543053af46; SSO=eG9YZStrSUV5U21GYUFuL2JqNjBLa3Zic2QyMXZHU0ZLNW81Z3VrdWZiNG5mbmJ3Mi91QmFjclF2SW5aOGFOWUZpL1V3UTRKQk1MQgpNNW1xY2dHaG5qd0tWQXdVQTNZeng0TW43Y0ZMVUFxZDlENGJPcEVWcTl5clIyV0NnTzRnZjFuSmRxdGhzZG89
Content-Length: 34
Accept: */*
Accept-Language: zh-cn
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E)
Host: 192.168.106.79:8080
Content-Type: application/x-www-form-urlencoded
Referer: http://bogus.referer.ibm.com
攻击说明
关于Referer:
HTTP Referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的,服务器籍此可以获得一些信息用于处理。
攻击者可以窃取或操纵客户会话和 cookie,它们可能用于模仿合法用户,从而使攻击者能够以该用户身份查看或变更用户记录以及执行事务。
建议加固方案
使用filter对header的referer信息进行校验,referer信息可以为空、当前web应用以及信任地址。
攻击请求示例
GET /demo/page/grade/gradeAction.do?method=userGroupTree&authz=true&nodeType=province&fullId=ha&provinceId=ha&uid=1378952847091&id="'><A%20HREF="/WF_XSRF.html">Injected%20Link</A>&a_dhx_rSeed=1378952847091
攻击说明
和“跨站点脚本编制”类似,只是请求注入了<a>标签,参数返回页面时将在页面生成可以非法的连接。由于这个相对“跨站点脚本编制”比较明显,因此级别是中。
建议加固方案
启动校验框架,对请求参数进行校验,比对使用正则表达式对参数的值进行校验——重点过滤<和>。
攻击请求示例
GET /demo/ city/cityAction.do?method=cityTree&uid=1378952840737&id=ha"/>%3cabc+xmlns%3axyz%3d'http%3a%2f%2fwww.w3.org%2f1999%2fxhtml'%3e%3cxyz%3aiframe+src%3d'http%3a%2f%2fdemo.testfire.net'%2f%3e%3c%2fabc%3e&a_dhx_rSeed=1378952840737
攻击说明
和“跨站点脚本编制”和“链接注入“类似,只是请求注入了<iframe>或<frame>标签,参数返回页面时将在页面生成一个非法的嵌套页面。
建议加固方案
启动校验框架,对请求参数进行校验,比对使用正则表达式对参数的值进行校验——重点过滤<和>。
攻击说明
HTML5中添加了新属性Autocomplete,该属性可以可以让用户在输入框中找到历史填写信息。若密码输入框(<input type="password" />)没有关系该属性,攻击者可能会绕开 Web 应用程序的认证机制。
建议加固方案
在开发和修改时,遇到密码输入框,关闭Autocomplete属性。
<input type="password" autocomplete="off" />
攻击请求示例
POST /demo/authn/authAction.do?method=add&forwardUrl=http%3A%2F%2F192.168.11.93%3A80%2Chttp%3A%2F%2Fwww.baidu.com%3A80&resCode=IAM111&resKey=5OlwlhzHNn8%3D&from=iam&targetAction=%2FloginAction%21authByIAM.action
攻击说明
参数值中发现了内部IP,攻击者可能会收集有关 Web 应用程序的敏感信息,如用户名、密码、机器名和/或敏感文件位置。
建议加固方案
这个安全级别是低,由于IP地址被攻击者获取到后可能展开后续针对服务器的攻击,而不是直接攻击当前应用程序,因此可参考修复。
攻击请求示例
GET /demo/_userAction.do
攻击结果
HTTP/1.1 200 OK
Set-Cookie: JSESSIONID=01853c86546b44cb00af12e425e8; path=/demo
Content-Length: 2120
Server: TongWeb Server
P3P: CP="CAO PSA OUR"
Content-Type: text/html;charset=UTF-8
Date: Thu, 12 Sep 2013 02:55:31 GMT
Connection: close
攻击说明
漏洞扫描工具尝试在正确请求前添加下划线“_”等符号,判断服务器上是否有旧版本或无用文件。
建议加固方案
即时清理服务器上的旧版本或无用文件。
攻击请求示例
GET /demo/Copy%20of%20userAction.do
攻击结果
HTTP/1.1 200 OK
Set-Cookie: JSESSIONID=01850ed7359fdce3604aedd9dac9; path=/demo
Content-Length: 2120
Server: TongWeb Server
P3P: CP="CAO PSA OUR"
Content-Type: text/html;charset=UTF-8
Date: Thu, 12 Sep 2013 02:55:30 GMT
Connection: close
攻击说明
漏洞扫描工具尝试访问Copy of userAction.do,从而判断服务器上是否遗留有备份文件。
建议加固方案
即时清理服务器上的备份或无用文件。
参考级别不再一一介绍,参考级别可以说不是漏洞,甚至有些时候就是正常的业务。
HTML中有有注释信息,可供攻击者参考
HTML中有邮件地址,攻击者可以获取目标用户的邮箱。
页面存在填写IP信息的输入框,攻击者可以抓去ip地址
存在上传功能,上传功能可能导致攻击者上传恶意脚本。
很多漏洞都可以通过校验框架实现加固,如Microsoft Windows MHTML 跨站点脚本编制【级别-高】、SQL 盲注【级别-高】、链接注入【级别-中】、通过框架钓鱼【级别-中】。
不开启检验框架是一个重大的错误。
虽然有些浏览器不支持header信息,但是这里依旧是攻击的重点区域,需要对header信息进行统一的管理。
工具毕竟不是人,程序在判断漏洞的时候有一些硬性条件,比如:返回200状态、页面显示了某些数据等。
这些结果也许是错误的:假设我的web应用拦截并处理的攻击,但是我需要在页面提示,由于服务器响应的这次攻击请求,漏扫软件依旧认为web被攻击了。
输入非权限管理范围的信息能够查询到数据,这类操作是程序的鉴权模块不完善导致的,不能算作安全漏洞。
同时,安全的加固通常在进入控制层之前完成,如filter或是校验框架。鉴权通常在业务层之前完成,如service的代理类中。两者混淆将导致程序复杂度的增加。
不积跬步无以至千里,加强安全编码是一项长期的工作,需要每一位开发者重视。