密级:保密
B WEB 开发安全漏洞修复方案
(V1.0)
文档编号:
文档名称:
WEB 开发安全漏洞修复方案 编
写:
审
核:
批
准:
批准日期:
技术研究部
文档修订记录 编号 版本号 修订内容简述 修订日期 作者 审核 1 V1.0 初稿 2012-7
6
7
8
9
10
11
12
13
14
15
16
(V1.0)
........................................................................................................................................... 1 1.1 背景 ............................................................................................................................ 1 1.2 FSDP 安全漏洞清单 ...................................................................................................... 1 1.3 安全漏洞修复方案 ......................................................................................................... 1 1.3.1 会话标识未更新 ................................................................................................. 1 1.3.2 登录错误消息凭证枚举 ..................................................................................... 2 1.3.3 不充分帐户封锁 ................................................................................................. 2 1.3.4 跨站点脚本编制 ................................................................................................. 3 1.3.5 已解密的登录请求 ............................................................................................. 6 1.3.6 跨站点脚本编制 .............................................................................................. 10 1.3.7 通过框架钓鱼 .................................................................................................. 14 1.3.8 链接注入(便于跨站请求伪造)
.................................................................. 19 1.3.9 应用程序错误 .................................................................................................. 26 1.3.10 SQL 注入 ........................................................................................................ 30 1.3.11 发现数据库错误模式 ................................................................................... 40 1.3.12 启用了不安全的 HTTP 方法 ........................................................................ 49 1.3.13 发现电子邮件地址模式 ............................................................................... 51 1.3.14 HTML 注释敏感信息泄露 .............................................................................. 52 1.3.15 发现内部 IP 泄露模式 ................................................................................. 53 1.3.16 主机允许从任何域进行 flash 访问 ......................................................... 54 1.3.17 主机应用软件漏洞修复 ............................................................................... 54 1.3.18 目录列表 ....................................................................................................... 55 1.3.19 跨站点请求伪造 ........................................................................................... 56 1.1 需要注意的问题 ........................................................................................................... 57
1.1 背景 随着移动公司对信息安全的进一步加强,要求我们部署的系统必须满足安全扫描要求。
本文档描述了安徽移动对 FSDP 安全扫描的漏洞的解决方案,并作为 WEB 开发的安全编程规范。
1.2 FSDP 安全漏洞清单 见《WEB 开发安全漏洞清单.xlsx》
1.3 安全漏洞修复方案 1.3.1 会话标识未更新
(一) URL http://10.149.113.200/loginAction.do (二) 安全问题描述 根据WASC:“会话固定”是一种攻击技术,会强制用户的会话标识变成显式值。固定会话标识值的技术有许多种,会随着目标Web 站点的功能而不同。从利用“跨站点脚本编 制”到向Web 站点密集发出先前生成的HTTP 请求,都在这些技术范围内。用户的会话标识固定之后,攻击者会等待用户登录,然后利用预定义的会话标识值来假定用户的联机身份。
(三) 攻击方法 登录过程前后会话标识的比较,显示它们并未更新,这表示有可能伪装用户。初步得知会话标识值后,远程攻击者有可能得以充当已登录的合法用户。
任何时候,只要一名用户与应用程序的交互状态由匿名转变为确认,应用程序就应该发布一个新的会话令牌。这不仅适用于用户成功登录的情况,而且适用于匿名用户首次提交个人或其他敏感信息时。
(四) 安全规范要求 COOKIE 中的登陆前 JSESSIONID 与登陆后 JESSIONID 不能相同。(只有 J2EE 应用服务器为 JESSIONID,其他应用服务器可能不同)
(五) 解决方案 将如下代码加入到登陆页面(login.jsp)的最后行:
<%
request.getSession().invalidate();//清空 session
Cookie cookie = request.getCookies()[0];//获取 cookie
cookie.setMaxAge(0);//让 cookie 过期
%>
1.3.2 登录错误消息凭证枚举
(一) URL http://10.149.113.200/loginAction.do (二) 安全问题描述 当试图利用不正确的凭证来登录时,当用户输入无效的用户名和无效的密码时,应用程序会分别生成不同的错误消息。
通过利用该行为,攻击者可以通过反复试验(蛮力攻击技术)来发现应用程序的有效用户名,再继续尝试发现相关联的密码。这样会得到有效用户名和密码的枚举,攻击者可以用来访问帐户。
(三) 攻击方法 修改 http://10.149.113.200/loginAction.do 的 HTTP 报文头:将参数“optrid”的值设置为 “ test123WithSomeChars ”, 除 去 cookie “ JSESSIONID ”, 除 去 HTTP 头“Cookie=JSESSIONID”。
(四) 安全规范要求 对每个错误的登录尝试发出相同的错误消息,不管是哪个字段发生错误,特别是用户名或密码字段错误。
(五) 解决方案 LoginImpl.java 类中 getLoginInfo 方法,涉及到登录错误提示的都改成:
“您输入的用户名或密码不正确!”。
登录超过 3 次数的改成:
“您尝试登陆失败超过"+Constans.LOGIN_ERROR_TIMES+"次,请 30 分钟后再登陆!”。
1.3.3 不充分帐户封锁
(一) URL http://10.149.113.200/loginAction.do (一) 安全问题描述 发送了两次合法的登录尝试,并且在其间发送了几次错误的登录尝试。最后一个响应与第一个响应相同。这表明存在未充分实施帐户封锁的情况,从而使登录页面可能受到蛮力攻
击。(即使第一个响应不是成功的登录页面,也是如此。)
(二) 攻击方法 修改 http://10.149.113.200/loginAction.do 的 HTTP报文头:除去cookie“JSESSIONID”,除去HTTP 头“Cookie=JSESSIONID”。
(三) 安全规范要求 多次登录尝试失败后实施帐户封锁 (四) 解决方案 LoginImp.java 中的 getLoginInfo 方法,修订如下代码片段:
//判断登陆失败次数
if(!checkLoginError(logininfo)){
ret.setRetCode("0003");
ret.setRetDesc("您尝试登陆失败超过"+Constans.LOGIN_ERROR_TIMES+"次,请"+Constans.LOGIN_ERROR_LOCK_SECOND+"分钟后再登录!");
return ret;
}
//增加验证登陆错误次数代码
addLoginErrorRec(logininfo);
1.3.4 跨站点脚本编制
(一) URL http://10.149.113.200/callAction.do http://10.149.113.200/loginAction.do
(二) 安全问题描述 可能会窃取或操纵客户会话和cookie,它们可能用于模仿合法用户,从而使黑客能够以该用户身份查看或变更用户记录以及执行事务。
(三) 攻击方法 Web 站点中所包含的脚本直接将用户在HTML 页面中的输入(通常 是参数值)返回,而不预先加以清理。如果脚本在响应页面中返回由JavaScript 代码组成的输入,浏览器便可以执行此输入。因此,有可能形成指向站点的若干链接,且其中一个参数
包含恶意的JavaScript 代码。该代码将在站点上下文中(由用户浏览器)执行,这使得该代码有权访问用户在该站点中具有访问权的cookie,以及站点中其他可通过用户浏览器访问的窗口。
攻击依照下列方式继续进行:攻击者诱惑合法用户单击攻击者生成的链接。用户单击该链接时,便会生成对于Web 站点的请求,其中的参数值含有恶意的JavaScript 代码。如果Web 站点将这个参数值嵌入在响应的HTML 页面中(这正是站点问题的本质所在),恶意代码便会在用户浏览器中运行。
(四) 安全规范要求
FSDP 框架中在传递参数时有两个主要参数 classes 与 common,一个指定要调用的service,一个是调用 service 中的方法,如果 service 或方法不存在,就会跳转到错误 信 息 显 示 , 并 将 详 细 的 错 误 信 息 显 示 出 来 , 如 :callAction.do?method=call&nextPage=/oa/task/task_querylayout.jsp&classes=taskClientImpl&common=getInfoQueryDate 这个 URL,如果我们将 URL修改一下,变成如下:
callAction.do?method=call&nextPage=/oa/task/task_querylayout.jsp&classes=taskClientImpl<script>alert(123)</script>&common=getInfoQueryDate,通过这个 URL 执行时会提示 service 不存在,并跳转到错误页面,同时会弹出123 信息的提示窗口,同时如果我们修改 common 参数也能达到这种效果
(五) 解决方案
web.xml 增加如下配置:
<!-- 可能存在的跨域代码字符串,用逗号分开 --> <context-param> <param-name>CROSS_DOMAIN_STR</param-name> <param-value><,>,%3C,%3E</param-value> </context-param> Constans.java 类增加如下变量:
/** * 跨域特殊字符判断 */
public static List<String> CROSS_DOMAIN_STR = new ArrayList<String>(); FriendOneServlet.java 类增加 initSafetyConf 安全初始化配置方法 private void initSafetyConf(){ Constans.CROSS_DOMAIN_STR.clear(); String crossDomainStr = getServletContext().getInitParameter("CROSS_DOMAIN_STR"); String[] crossDomainAry = crossDomainStr.split(","); for(String s : crossDomainAry){ Constans.CROSS_DOMAIN_STR.add(s.trim()); } Constans.LOGIN_ERROR_TIMES = getServletContext().getInitParameter("LOGIN_ERROR_TIMES"); Constans.LOGIN_ERROR_LOCK_SECOND = getServletContext().getInitParameter("LOGIN_ERROR_LOCK_SECOND"); logger.info(Constans.CROSS_DOMAIN_STR); logger.info("LOGIN_ERROR_TIMES="+Constans.LOGIN_ERROR_TIMES); logger.info("LOGIN_ERROR_LOCK_SECOND="+Constans.LOGIN_ERROR_LOCK_SECOND); } 在 init 方法中直接调用 在 ActionFilter.java 类中增加 checkCrossDomain 跨域特殊字符串检查,并在doFilter 中进行判断验证,如果存在特殊字符,则直接跳转到登陆界面 修改 error.jsp,此处是用于处理返回的错误信息 目前是通过<html-el:errors />标签方式加载错误信息,此处可根据输入的条件来进行错误注入,执行 js 代码,修改该当如下:
一、将此错误标签通过 textarea 进行包装,错误信息是以文本方式显示,无法执行,如下:
<textarea style="height:100%;width:95%;color:red" readonly=true><html-el:errors /></textarea>
同 时 修 改 BaseWebAction.java 类 的 processError 方 法 , 将sb.append(ste[i]).append("<br>"); 这 段 代 码 改 为sb.append(ste[i]).append("\n");
二、无论返回什么错误信息,此处只显示“操作故障,请确认操作是否合法或联系管理员检查!”
启用了不安全的 HTTP 方法
<security-constraint>
<web-resource-collection>
<url-pattern>/*</url-pattern>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>HEAD</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
<http-method>SEARCH</http-method>
<http-method>COPY</http-method>
<http-method>MOVE</http-method>
<http-method>PROPFIND</http-method>
<http-method>PROPPATCH</http-method>
<http-method>MKCOL</http-method>
<http-method>LOCK</http-method>
<http-method>UNLOCK</http-method>
</web-resource-collection>
<auth-constraint>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
1.3.5 已解密的登录请求
(一) URL http://10.149.113.200/loginAction.do (二) 安全问题描述
用户登录密码为明文 (三) 攻击方法 可通过 http 报文截取登录用户密码 (四) 安全规范要求 发送敏感信息时,始终使用 SSL 和 HTTP POST 方法 (五) 解决方案
修改 server.xml
<Connector protocol="org.apache.coyote.http11.Http11NioProtocol"
port="8443" minSpareThreads="5" maxSpareThreads="75"
enableLookups="true" disableUploadTimeout="true"
acceptCount="100"
maxThreads="200"
scheme="https" secure="true" SSLEnabled="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="D:/apache-tomcat-6.0.18/server.keystore"
keystorePass="friendone"/> 标红的地方注意,要与附件中 server.keystore 存放的位置一致 标蓝的 8443 要注意,外网的一律全用 443 端口,BOSS 网系统还是全用 8443 端口。
修改 web.xml,在 welcome-file-list 后面增加如下配置 <login-config>
<!-- Authorization setting for SSL -->
<auth-method>CLIENT-CERT</auth-method>
<realm-name>Client Cert Users-only Area</realm-name>
<auth-method>BASIC</auth-method>
</login-config>
<security-constraint>
<!-- Authorization setting for SSL -->
<web-resource-collection>
<web-resource-name>SSL</web-resource-name> /*/oa/login.jsp 为应用登录 URL,此为公司 OA 则为此串*/
<url-pattern>/oa/login.jsp</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<!-- 禁止不安全的 http 方法 -->
<security-constraint>
<web-resource-collection>
<web-resource-name>fortune</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>HEAD</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint></auth-constraint>
</security-constraint> 增加初始化变量:
<context-param>
<param-name>HTTP_URI</param-name>
<param-value>8080</param-value>
</context-param>
此为从 https 转到 http 时的跳转值, 为应用部署服务器 IP 地址为端口
LoginAction.java 修改如下:
Login 方法修改如下:
String requesturl = request.getRequestURL().toString(); requesturl = requesturl.substring(requesturl.indexOf("//") + 2); requesturl = requesturl.substring(0,
requesturl.indexOf(":")); response.sendRedirect("http://"+requesturl+":"+Constans.HTTP_URI+request.getContextPath()+"/loginAction.do?method=flogin&sessionid="+session.getId());
return null; //
clearTempFile();
//return new ActionForward("/"+Constans.PROJECT_NAME+"/frames/indexFrame.jsp");
增加以下方法:
public ActionForward flogin(ActionMapping mapping,ActionForm form,HttpServletRequest request,HttpServletResponse response) throws Exception{
try{
String value = request.getParameter("sessionid");
HashMap sessions = (HashMap) request.getSession().getServletContext().getAttribute("sessions");
HttpSession session = (HttpSession)sessions.get(value);
if(session != null){
HttpSession nsession = request.getSession();
nsession.setAttribute(Constans.LOGIN_USER,session.getAttribute(Constans.LOGIN_USER));
sessions.put(value, nsession);
session = null;
}
return new ActionForward("/"+Constans.PROJECT_NAME+"/frames/indexFrame.jsp");
}catch(Exception e){
logger.error("LoginAction Exception Error:"+e.toString());
throw e;
}
}
修改 LoginAction.java 类的 systemLogout 方法:
String requesturl = request.getRequestURL().toString();
requesturl = requesturl.substring(requesturl.indexOf("//") + 2);
requesturl = requesturl.substring(0, requesturl.indexOf(":"));
response.sendRedirect("http://"+requesturl+":"+Constans.HTTP_URI+request.getContextPath()+LOGINPAGE);
return null; //
return new ActionForward(LOGINPAGE); 1.3.6 跨站点脚本编制 (一) URL http://10.149.113.200/callAction.do http://10.149.113.200/loginAction.do (二) 安全问题描述 “跨站点脚本编制”攻击是一种隐私违例,可让攻击者获取合法用户的凭证,并在与特定 Web 站点交互时假冒这位用户。
这个攻击立足于下列事实:Web 站点中所包含的脚本直接将用户在 HTML 页面中的输入(通常是参数值)返回,而不预先加以清理。如果脚本在响应页面中返回由JavaScript 代码组成的输入,浏览器便可以执行此输入。因此,有可能形成指向站点的若干链接,且其中一个参数包含恶意的 JavaScript 代码。该代码将在站点上下文中(由用户浏览器)执行,这使得该代码有权访问用户在该站点中具有访问权的cookie,以及站点中其他可通过用户浏览器访问的窗口。
攻击依照下列方式继续进行:攻击者诱惑合法用户单击攻击者生成的链接。用户单击该链接时,便会生成对于 Web 站点的请求,其中的参数值含有恶意的 JavaScript 代码。如果 Web 站点将这个参数值嵌入在响应的 HTML 页面中(这正是站点问题的本质所在),恶意代码便会在用户浏览器中运行。
(三) 攻击方法 A. 在响应页面中,返回发送给 CGI 脚本的参数值,嵌入在 HTML 中。
例如:[请求] GET /cgi-bin/script.pl?name=JSmith HTTP/1.0 [响应] HTTP/1.1 200 OK Server: SomeServer Date: Sun, 01 Jan 2002 00:31:19 GMT Content-Type: text/html Accept-Ranges: bytes Content-Length: 27
<HTML> Hello JSmith </HTML> B. 在 HTML 参数值上下文中,返回发送给 CGI 脚本的参数值。
例如:[请求] GET /cgi-bin/script.pl?name=JSmith HTTP/1.0 [响应] HTTP/1.1 200 OK Server: SomeServer Date: Sun, 01 Jan 2002 00:31:19 GMT Content-Type: text/html Accept-Ranges: bytes Content-Length: 254 <HTML> Please fill in your zip code: <FORM METHOD=GET ACTION="/cgi-bin/script.pl"> <INPUT TYPE=text NAME="name" value="JSmith"> <br> <INPUT TYPE=text NAME="zip" value="Enter zip code here"> <br> <INPUT TYPE=submit value="Submit"> </FORM> </HTML> (四) 安全规范要求 通过验证用户输入未包含危险字符,便可能防止恶意的用户导致应用程序执行计划外的任务, 例如:启动任意 SQL 查询、嵌入将在客户端执行的 Javascript 代码、运行各种操作系统命令, 等等。
建议过滤出所有以下字符:
[1] |(竖线符号)
[2] & (& 符号)
[3];(分号)
[4] $(美元符号)
[5] %(百分比符号)
[6] @(at 符号)
[7] "(单引号)
[8] "(引号)
[9] \"(反斜杠转义单引号)
[10] \"(反斜杠转义引号)
[11] <>(尖括号)
[12] ()(括号)
[13] +(加号)
[14] CR(回车符,ASCII 0x0d)
[15] LF(换行,ASCII 0x0a)
[16] ,(逗号)
[17] \(反斜杠)
(五) 解决方案 LoginImpl.java 类中的 getLoginInfo 方法,将之前的操作员工号与密码拼接方式改成 SQL 变量绑定方式 StringBuffer sqlbuf = new StringBuffer(); sqlbuf.append("SELECT .orgcode,A.userid,A.username,A.password,A.validate,A.usertype,C.orgname,C.norgcode,C.orgtype,"); sqlbuf.append(" now() AS logindate,sp_genseqfunc("S","D") AS sessionid,"); sqlbuf.append(" (SELECT GROUP_CONCAT(B.datatype) FROM tb_userdatarel B WHERE B.userid = ?) AS datatypes ,PASSWORD(?)=A.password AS pwdiscorrect"); sqlbuf.append(" FROM tb_user A,tb_organization C"); sqlbuf.append(" WHERE A.userid = ? AND A.effdate <= CURDATE() AND A.expdate >= CURDATE() AND C.orgcode = A.orgcode");
web.xml 增加如下配置:
<!-- 可能存在的跨域代码字符串,用逗号分开 --> <context-param> <param-name>CROSS_DOMAIN_STR</param-name> <param-value><,>,%3C,%3E</param-value> </context-param> <context-param>
<param-name>GET_CROSS_DOMAIN_STR</param-name> <param-value><,>,",",%,;,(,),&,+,HTTP,http,%0a</param-value> </context-param>
Constans.java 类增加如下变量:
/** * 跨域特殊字符判断 */ public static List<String> CROSS_DOMAIN_STR = new
ArrayList<String>(); public static List<String> GET_CROSS_DOMAIN_STR = new ArrayList<String>(); FriendOneServlet.java 类增加 initSafetyConf 安全初始化配置方法 private void initSafetyConf(){ Constans.CROSS_DOMAIN_STR.clear(); String crossDomainStr = getServletContext().getInitParameter("CROSS_DOMAIN_STR"); String[] crossDomainAry = crossDomainStr.split(","); for(String s : crossDomainAry){ Constans.CROSS_DOMAIN_STR.add(s.trim()); } crossDomainStr = getServletContext().getInitParameter("GET_CROSS_DOMAIN_STR"); crossDomainAry = crossDomainStr.split(","); for(String s : crossDomainAry){ Constans.GET_CROSS_DOMAIN_STR.add(s.trim()); } Constans.LOGIN_ERROR_TIMES = getServletContext().getInitParameter("LOGIN_ERROR_TIMES"); Constans.LOGIN_ERROR_LOCK_SECOND = getServletContext().getInitParameter("LOGIN_ERROR_LOCK_SECOND"); Constans.HTTP_URI = getServletContext().getInitParameter("HTTP_URI");
logger.info(Constans.CROSS_DOMAIN_STR); logger.info("LOGIN_ERROR_TIMES="+Constans.LOGIN_ERROR_TIMES); logger.info("LOGIN_ERROR_LOCK_SECOND="+Constans.LOGIN_ERROR_LOCK_SECOND); logger.info("HTTP_URI="+Constans.HTTP_URI); } 在 init 方法中直接调用
在 ActionFilter.java 类中增加 checkCrossDomain 跨域特殊字符串检查,并在 doFilter 中进行判断验证,如果存在特殊字符,则直接跳转到登陆界面 修改 error.jsp,此处是用于处理返回的错误信息 目前是通过<html-el:errors />标签方式加载错误信息,此处可根据输入的条件来进行错误注入,执行 js 代码,修改该当如下:
一、将此错误标签通过 textarea 进行包装,错误信息是以文本方式显示,无法执行,如下:
<textarea style="height:100%;width:95%;color:red" readonly=true><html-el:errors /></textarea>
同 时 修 改 BaseWebAction.java 类 的 processError 方 法 , 将sb.append(ste[i]).append("<br>"); 这 段 代 码 改 为sb.append(ste[i]).append("\n");
二、无论返回什么错误信息,此处只显示“操作故障,请确认操作是否合法或联系管理员检查!” 1.3.7 通过框架钓鱼 (一) URL http://10.149.113.200/callAction.do (二) 安全问题描述 网络钓鱼是一个通称,代表试图欺骗用户交出私人信息,以便电子欺骗身份。
攻击者有可能注入 frame 或 iframe 标记,其中含有类似受攻击之网站的恶意属性。不小心的用户有可能浏览它,但并不知道他正在离开原始网站,冲浪到恶意的网站。之后,攻击者便可以诱惑用户重新登录,然后获取他的登录凭证。
(三) 攻击方法 伪造的网站嵌入在原始网站中,这个情况对攻击者有帮助,因为他的网络钓鱼企图会披上更可信赖的外表。
利用的样本:
如 果 参 数 值 未 经 适 当 清 理 便 反 映 在 响 应 中 , 那 么 下 列 请 求 :http://[SERVER]/script.aspx?parameter=<frame name="evil" src="www.evil.com">会使响应含有通往这个邪恶站点的框架。
(四) 安全规范要求 若干问题的补救方法在于对用户输入进行清理。
通过验证用户输入未包含危险字符,便可能防止恶意的用户导致应用程序执行计划外的任务,
例如:启动任意 SQL 查询、嵌入将在客户端执行的 Javascript 代码、运行各种操作系统命令, 等等。
建议过滤出所有以下字符:
[1] |(竖线符号)
[2] & (& 符号)
[3];(分号)
[4] $(美元符号)
[5] %(百分比符号)
[6] @(at 符号)
[7] "(单引号)
[8] "(引号)
[9] \"(反斜杠转义单引号)
[10] \"(反斜杠转义引号)
2012-7-4 16:57:34 154/187 [11] <>(尖括号)
[12] ()(括号)
[13] +(加号)
[14] CR(回车符,ASCII 0x0d)
[15] LF(换行,ASCII 0x0a)
[16] ,(逗号)
[17] \(反斜杠)
以下部分描述各种问题、问题的修订建议以及可能触发这些问题的危险字符:
SQL 注入和 SQL 盲注:
A. 确保用户输入的值和类型(如 Integer、Date 等)有效,且符合应用程序预期。
B. 利用存储过程,将数据访问抽象化,让用户不直接访问表或视图。当使用存储过程时,请利 用 ADO 命令对象来实施它们,以强化变量类型。
C. 清理输入以排除上下文更改符号,例如:
[1] "(单引号)
[2] "(引号)
[3] \"(反斜线转义单引号)
[4] \"(反斜杠转义引号)
[5] )(结束括号)
[6] ;(分号)
跨站点脚本编制:
A. 清理用户输入,并过滤出 JavaScript 代码。我们建议您过滤下列字符:
[1] <>(尖括号)
[2] "(引号)
[3] "(单引号)
[4] %(百分比符号)
[5] ;(分号)
[6] ()(括号)
[7] &(& 符号)
[8] +(加号)
B. 如果要修订 <%00script> 变体,请参阅 MS 文章 821349 C. 对于 UTF-7 攻击:
[-] 可能的话,建议您施行特定字符集编码(使用 "Content-Type" 头或 <meta> 标记)。
HTTP 响应分割:清理用户输入(至少是稍后嵌入在 HTTP 响应中的输入)。
请确保输入未包含恶意的字符,例如:
[1] CR(回车符,ASCII 0x0d)
[2] LF(换行,ASCII 0x0a)远程命令执行:清理输入以排除对执行操作系统命令有意义的符 号,例如:
[1] |(竖线符号)
[2] & (& 符号)
[3];(分号)
执行 shell 命令:
A. 绝不将未检查的用户输入传递给 eval()、open()、sysopen()、system() 之类的 Perl 命令。
B. 确保输入未包含恶意的字符,例如:
[1] $(美元符号)
[2] %(百分比符号)
[3] @(at 符号)
XPath 注入:清理输入以排除上下文更改符号,例如:
[1] "(单引号)
[2] "(引号)
等 LDAP 注入:
A. 使用正面验证。字母数字过滤(A..Z,a..z,0..9)适合大部分 LDAP 查询。
B. 应该过滤出或进行转义的特殊 LDAP 字符:
2012-7-4 16:57:34 155/187 [1] 在字符串开头的空格或“#”字符 [2] 在字符串结尾的空格字符 [3] ,(逗号)
[4] +(加号)
[5] "(引号)
[6] \(反斜杠)
[7] <>(尖括号)
[8] ;(分号)
[9] ()(括号)
MX 注入:
应该过滤出特殊 MX 字符:
[1] CR(回车符,ASCII 0x0d)
[2] LF(换行,ASCII 0x0a)记录伪造:
应该过滤出特殊记录字符:
[1] CR(回车符,ASCII 0x0d)
[2] LF(换行,ASCII 0x0a)
[3] BS(退格,ASCII 0x08)
ORM 注入:
A. 确保用户输入的值和类型(如 Integer、Date 等)有效,且符合应用程序预期。
B. 利用存储过程,将数据访问抽象化,让用户不直接访问表或视图。
C. 使用参数化查询 API D. 清理输入以排除上下文更改符号,例如:
(*):
[1] "(单引号)
[2] "(引号)
[3] \"(反斜线转义单引号)
[4] \"(反斜杠转义引号)
[5] )(结束括号)
[6] ;(分号)
(*) 这适用于 SQL。高级查询语言可能需要不同的清理机制。
(五) 解决方案 LoginImpl.java 类中的 getLoginInfo 方法,将之前的操作员工号与密码拼接方式改成 SQL 变量绑定方式 StringBuffer sqlbuf = new StringBuffer(); sqlbuf.append("SELECT .orgcode,A.userid,A.username,A.password,A.validate,A.usertype,C.orgname,C.norgcode,C.orgtype,"); sqlbuf.append(" now() AS logindate,sp_genseqfunc("S","D") AS sessionid,"); sqlbuf.append(" (SELECT GROUP_CONCAT(B.datatype) FROM tb_userdatarel B WHERE B.userid = ?) AS datatypes ,PASSWORD(?)=A.password AS pwdiscorrect"); sqlbuf.append(" FROM tb_user A,tb_organization C"); sqlbuf.append(" WHERE A.userid = ? AND A.effdate <= CURDATE() AND A.expdate >= CURDATE() AND C.orgcode = A.orgcode");
web.xml 增加如下配置:
<!-- 可能存在的跨域代码字符串,用逗号分开 --> <context-param> <param-name>CROSS_DOMAIN_STR</param-name> <param-value><,>,%3C,%3E</param-value> </context-param> Constans.java 类增加如下变量:
/** * 跨域特殊字符判断 */ public static List<String> CROSS_DOMAIN_STR = new ArrayList<String>(); FriendOneServlet.java 类增加 initSafetyConf 安全初始化配置方法 private void initSafetyConf(){ Constans.CROSS_DOMAIN_STR.clear(); String crossDomainStr = getServletContext().getInitParameter("CROSS_DOMAIN_STR"); String[] crossDomainAry = crossDomainStr.split(",");
for(String s : crossDomainAry){ Constans.CROSS_DOMAIN_STR.add(s.trim()); } Constans.LOGIN_ERROR_TIMES = getServletContext().getInitParameter("LOGIN_ERROR_TIMES"); Constans.LOGIN_ERROR_LOCK_SECOND = getServletContext().getInitParameter("LOGIN_ERROR_LOCK_SECOND"); logger.info(Constans.CROSS_DOMAIN_STR); logger.info("LOGIN_ERROR_TIMES="+Constans.LOGIN_ERROR_TIMES); logger.info("LOGIN_ERROR_LOCK_SECOND="+Constans.LOGIN_ERROR_LOCK_SECOND); } 在 init 方法中直接调用 在 ActionFilter.java 类中增加 checkCrossDomain 跨域特殊字符串检查,并在 doFilter 中进行判断验证,如果存在特殊字符,则直接跳转到登陆界面 修改 error.jsp,此处是用于处理返回的错误信息 目前是通过<html-el:errors />标签方式加载错误信息,此处可根据输入的条件来进行错误注入,执行 js 代码,修改该当如下:
一、将此错误标签通过 textarea 进行包装,错误信息是以文本方式显示,无法执行,如下:
<textarea style="height:100%;width:95%;color:red" readonly=true><html-el:errors /></textarea>
同 时 修 改 BaseWebAction.java 类 的 processError 方 法 , 将sb.append(ste[i]).append("<br>"); 这 段 代 码 改 为sb.append(ste[i]).append("\n");
二、无论返回什么错误信息,此处只显示“操作故障,请确认操作是否合法或联系管理员检查!”
1.3.8 链接注入(便于跨站请求伪造)
(一) URL http://10.149.113.200/callAction.do
(二) 安全问题描述 可能会劝说初级用户提供诸如用户名、密码、信用卡号、社会保险号等敏感信息 可能会窃取或操纵客户会话和 cookie,它们可能用于模仿合法用户,从而使黑客能够以该用户身份查看或变更用户记录以及执行事务可能会在 Web 服务器上上载、修改或删除 Web 页面、脚本和文件 (三) 攻击方法 “链接注入”是修改站点内容的行为,其方式为将外部站点的 URL 嵌入其中,或将有易受攻击的 站点中的脚本的 URL 嵌入其中。将 URL 嵌入易受攻击的站点中,攻击者便能够以它为平台来 启动对其他站点的攻击,以及攻击这个易受攻击的站点本身。
在这些可能的攻击中,有些需要用户在攻击期间登录站点。攻击者从这一易受攻击的站点本身 启动这些攻击,成功的机会比较大,因为用户登录的可能性更大。
“链接注入”漏洞是用户输入清理不充分的结果,清理结果会在稍后的站点响应中返回给用户。
攻击者能够将危险字符注入响应中,便能够嵌入 URL 及其他可能的内容修改。
以下是“链接注入”的示例(我们假设“www.vulnerable.com”站点有一个用来问候用户的参数,称 为“name”)。
下列请求:HTTP://www.vulnerable.com/greet.asp?name=John Smith 会生成下列响应:
<HTML> <BODY> Hello, John Smith. </BODY> </HTML> 然而,恶意的用户可以发送下列请求:
HTTP://www.vulnerable.com/greet.asp?name=<IMG SRC="http://www.ANY-SITE.com/ANYSCRIPT. asp"> 这会返回下列响应:
<HTML> <BODY>
Hello, <IMG SRC="http://www.ANY-SITE.com/ANY-SCRIPT.asp">. </BODY> </HTML> 2012-7-4 16:57:34 149/187 如该示例所示,这有可能导致用户浏览器向几乎是攻击者所期待的任何站点发出自动请求。因 此,他可能利用这个“链接注入”漏洞来启动若干类型的攻击:
跨站点伪造请求:攻击者可以让用户的浏览器向用户目前登录的站点发送请求,以及执行用户 并不想执行的操作。
这些操作可能包括从站点中注销,或修改用户的概要文件、电子邮件地址,甚至是修改密码, 结果造成彻底的帐户接管。
跨站点脚本编制:任何“跨站点脚本编制”攻击都开始自诱惑用户单击精心制作的URL,以便利 用受害者站点中的漏洞。
发送含有恶意链接的电子邮件,或创建一个 Web 站点来包含指向易受攻击的站点的链接,通 常可以做到这一点。
当采用“链接注入”漏洞时,有可能在 A 站点中嵌入一个恶意的 URL,当单击这个链接时,便启 动对 B 站点的“跨站点脚本编制”攻击。
网络钓鱼:攻击者有可能注入指向类似受攻击站点的恶意站点的链接。
不小心的用户可能单击这个链接,但并不知道自己即将离开原始站点而浏览到恶意站点。之 后,攻击者便可以诱惑用户重新登录,然后获取他的登录凭证。
(四) 安全规范要求 通过验证用户输入未包含危险字符,便可能防止恶意的用户导致应用程序执行计划外的任务, 例如:启动任意 SQL 查询、嵌入将在客户端执行的 Javascript 代码、运行各种操作系统命令, 等等。
建议过滤出所有以下字符:
[1] |(竖线符号)
[2] & (& 符号)
[3];(分号)
[4] $(美元符号)
[5] %(百分比符号)
[6] @(at 符号)
[7] "(单引号)
[8] "(引号)
[9] \"(反斜杠转义单引号)
[10] \"(反斜杠转义引号)
[11] <>(尖括号)
[12] ()(括号)
[13] +(加号)
[14] CR(回车符,ASCII 0x0d)
[15] LF(换行,ASCII 0x0a)
[16] ,(逗号)
[17] \(反斜杠)
以下部分描述各种问题、问题的修订建议以及可能触发这些问题的危险字符:
SQL 注入和 SQL 盲注:
A. 确保用户输入的值和类型(如 Integer、Date 等)有效,且符合应用程序预期。
B. 利用存储过程,将数据访问抽象化,让用户不直接访问表或视图。当使用存储过程时,请利 用 ADO 命令对象来实施它们,以强化变量类型。
C. 清理输入以排除上下文更改符号,例如:
[1] "(单引号)
[2] "(引号)
[3] \"(反斜线转义单引号)
[4] \"(反斜杠转义引号)
[5] )(结束括号)
[6] ;(分号)
2012-7-4 16:57:34 150/187 跨站点脚本编制:
A. 清理用户输入,并过滤出 JavaScript 代码。我们建议您过滤下列字符:
[1] <>(尖括号)
[2] "(引号)
[3] "(单引号)
[4] %(百分比符号)
[5] ;(分号)
[6] ()(括号)
[7] &(& 符号)
[8] +(加号)
B. 如果要修订 <%00script> 变体,请参阅 MS 文章 821349 C. 对于 UTF-7 攻击:
[-] 可能的话,建议您施行特定字符集编码(使用 "Content-Type" 头或 <meta> 标记)。
HTTP 响应分割:清理用户输入(至少是稍后嵌入在 HTTP 响应中的输入)。
请确保输入未包含恶意的字符,例如:
[1] CR(回车符,ASCII 0x0d)
[2] LF(换行,ASCII 0x0a)远程命令执行:清理输入以排除对执行操作系统命令有意义的符 号,例如:
[1] |(竖线符号)
[2] & (& 符号)
[3];(分号)
执行 shell 命令:
A. 绝不将未检查的用户输入传递给 eval()、open()、sysopen()、system() 之类的 Perl 命令。
B. 确保输入未包含恶意的字符,例如:
[1] $(美元符号)
[2] %(百分比符号)
[3] @(at 符号)
XPath 注入:清理输入以排除上下文更改符号,例如:
[1] "(单引号)
[2] "(引号)
等 LDAP 注入:
A. 使用正面验证。字母数字过滤(A..Z,a..z,0..9)适合大部分 LDAP 查询。
B. 应该过滤出或进行转义的特殊 LDAP 字符:
[1] 在字符串开头的空格或“#”字符
[2] 在字符串结尾的空格字符 [3] ,(逗号)
[4] +(加号)
[5] "(引号)
[6] \(反斜杠)
[7] <>(尖括号)
[8] ;(分号)
[9] ()(括号)
MX 注入:
应该过滤出特殊 MX 字符:
[1] CR(回车符,ASCII 0x0d)
[2] LF(换行,ASCII 0x0a)记录伪造:
应该过滤出特殊记录字符:
[1] CR(回车符,ASCII 0x0d)
[2] LF(换行,ASCII 0x0a)
[3] BS(退格,ASCII 0x08)
ORM 注入:
A. 确保用户输入的值和类型(如 Integer、Date 等)有效,且符合应用程序预期。
2012-7-4 16:57:34 151/187 B. 利用存储过程,将数据访问抽象化,让用户不直接访问表或视图。
C. 使用参数化查询 API D. 清理输入以排除上下文更改符号,例如:
(*):
[1] "(单引号)
[2] "(引号)
[3] \"(反斜线转义单引号)
[4] \"(反斜杠转义引号)
[5] )(结束括号)
[6] ;(分号)
(*) 这适用于 SQL。高级查询语言可能需要不同的清理机制。
(五) 解决方案 LoginImpl.java 类中的 getLoginInfo 方法,将之前的操作员工号与密码拼接方式改成 SQL 变量绑定方式 StringBuffer sqlbuf = new StringBuffer();
sqlbuf.append("SELECT .orgcode,A.userid,A.username,A.password,A.validate,A.usertype,C.orgname,C.norgcode,C.orgtype,"); sqlbuf.append(" now() AS logindate,sp_genseqfunc("S","D") AS sessionid,"); sqlbuf.append(" (SELECT GROUP_CONCAT(B.datatype) FROM tb_userdatarel B WHERE B.userid = ?) AS datatypes ,PASSWORD(?)=A.password AS pwdiscorrect"); sqlbuf.append(" FROM tb_user A,tb_organization C"); sqlbuf.append(" WHERE A.userid = ? AND A.effdate <= CURDATE() AND A.expdate >= CURDATE() AND C.orgcode = A.orgcode");
web.xml 增加如下配置:
<!-- 可能存在的跨域代码字符串,用逗号分开 --> <context-param> <param-name>CROSS_DOMAIN_STR</param-name> <param-value><,>,%3C,%3E</param-value> </context-param> Constans.java 类增加如下变量:
/** * 跨域特殊字符判断 */ public static List<String> CROSS_DOMAIN_STR = new ArrayList<String>(); FriendOneServlet.java 类增加 initSafetyConf 安全初始化配置方法 private void initSafetyConf(){ Constans.CROSS_DOMAIN_STR.clear(); String crossDomainStr = getServletContext().getInitParameter("CROSS_DOMAIN_STR"); String[] crossDomainAry = crossDomainStr.split(","); for(String s : crossDomainAry){ Constans.CROSS_DOMAIN_STR.add(s.trim()); } Constans.LOGIN_ERROR_TIMES = getServletContext().getInitParameter("LOGIN_ERROR_TIMES");
Constans.LOGIN_ERROR_LOCK_SECOND = getServletContext().getInitParameter("LOGIN_ERROR_LOCK_SECOND"); logger.info(Constans.CROSS_DOMAIN_STR); logger.info("LOGIN_ERROR_TIMES="+Constans.LOGIN_ERROR_TIMES); logger.info("LOGIN_ERROR_LOCK_SECOND="+Constans.LOGIN_ERROR_LOCK_SECOND); } 在 init 方法中直接调用 在 ActionFilter.java 类中增加 checkCrossDomain 跨域特殊字符串检查,并在 doFilter 中进行判断验证,如果存在特殊字符,则直接跳转到登陆界面 修改 error.jsp,此处是用于处理返回的错误信息 目前是通过<html-el:errors />标签方式加载错误信息,此处可根据输入的条件来进行错误注入,执行 js 代码,修改该当如下:
一、将此错误标签通过 textarea 进行包装,错误信息是以文本方式显示,无法执行,如下:
<textarea style="height:100%;width:95%;color:red" readonly=true><html-el:errors /></textarea>
同 时 修 改 BaseWebAction.java 类 的 processError 方 法 , 将sb.append(ste[i]).append("<br>"); 这 段 代 码 改 为sb.append(ste[i]).append("\n");
二、无论返回什么错误信息,此处只显示“操作故障,请确认操作是否合法或联系管理员检查!”
1.3.9 应用程序错误 (一) URL http://10.149.113.200/callAction.do
http://10.149.113.200/clientAction.do
http://10.149.113.200/loginAction.do (二) 安全问题描述 未对入局参数值执行适当的边界检查 未执行验证以确保用户输入与预期的数据类型匹配
(三) 攻击方法 如果攻击者通过伪造包含非应用程序预期的参数或参数值的请求,来探测应用程序(如以下示 例所示),那么应用程序可能会进入易受攻击的未定义状态。攻击者可以从应用程序对该请 求的响应中获取有用的信息,且可利用该信息,以找出应用程序的弱点。
例如,如果参数字段是单引号括起来的字符串(如在 ASP 脚本或 SQL 查询中),那么注入的 单引号将会提前终止字符串流,从而更改脚本的正常流程/语法。
错误消息中泄露重要信息的另一个原因,是脚本编制引擎、Web 服务器或数据库配置错误。
以下是一些不同的变体:
[1] 除去参数 [2] 除去参数值 [3] 将参数值设置为空值 [4] 将参数值设置为数字溢出(+/- 99999999...
相关热词搜索: 例文 安全漏洞 修复