本博客整理自图解HTTP和众多网络文章,对HTTP完全漏洞进行梳理,并介绍了java解决方案。
简单的HTTP协议本身并不存在安全性问题,因此协议本身几乎不会成为攻击的对象,但是HTTP应用的服务端和客户端以及web应用资源是主要的攻击目标。
虽然HTTP协议本身不在安全性问题,但是因为协议本身不包含会话管理、加密处理的要求,因此使用HTTP开发的应用和服务器容易成为攻击对象。如远程登录SSH协议,SSH具备协议级别的认证和会话管理等,比HTTP就安全性来说就做的更好。也因为如此HTTPS才在越来越多的场合替代HTTP协议,强制要求使用HTTPS。
一、攻击模式
针对web应用的攻击模式主要有: 主动攻击 和 被动攻击
- 以服务器为目标的主动攻击
指通过直接访问web应用,把攻击代码传入的攻击模式。典型的攻击有:sql注入攻击、os命令注入攻击
- 以服务器为目标的被动攻击
被动攻击(passive attack)是利用圈套策略执行攻击代码的攻击模式。典型攻击为跨站脚本攻击和跨站请求伪造
- 诱使用户触发已经设置好的陷阱,启动发送已嵌入攻击代码的http请求
- 用户的浏览器和邮件客户端会触发陷阱
- 中招的用户浏览器会把含有攻击代码的http请求发送给目标web应用
- 执行完攻击代码存在安全漏洞的web应用会成为攻击者的跳板,导致用户所持cookie个人信息被窃取,登陆装填中的用户权限被恶意滥用。
二、典型攻击
2.1、跨站脚本攻击( Cross-Site Scripting, XSS)
可以在正常的url中嵌入js脚本,设置陷阱,引诱用户点击此非法连接后,输入自己的个人信息,结果个人信息被js脚本传递给攻击者。用户很难意识到自己的登陆信息已经泄露,利用跨站脚本可以窃取用户的cookie。
举例:
http://www.test.com/msg.php?send=Hello,World!
接收者将会接收信息并显示Hello,Word
非正常发送消息:
http://www.test.com/msg.php?send=<script>alert('I am here!')</script>
接收者接收消息显示的时候将会弹出警告窗口
可以想像如果在script里面嵌入一个远程脚本,那么可能带来很不好的后果。
分析:
过于信任客户端提交的数据!因此解决思路就是不信任任何客户端提交的数据,只要是客户端提交的数据就应该先进行相应的过滤处理然后方可进行下一步的操作。
解决办法:
Java Web应用的解决办法:进行提交信息的过滤。
1)web.xml中增加过滤器配置
<filter>
<filter-name>xssFilter</filter-name>
<filter-class>com.test.xss.XssFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>xssFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
1.创建XssHttpServletRequestWrapper
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
HttpServletRequest orgRequest = null;
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
orgRequest = request;
}
/**
* 覆盖getParameter方法,将参数名和参数值都做xss过滤。<br/>
* 如果需要获得原始的值,则通过super.getParameterValues(name)来获取<br/>
* getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
*/
@Override
public String getParameter(String name) {
String value = super.getParameter(xssEncode(name));
if (value != null) {
value = xssEncode(value);
}
return value;
}
/**
* 覆盖getHeader方法,将参数名和参数值都做xss过滤。<br/>
* 如果需要获得原始的值,则通过super.getHeaders(name)来获取<br/>
* getHeaderNames 也可能需要覆盖
*/
@Override
public String getHeader(String name) {
String value = super.getHeader(xssEncode(name));
if (value != null) {
value = xssEncode(value);
}
return value;
}
/**
* 将容易引起xss漏洞的半角字符直接替换成全角字符
* @param s
* @return
*/
private static String xssEncode(String s) {
if (s == null || s.isEmpty()) {
return s;
}
StringBuilder sb = new StringBuilder(s.length() + 16);
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
switch (c) {
case '>':
sb.append(">");// 转义大于号
break;
case '<':
sb.append("<");// 转义小于号
break;
case '\'':
sb.append("'");// 转义单引号
break;
case '\"':
sb.append(""");// 转义双引号
break;
case '&':
sb.append("&");// 转义&
break;
case '@':
sb.append("@");// 转义@
break;
case '%':
sb.append("%");// 转义%
break;
case '(':
sb.append("(");// 转义(
break;
case ')':
sb.append(")");// 转义)
break;
case ',':
sb.append(",");// 转义,
break;
case '.':
sb.append("。");// 转义.
break;
case ';':
sb.append(";");// 转义;
break;
case '|':
sb.append("|");// 转义|
break;
default:
sb.append(c);
break;
}
}
return sb.toString();
}
/**
* 获取最原始的request
* @return
*/
public HttpServletRequest getOrgRequest() {
return orgRequest;
}
/**
* 获取最原始的request的静态方法
* @return
*/
public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
if (req instanceof XssHttpServletRequestWrapper) {
return ((XssHttpServletRequestWrapper) req).getOrgRequest();
}
return req;
}
}
2.创建Filter
public class XssFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(
(HttpServletRequest) request);
chain.doFilter(xssRequest, response);
}
@Override
public void destroy() {
}
}
2.2、Sql注入攻击
指对于web应用使用的数据库,通过运行非法的sql产生攻击,这种攻击就叫做sql注入(sql Injection)。非法注入攻击可能有如下影响:
1)非法查看和篡改书库内的数据
2)规避认证
3)执行和数据库服务器业务相关的程序
举例来说:
在登陆页面,填好正确的用户名(admin)和密码(admin123)后,点击提交,将会返回给我们“欢迎管理员”的界面。
因为根据我们提交的用户名和密码被合成到SQL查询语句当中之后是这样的:
select * from users where username='admin' and password=md5(admin123)
很明显,用户名和密码都和我们之前给出的一样,肯定能够成功登陆。但是,如果我们输入一个错误的用户名或密码呢?很明显,肯定登入不了吧。恩,正常情况下是如此,但是对于有SQL注入漏洞的网站来说,只要构造个特殊的“字符串”,照样能够成功登录。
比如:在用户名输入框中输入:’or 1=1#,密码随便输入,这时候的合成后的SQL查询语句为:
select * from users where username='' or 1=1#' and password=md5('')
语义分析:“#”在mysql中是注释符,这样井号后面的内容将被mysql视为注释内容,这样就不会去执行了,换句话说,以下的两句sql语句等价:
select * from users where username='' or 1=1#' and password=md5('')
等价于
select * from users where username='' or 1=1
因为1=1永远都是成立的,即where子句总是为真,将该sql进一步简化之后,等价如下select语句:
select * from users
没错,该sql语句的作用是检索users表中的所有字段
我们利用万能语句(’or 1=1#)能够登录!一个经构造后的sql语句竟有如此可怕的破坏力,相信你看到这后,开始对sql注入有了一个理性的认识了吧~
分析原因:
也是对用户提交数据没有进行校验
解决办法:
使用filter过滤器实现的代码如下:
1、web.xml增加过滤器
<filter>
<filter-name>antiSqlInjection</filter-name>
<filter-class>com.test.filter.AntiSqlInjectionfilter</filter-class>
</filter>
<filter-mapping>
<filter-name>antiSqlInjection</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2、过滤器实现
package com.test.filter;
import javax.servlet.Filter;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class AntiSqlInjectionfilter implements Filter {
public void destroy() {
// TODO Auto-generated method stub
}
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest args0, ServletResponse args1,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) args0;
HttpServletResponse res = (HttpServletResponse) args1;
//获得所有请求参数名
Enumeration params = req.getParameterNames();
String sql = "";
while (params.hasMoreElements()) {
//得到参数名
String name = params.nextElement().toString();
//得到参数对应值
String[] value = req.getParameterValues(name);
for (int i = 0; i < value.length; i++) {
sql = sql + value[i];
}
}
//有sql关键字,跳转到error.html
if (sqlValidate(sql)) {
throw new IOException("您发送请求中的参数中含有非法字符");
//String ip = req.getRemoteAddr();
} else {
chain.doFilter(args0, args1);
}
}
//效验
protected static boolean sqlValidate(String str) {
str = str.toLowerCase();//统一转为小写
String badStr = "'|and|exec|execute|insert|select|delete|update|count|drop|*|%|chr|mid|master|truncate|" +
"char|declare|sitename|net user|xp_cmdshell|;|or|-|+|,|like'|and|exec|execute|insert|create|drop|" +
"table|from|grant|use|group_concat|column_name|" +
"information_schema.columns|table_schema|union|where|select|delete|update|order|by|count|*|" +
"chr|mid|master|truncate|char|declare|or|;|-|--|+|,|like|//|/|%|#";//过滤掉的sql关键字,可以手动添加
String[] badStrs = badStr.split("\\|");
for (int i = 0; i < badStrs.length; i++) {
if (str.indexOf(badStrs[i]) >= 0) {
return true;
}
}
return false;
}
}
上述例子中关键字or和 # 都满足过滤条件,都会被过滤出来
2.3、OS命令注入攻击
OS命令注入和SQL注入差不多,只不过SQL注入是针对数据库的,而OS命令注入是针对操作系统的。OS命令注入即能够在服务器上执行任意命令。
如何防止OS命令注入:
不要调用外部程序。举个例子,在UNIX系统上,有一个叫CGI的程序,可以执行sendmail命令来发送邮件。也许你的web应用程序也有发送邮件的功能,通过直接调用CGI程序发送邮件非常的简单,但是不要这样做,因为在执行sendmail命令的同时,也会混杂进其他OS命令,正确的做法是使用发送邮件的library。
解决办法:
过滤掉如下: 、; ,[ ,] ,| ,< ,> ,\ 之类的符号
2.4、http首部注入攻击
在响应首部字段内插入换行,添加影响首部或者主体的一种攻击Web应用有时会把外部接收到的数值,赋给响应首部的location和set-cookie
首部注入攻击造成的影响:
1)设置任务cookie信息
2)重定向至任务url
3)显示任意的主题( http响应截断攻击)
2.4.1 举例
常见的sql注入一般都是通过表单或请求参数进行注入,但这里给出的例子是通过HTTP协议头部进行注入。
GET / HTTP/1.1
Host: www.example.com
Connection: keep-alive
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'(select*from(select(sleep(20)))a) #
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,fr;q=0.6
HTTP协议的User-Agent头部为“Mozilla/5.0’(select*from(select(sleep(20)))a) #”。其中“select * from (select(sleep(20)))”是不正常的,可能是攻击者正在注入攻击。
这里的攻击主要是让数据库什么也不干而睡眠20秒,从而浪费数据库处理线程。这是一个简单的注入,除此之外还能进行更多复杂的攻击。一般获取头部的信息用于数据分析,例如这里获取User-Agent就可以知道客户都是通过什么浏览器访问系统的。jsp中直接通过request.getHeader("User-Agent")就可以获取到该值,接着可能就直接保存到数据库了,Mozilla/5.0’(select*from(select(sleep(20)))a) #中的#号在数据库中作为注释符号,它产生的sql可能如下:
INSERT INTO visits (useragent, datetime) VALUES ('Mozilla/5.0', (select*from(select(sleep(20)))a)) #', '2016-06-13 13:00:06')
这时#号后面都被注释掉了,于是数据库睡眠了20秒。
2.4.2解决办法:
避免这种攻击的方法,就是过滤所有的response headers,除去header中出现的非法字符,尤其是CRLF。
2.5、Http响应截断攻击
利用两个换行并排插入字符串后发送,利用连续的换行,可以做出首部和主体分隔所需的空行了。这样就能伪造主体,达到攻击的目的。
如何避免 HTTP 响应截断
要避免HTTP响应截断,需要注意以下几点:
(1)对用户的输入进行合理验证,对特殊字符(如<、>、’、”等)等进行编码。
(2)创建一份安全字符白名单,只接受完全由这些受认可的字符组成的输入出现在 HTTP 响应头文件中。
(3)使用源代码静态分析工具,进行自动化的检测,可以有效的发现源代码中的 HTTP 响应截断问题。如
在上述修复代码中,第38行使用 Refenence 类对环境变量值进行 decode,剔除特殊字符。
2.6、获取Cookie攻击
通过Java Script非常容易访问到当前网站的cookie。你可以打开任何网站,然后在浏览器地址栏中输 入:javascript:alert(doucment.cookie),立刻就可以看到当前站点的cookie(如果有的话)。攻击者可以利用这个特 性来取得你的关键信息。例如,和XSS攻击相配合,攻击者在你的浏览器上执行特定的Java Script脚本,取得你的cookie。假设这个网站仅依赖cookie来验证用户身份,那么攻击者就可以假冒你的身份来做一些事情。
现在多数浏览器都支持在cookie上打上HttpOnly的标记,凡有这个标志的cookie就无法通过Java Script来取得,如果能在关键cookie上打上这个标记,就会大大增强cookie的安全性
2.7、目录遍历攻击
对本无意公开的文件目录,通过非法截断其目录路径后,达成访问目的的一种攻击。这种攻击也称为Path Traversal攻击
文件交互是一种简单的过程,但是由于文件名可以任意更改而服务器支持“~/”,“../”等特殊符号的目录回溯,从而使攻击者越权访问或者覆盖敏感数据,如网站的配置文件、系统的核心文件,这样的缺陷被命名为路径遍历漏洞