本文于2016年4月完成,发布在个人博客网站上。 考虑个人博客因某种原因无法修复,于是在博客园安家,之前发布的文章逐步搬迁过来。
诡异的问题
分析AppScan扫描报告的时候,发现报告里提示“HTTP动词篡改导致的认证旁路”,一个名字很长,很怪异的问题。咨询度娘没有获取到必要的信息,于是只好按照AppScan报告里给出的重现步骤,实地操作来看看。
AppScan给出的复现步骤很简单,如下:
使用burpsuite拦截浏览器发出的HTTP请求,如下为样例:
GET /index.jsp HTTP/1.1 Host: 127.0.0.1:8080 Cache-Control: max-age=0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36 Accept-Encoding: gzip, deflate, sdch Accept-Language: zh-CN,zh;q=0.8 Connection: close
修改HTTP方法,比如把
GET
修改为BOGUS
,然后点击Forward
,把请求转发给Web服务器。BOGUS /index.jsp HTTP/1.1 Host: 127.0.0.1:8080 Cache-Control: max-age=0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36 Accept-Encoding: gzip, deflate, sdch Accept-Language: zh-CN,zh;q=0.8 Connection: close
令人大跌眼镜的事情发生了,Web服务器居然返回了HTTP 200,同时正确的返回了页面;
顿时让人有点小郁闷。按照产品线要求,当前所在项目使用的是平台部门基于Apache Tomcat 7.0.x源码做过加固的版本,按理说不该存在类似的安全问题。
对照Tomcat加固要求,重新检查了$CATALINA_BASE/conf/web.xml,确认其中包含了如下配置,这说明已经屏蔽了不安全的HTTP方法,应该是没有问题才对。
HEAD PUT DELETE OPTIONS TRACE /*
那么为什么AppScan扫描报告中出现了前述问题,Jackie一时有些想不明白。
项目的技术特点
当前所在项目使用了Spring+Struts2+iBatis,从技术组合上可以说非常传统,但在技术应用上存在很大不同,比如:
- 浏览器请求页面时,使用通用Action将请求重定向至对应的jsp页面;
- 页面上使用jQuery提供的ajax对象来加载呈现需要的数据;
- 为了实现项目国际化的需求,要求所有对jsp的请求都需要经过通用Action的转发;
- 页面代码使用Struts2的s标签来提取国际化信息;
这样的应用方式其实有不得以的苦衷,项目刚成立的时候,组内全是新人,对Struts2仅有名义上的理解,没有人在项目中实际运用中使用Struts2,另外项目进度压的很紧,没时间给大家去了解和学习;而使用ajax来实现页面与Web服务之间通信,实现和使用都简单,于是一直沿用至今。
Struts2的配置
Struts2在web.xml中的配置如下:
struts2 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter struts2 /*
简化后的struts.xml配置文件,内容如下:
/error.jsp {1}.jsp
通用Action类,简化后的MainAction
代码如下
import com.opensymphony.xwork2.ActionSupport;public class MainAction extends ActionSupport {private static final long serialVersionUID = 928135783255954591L;@Overridepublic String execute() throws Exception {return ActionSupport.SUCCESS;}}
从配置上讲,中规中矩,并没有什么特别的地方。
分析过程访问jsp页面的流程
按照前述配置,当用户在浏览器地址栏里输入后缀为.jsp
的url,敲击回车后,发生的事件如下:
由于在struts.xml文件中有如下配置,用户请求先被Struts2拦截;
由于在struts.xml中有如下配置,请求会被转发给
MainAction
来处理;{1}.jsp
由于
MainAction
类的execute
方法只返回了"success"
,于是请求又被重定向回了对应的jsp页面;@Override public String execute() throws Exception { return ActionSupport.SUCCESS; }
这时,对jsp页面的访问就由Tomcat接管了;
与9.0.x版本的Tomcat做对比
按照上面的分析,在第4步,如果Tomcat遇到了异常的请求方法,应该拒绝响应才是,不应当返回HTTP 200和页面。那么会不会是Tomcat实现存在问题?
平台发布加固后的Tomcat版本时并没有附带源码,于是从官网获取Tomcat发布的9.0.x版本做对比验证。执行前述的复现步骤,Tomcat 9.0.x版本给浏览器返回了如下响应:
HTTP Status 405 - JSPs only permit GET POST or HEADtype Status reportmessage JSPs only permit GET POST or HEADdescription The specified HTTP method is not allowed for the requested resource.
检查9.0.x版本Tomcat生成的servlet代码,发现在_jspService
中增加了对HTTP方法有效性的校验,只允许jsp编译生成的servlet响应GET、POST或HEAD方法,如下。
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) throws java.io.IOException, javax.servlet.ServletException { final java.lang.String _jspx_method = request.getMethod(); if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) { response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD"); return; }
生成上述代码的实现位于org.apache.jasper.compiler.Generator
类中,如下所示
// Method checkif (!pageInfo.isErrorPage()) { out.printil("final java.lang.String _jspx_method = request.getMethod();"); out.printin("if (!\"GET\".equals(_jspx_method) && !\"POST\".equals(_jspx_method) && !\"HEAD\".equals(_jspx_method) && "); out.println("!javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {"); out.pushIndent(); out.printin("response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "); out.println("\"" + Localizer.getMessage("jsp.error.servlet.invalid.method") + "\");"); out.printil("return;"); out.popIndent(); out.printil("}"); out.println();}
分析结论
这说明高版本的Tomcat已经意识到前述问题,并做了修复。
但此时还有一事不明,当请求HTTP请求中的方法不正确时,7.0.x版本的Tomcat依然可以正常返回页面信息。由于手上没有平台加固过的Tomcat的源码,这事情只好不了了之。
解决之道
显而易见,最直接的解决方法是更换Tomcat的版本,直接使用官网上发布的9.0.x版本。但官网发布的Tomcat没有经过安全加固,在项目中使用时需要自行做加固,难度不低,工作量不小。
所以不能换版本,得另想办法。
当前的诉求很简单,只允许Web服务器响应GET和POST请求,如果用户发起了其它请求,则清理会话、并跳转至错误页面。聪明的朋友一定想到了,使用J2EE标准中提供的Filter即可满足。
参考资料关于HTTP方法
- HTTP方法详解
- HTTP六种请求方法
- HTTP请求方法对照表
- HTTP协议的几种请求方法
- HTTP请求的方法
其它
- burpsuite,强大的渗透测试神器
- switchysharp,Google Chrome浏览器上目前最好用的代理程序
- Struts2,Struts2官网
- Filtering Requests and Responses,关于Filter的介绍材料
- tomcat on GitHub,托管在GitHub上的官方镜像仓库
- tomcat 7.0.x SVN,7.0.x版本的官方SVN仓库
本文来自博客园,作者:jackieathome,转载请注明原文链接:https://www.cnblogs.com/jackieathome/p/17936492.html