修复HTTP动词篡改导致的认证旁路问题的方法
本文于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方法,应该是没有问题才对。
<security-constraint>
<web-resource-collection>
<http-method>HEAD</http-method>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name></role-name>
</auth-constraint>
</security-constraint>
那么为什么AppScan扫描报告中出现了前述问题,Jackie一时有些想不明白。
项目的技术特点
当前所在项目使用了Spring+Struts2+iBatis,从技术组合上可以说非常传统,但在技术应用上存在很大不同,比如:
- 浏览器请求页面时,使用通用Action将请求重定向至对应的jsp页面;
- 页面上使用jQuery提供的ajax对象来加载呈现需要的数据;
- 为了实现项目国际化的需求,要求所有对jsp的请求都需要经过通用Action的转发;
- 页面代码使用Struts2的s标签来提取国际化信息;
这样的应用方式其实有不得以的苦衷,项目刚成立的时候,组内全是新人,对Struts2仅有名义上的理解,没有人在项目中实际运用中使用Struts2,另外项目进度压的很紧,没时间给大家去了解和学习;而使用ajax来实现页面与Web服务之间通信,实现和使用都简单,于是一直沿用至今。
Struts2的配置
Struts2在web.xml中的配置如下:
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
简化后的struts.xml配置文件,内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<constant name="struts.devMode" value="true" />
<constant name="struts.action.extension" value="jsp,action"/>
<constant name="struts.ui.theme" value="java"></constant>
<constant name="struts.objectFactory" value="spring" />
<constant name="struts.i18n.encoding" value="UTF-8" />
<package name="default" namespace="/" extends="struts-default">
<interceptors>
<interceptor-stack name="myStack">
<interceptor-ref name="basicStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="myStack" />
<global-results>
<result name="error">/error.jsp</result>
</global-results>
<global-exception-mappings>
<exception-mapping exception="java.lang.Exception" result="error"/>
</global-exception-mappings>
<action name="*" class="MainAction">
<result name="success">{1}.jsp</result>
</action>
</package>
</struts>
通用Action类,简化后的MainAction代码如下
import com.opensymphony.xwork2.ActionSupport;
public class MainAction extends ActionSupport {
private static final long serialVersionUID = 928135783255954591L;
@Override
public String execute() throws Exception {
return ActionSupport.SUCCESS;
}
}
从配置上讲,中规中矩,并没有什么特别的地方。
分析过程
访问jsp页面的流程
按照前述配置,当用户在浏览器地址栏里输入后缀为.jsp的url,敲击回车后,发生的事件如下:
由于在struts.xml文件中有如下配置,用户请求先被Struts2拦截;
<constant name="struts.action.extension" value="jsp,action"/>
由于在struts.xml中有如下配置,请求会被转发给
MainAction来处理;<action name="*" class="MainAction">
<result name="success">{1}.jsp</result>
</action>
由于
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 HEAD
type Status report
message JSPs only permit GET POST or HEAD
description 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 check
if (!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方法
其它
- burpsuite,强大的渗透测试神器
- switchysharp,Google Chrome浏览器上目前最好用的代理程序
- Struts2,Struts2官网
- Filtering Requests and Responses,关于Filter的介绍材料
- tomcat on GitHub,托管在GitHub上的官方镜像仓库
- tomcat 7.0.x SVN,7.0.x版本的官方SVN仓库
修复HTTP动词篡改导致的认证旁路问题的方法的更多相关文章
- 网络安全-使用HTTP动词篡改的认证旁路
这个东西去年的安全扫描都没有,今天就扫出来了,非常奇怪的一个东西.好吧,找资料找原因.结果可能应为搜索名词的原因,这个问题在群友的帮助下解决了. 在我理解中servlet只有post和get方法,然后 ...
- asp.net mvc 安全测试漏洞 " HTTP 动词篡改的认证旁路" 问题解决
IBM Security Appscan漏洞筛查-HTTP 动词篡改的认证旁路漏洞,具体解决方案: 在Web.Config中system.webServer节点增加配置security: <se ...
- MVC4升级MVC5导致原项目出错的解决方法
原文:MVC4升级MVC5导致原项目出错的解决方法 出现安全透明方法"WebMatrix.WebData.PreApplicationStartCode.Start()"尝试访问安 ...
- 将Python的Django框架与认证系统整合的方法
将Python的Django框架与认证系统整合的方法 这篇文章主要介绍了将Python的Django框架与认证系统整合的方法,包括指定认证后台和编写认证后台等内容,需要的朋友可以参考下 将Django ...
- 安装sqlserver导致80端口被占用解决方法
安装sqlserver导致80端口被占用解决方法 系统占用的端口一般都是微软官方的产品占用的.所以这个时候主要考虑到几个服务: SQL Server导致.其中很有可能是SQL Server Repor ...
- 导致页面顶部空白一行解决方法 【】
2016年11月7日10:57:10 模板文件生成html文件之后会在body开头处加入一个可见的控制符,导致页面头部会出现一个空白行.原因是页面的编码是UTF-8 + BOM. ...
- Ubuntu ./configure 半途终止 导致没有生成makefile文件 解决方法
在安装thrift的时候,解压包进入目录,执行命令: ./configure 之后,发现某些包没有安装,导致configure到一半的时候退出,接着make发现没有makefile文件.估计是我系统安 ...
- apache不断占内存过大,导致虚拟机内存不足,处理方法。
我用512M的vps,访问量不大,但内存占用很大,甚至宕机. 我用top,然后shitf+m发现,httpd占用内存极大.经过网上找资料设置后,用过一段时间终于没再出现内存问题了. 首先查找配置文件的 ...
- 移动端touch事件影响click事件以及在touchmove添加preventDefault导致页面无法滚动的解决方法
这两天自己在写一个手机网页,用到了触屏滑动的特效,就是往右滑动的时候左侧隐藏的菜单从左边划出来. 做完之后在手机原生浏览器中运行正常,但在QQ和微信中打开,发现touchmove只会触发一次,而且to ...
- 导致页面顶部空白一行解决方法
模板文件生成html文件之后会在body开头处加入一个可见的控制符,导致页面头部会出现一个空白行.原因是页面的编码是UTF-8 + BOM. 这种编码方式一般会在windows操 ...
随机推荐
- 【LeetCode回溯算法#04】组合总和I与组合总和II(单层处理位置去重)
组合总和 力扣题目链接(opens new window) 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target ...
- 【Azure 存储服务】记一次调用Storage Blob API使用 SharedKey Authorization出现的403错误
问题描述 使用Azure Storag Blob REST API上传文件,用SharedKey作为Authorization出现403错误. 错误消息 b'\xef\xbb\xbf<?xml ...
- Redisson 框架中的分布式锁
实现分布式锁通常有三种方式:数据库.Redis 和 Zookeeper.我们比较常用的是通过 Redis 和 Zookeeper 实现分布式锁.Redisson 框架中封装了通过 Redis 实现的分 ...
- Java 异常整合练习
1 package com.bytezero.throwable2; 2 3 /** 4 * 5 * @Description 异常练习 6 * @author Bytezero·zhenglei! ...
- C++//queue 队列 容器 先进先出 只有队头 队尾能被外界访问 因此不允许有遍历行为
1 //queue 队列 容器 先进先出 只有队头 队尾能被外界访问 因此不允许有遍历行为 2 3 4 #include<iostream> 5 #include<queue> ...
- Seata的技术调研
引子 本文不剖析业内分布式组件,只剖析seata这一组件的技术调研.看看是否存在接入价值. 一.概述 Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务.Seata ...
- 分支合并规则 dev master v1.0.XX 三条分支的合并原则
分支合并规则 dev master v1.0.XX 三条分支的合并原则
- git svn 提交代码日志填写规范 BUG NEW DEL CHG TRP gitz 日志z
git svn 提交代码日志填写规范 BUG NEW DEL CHG TRP gitz 日志z
- FreeRTOS教程4 消息队列
1.准备材料 正点原子stm32f407探索者开发板V2.4 STM32CubeMX软件(Version 6.10.0) Keil µVision5 IDE(MDK-Arm) 野火DAP仿真器 XCO ...
- day06-JavaScript03
JavaScript03 11.DOM 官方文档:https://www.w3school.com.cn/js/js_htmldom.asp 基本介绍: DOM全称是Document Object M ...