详解Javaweb中常见漏洞的防御
上一篇给大家介绍了SpringMVC中常见的客户端数据输入点,这一篇给大家讲解下java中常见漏洞的防御方法。
0x01.sql注入
下面我们就用利用SpringMVC自带的数据库操作类jdbcTemplate举例。比如下面Dao中有如下的两个函数。
函数save使用的是绑定变量的形式很好的防止了sql注入,而queryForInt_函数接收id参数直接对sql语句进行了拼接,测试时出现sql注入。
- public static void save(String username,String password) {
- jdbcTemplate.update("insert into test_table(user_name,password) values(?,?)",
- new Object[]{username,password});
- }
- public static int queryForInt_(String id){
- return jdbcTemplate.queryForInt("select count(0) from test_table where id = " + id);
- }
public static void save(String username,String password) {
jdbcTemplate.update("insert into test_table(user_name,password) values(?,?)",
new Object[]{username,password});
} public static int queryForInt_(String id){
return jdbcTemplate.queryForInt("select count(0) from test_table where id = " + id);
}
#为了方便仅仅贴出了DAO层代码
所以,在java代码的开发过程中,我们尽量避免使用拼接sql语句的形式去执行数据库语句。如果需要使用拼接sql语句的形式进行数据库查询,那么OWASP提供了一个防御sql注入的Esapi包,这个包中的encodeForSQL方法能对sql注入进行很好的防御。
接着我们就分析下这个encodeForSQL方法。
首先我们介绍这个方法的使用,使用时调用如下,不同的数据库使用不到的方法。
- //防止Oracle注入
- ESAPI.encoder().encodeForSQL(new OracleCodec(),queryparam)
- //防止mysql注入
- ESAPI.encoder().encodeForSQL(new MySQLCodec(Mode.STANDARD),queryparam) //Mode.STANDARK为标准的防注入方式,mysql一般用使用的是这个方式
- //防止DB2注入
- ESAPI.encoder().encodeForSQL(new DB2Codec(),queryparam)
- //防止Oracle注入的方法例子,为了方便仅仅给出sql语句的拼接部分
- Codec ORACLE_CODEC = new OracleCodec();
- String query ="SELECT user_id FROM user_data WHERE user_name = ‘"+ESAPI.encoder().encodeForSQL(ORACLE_CODEC,req.getParameter("userID"))+"’ and user_password = ‘"+ESAPI.encoder().encodeForSQL(ORACLE_CODEC,req.getParameter("pwd"))+"’";
//防止Oracle注入
ESAPI.encoder().encodeForSQL(new OracleCodec(),queryparam)
//防止mysql注入
ESAPI.encoder().encodeForSQL(new MySQLCodec(Mode.STANDARD),queryparam) //Mode.STANDARK为标准的防注入方式,mysql一般用使用的是这个方式
//防止DB2注入
ESAPI.encoder().encodeForSQL(new DB2Codec(),queryparam) //防止Oracle注入的方法例子,为了方便仅仅给出sql语句的拼接部分
Codec ORACLE_CODEC = new OracleCodec();
String query ="SELECT user_id FROM user_data WHERE user_name = ‘"+ESAPI.encoder().encodeForSQL(ORACLE_CODEC,req.getParameter("userID"))+"’ and user_password = ‘"+ESAPI.encoder().encodeForSQL(ORACLE_CODEC,req.getParameter("pwd"))+"’";
下面我们就用mysql为例字分析encodeForSQL函数做了什么防御。具体函数过程就不跟踪了,直接分析最后调用了哪个方法。根据代码可知最后调用的是encodeCharacter方法。
- public String encodeCharacter( char[] immune, Character c ) {
- char ch = c.charValue();
- // check for immune characters
- if ( containsCharacter( ch, immune ) ) {
- return ""+ch;
- }
- // check for alphanumeric characters
- String hex = Codec.getHexForNonAlphanumeric( ch );
- if ( hex == null ) {
- return ""+ch;
- }
- switch( mode ) {
- case ANSI: return encodeCharacterANSI( c );
- case STANDARD: return encodeCharacterMySQL( c );
- }
- return null;
- }
public String encodeCharacter( char[] immune, Character c ) {
char ch = c.charValue(); // check for immune characters
if ( containsCharacter( ch, immune ) ) {
return ""+ch;
} // check for alphanumeric characters
String hex = Codec.getHexForNonAlphanumeric( ch );
if ( hex == null ) {
return ""+ch;
} switch( mode ) {
case ANSI: return encodeCharacterANSI( c );
case STANDARD: return encodeCharacterMySQL( c );
}
return null;
}
上述方法中containsCharacter函数是不进行验证的字符串白名单,Codec.getHexForNonAlphanumeric函数查找字符传中是否有16进制,没有返回空值。
而encodeCharacterANSI和encodeCharacterMySQL才是防御的重点,我们看一下这两个函数的不同,如果选择的我们选择是Mode.ANSi模式,则字符串则进入下面的函数,可以看到这个函数对单撇号和双撇号进行了转义。
- private String encodeCharacterANSI( Character c ) {
- if ( c == '\'' )
- return "\'\'";
- if ( c == '\"' )
- return "";
- return ""+c;
- }
private String encodeCharacterANSI( Character c ) {
if ( c == '\'' )
return "\'\'";
if ( c == '\"' )
return "";
return ""+c;
}
如果选择的是Mode.STANDARD模式,则字符串则进入下面的函数,可以看到这个函数对单撇号和双撇号、百分号、反斜线等更多的符号进行了转换,所以使用时推荐使用标准模式。
- private String encodeCharacterMySQL( Character c ) {
- char ch = c.charValue();
- if ( ch == 0x00 ) return "\\0";
- if ( ch == 0x08 ) return "\\b";
- if ( ch == 0x09 ) return "\\t";
- if ( ch == 0x0a ) return "\\n";
- if ( ch == 0x0d ) return "\\r";
- if ( ch == 0x1a ) return "\\Z";
- if ( ch == 0x22 ) return "\\\"";
- if ( ch == 0x25 ) return "\\%";
- if ( ch == 0x27 ) return "\\'";
- if ( ch == 0x5c ) return "\\\\";
- if ( ch == 0x5f ) return "\\_";
- return "\\" + c;
- }
private String encodeCharacterMySQL( Character c ) {
char ch = c.charValue();
if ( ch == 0x00 ) return "\\0";
if ( ch == 0x08 ) return "\\b";
if ( ch == 0x09 ) return "\\t";
if ( ch == 0x0a ) return "\\n";
if ( ch == 0x0d ) return "\\r";
if ( ch == 0x1a ) return "\\Z";
if ( ch == 0x22 ) return "\\\"";
if ( ch == 0x25 ) return "\\%";
if ( ch == 0x27 ) return "\\'";
if ( ch == 0x5c ) return "\\\\";
if ( ch == 0x5f ) return "\\_";
return "\\" + c;
}
我们介绍了利用绑定变量和利用esapi两种方式对sql注入进行防御,我的建议是尽量使用绑定变量的是形式进行防注入,安全性能都比较好。
0x02:跨站脚本攻击
关于跨站脚本攻击的防御,我们分析esapi的防御方式。
esapi的防御方式是根据输出点的不同在不同的输出点进行相应的编码。我们看一下使用方法:
- xss输出点在html网页中
- ESAPI.encoder().encodeForHTML(String input)
- xss输出点在html属性中
- ESAPI.encoder().encodeForHTMLAttribute(String input)
- xss输出点在JavaScript代码中
- ESAPI.encoder().encodeForJavaScript(String input)
- xss输出点在CSS代码中
- ESAPI.encoder().encodeForCSS(String input)
- xss输出点在VBScript代码中
- ESAPI.encoder().encodeForVBScript(String input)
- xss输出点在XPath中
- ESAPI.encoder().encodeForXPath(String input)
- xss输出点在XML中
- ESAPI.encoder().encodeForXML(String input)
- xss输出点在XML属性中
- ESAPI.encoder().encodeForXMLAttribute(String input)
- 直接对url进行URL编码
- ESAPI.encoder().encodeForURL(String input)
xss输出点在html网页中
ESAPI.encoder().encodeForHTML(String input)
xss输出点在html属性中
ESAPI.encoder().encodeForHTMLAttribute(String input)
xss输出点在JavaScript代码中
ESAPI.encoder().encodeForJavaScript(String input)
xss输出点在CSS代码中
ESAPI.encoder().encodeForCSS(String input)
xss输出点在VBScript代码中
ESAPI.encoder().encodeForVBScript(String input)
xss输出点在XPath中
ESAPI.encoder().encodeForXPath(String input)
xss输出点在XML中
ESAPI.encoder().encodeForXML(String input)
xss输出点在XML属性中
ESAPI.encoder().encodeForXMLAttribute(String input)
直接对url进行URL编码
ESAPI.encoder().encodeForURL(String input)
如果java输出在html页面,使用如下示例的方法即可。
- String username = ESAPI.encoder().encodeForHTML(req.getParameter("name"))
String username = ESAPI.encoder().encodeForHTML(req.getParameter("name"))
接下来我们就研究这个方法的具体实现。
- public String encodeCharacter( char[] immune, Character c ) {
- // check for immune characters
- if ( containsCharacter(c, immune ) ) {
- return ""+c;
- }
- // check for alphanumeric characters
- String hex = Codec.getHexForNonAlphanumeric(c);
- if ( hex == null ) {
- return ""+c;
- }
- // check for illegal characters
- //ascii码中的非数字,字符的编码,一般为非打印字符,即不能转换的为uncoide的ascii字符,直接替换成\ufffd,显示的为?
- if ( ( c <= 0x1f && c != '\t' && c != '\n' && c != '\r' ) || ( c >= 0x7f && c <= 0x9f ) )
- {
- hex = REPLACEMENT_HEX; // Let's entity encode this instead of returning it
- c = REPLACEMENT_CHAR;
- }
- // check if there's a defined entity
- //#恶意字符以实体的形式输出
- String entityName = (String) characterToEntityMap.get(c);
- if (entityName != null) {
- return "&" + entityName + ";";
- }
- // return the hex entity as suggested in the spec,#如果是16进制就转换为html16进制实体字符输出
- return "&#x" + hex + ";";
- }
public String encodeCharacter( char[] immune, Character c ) { // check for immune characters
if ( containsCharacter(c, immune ) ) {
return ""+c;
} // check for alphanumeric characters
String hex = Codec.getHexForNonAlphanumeric(c);
if ( hex == null ) {
return ""+c;
} // check for illegal characters //ascii码中的非数字,字符的编码,一般为非打印字符,即不能转换的为uncoide的ascii字符,直接替换成\ufffd,显示的为?
if ( ( c <= 0x1f && c != '\t' && c != '\n' && c != '\r' ) || ( c >= 0x7f && c <= 0x9f ) )
{
hex = REPLACEMENT_HEX; // Let's entity encode this instead of returning it
c = REPLACEMENT_CHAR;
} // check if there's a defined entity
//#恶意字符以实体的形式输出
String entityName = (String) characterToEntityMap.get(c);
if (entityName != null) {
return "&" + entityName + ";";
} // return the hex entity as suggested in the spec,#如果是16进制就转换为html16进制实体字符输出
return "&#x" + hex + ";";
}
containsCharacter函数一般定义不需要编码的字符,如果我们不想编码那个字符就可以利用这个函数定义。
Codec.getHexForNonAlphanumeric函数判断是否是数字和字母,如果仅仅是字符和字母就直接返回,不在编码。
剩下的代码是对非数字和字母的字符进行实体编码或者html十六进制字符编码。
如果java输出在js代码页面,使用如下示例的方法即可。
- String username = ESAPI.encoder().encodeForJavaScript(req.getParameter("name"))
String username = ESAPI.encoder().encodeForJavaScript(req.getParameter("name"))
我们研究一下encodeForJavaScript方法:
- public String encodeCharacter( char[] immune, Character c ) {
- // check for immune characters
- if ( containsCharacter(c, immune ) ) {
- return ""+c;
- }
- // check for alphanumeric characters
- String hex = Codec.getHexForNonAlphanumeric(c);
- if ( hex == null ) {
- return ""+c;
- }
- // Do not use these shortcuts as they can be used to break out of a context
- // if ( ch == 0x00 ) return "\\0";
- // if ( ch == 0x08 ) return "\\b";
- // if ( ch == 0x09 ) return "\\t";
- // if ( ch == 0x0a ) return "\\n";
- // if ( ch == 0x0b ) return "\\v";
- // if ( ch == 0x0c ) return "\\f";
- // if ( ch == 0x0d ) return "\\r";
- // if ( ch == 0x22 ) return "\\\"";
- // if ( ch == 0x27 ) return "\\'";
- // if ( ch == 0x5c ) return "\\\\";
- // encode up to 256 with \\xHH,编码成js十六进制的形式
- String temp = Integer.toHexString(c);
- if ( c < 256 ) {
- String pad = "00".substring(temp.length() );
- return "\\x" + pad + temp.toUpperCase();
- }
- // otherwise encode with \\uHHHH,编码成jsunicode编码格式
- String pad = "0000".substring(temp.length() );
- return "\\u" + pad + temp.toUpperCase();
- }
public String encodeCharacter( char[] immune, Character c ) { // check for immune characters
if ( containsCharacter(c, immune ) ) {
return ""+c;
} // check for alphanumeric characters
String hex = Codec.getHexForNonAlphanumeric(c);
if ( hex == null ) {
return ""+c;
} // Do not use these shortcuts as they can be used to break out of a context
// if ( ch == 0x00 ) return "\\0";
// if ( ch == 0x08 ) return "\\b";
// if ( ch == 0x09 ) return "\\t";
// if ( ch == 0x0a ) return "\\n";
// if ( ch == 0x0b ) return "\\v";
// if ( ch == 0x0c ) return "\\f";
// if ( ch == 0x0d ) return "\\r";
// if ( ch == 0x22 ) return "\\\"";
// if ( ch == 0x27 ) return "\\'";
// if ( ch == 0x5c ) return "\\\\"; // encode up to 256 with \\xHH,编码成js十六进制的形式
String temp = Integer.toHexString(c);
if ( c < 256 ) {
String pad = "00".substring(temp.length() );
return "\\x" + pad + temp.toUpperCase();
} // otherwise encode with \\uHHHH,编码成jsunicode编码格式
String pad = "0000".substring(temp.length() );
return "\\u" + pad + temp.toUpperCase();
}
恶意字符在js中的编码大家可以看到使用的是js的十六进制编码或者jsunicode编码进行的编码。
其实上面的方法大都是对字符进行html实体编码,html十六进制编码,js十六进制编码,jsunicode的编码和url编码来防止恶意标签的执行。如果感兴趣可以看一下其他的编码方法,原理大致相同就不在一一介绍。
详解Javaweb中常见漏洞的防御的更多相关文章
- 详解javaweb中jstl如何循环List中的Map数据_java - JAVA
文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 详解javaweb中jstl如何循环List中的Map数据 第一种方式: 1:后台代码(测试) List<Map& ...
- 深入详解SQL中的Null
深入详解SQL中的Null NULL 在计算机和编程世界中表示的是未知,不确定.虽然中文翻译为 “空”, 但此空(null)非彼空(empty). Null表示的是一种未知状态,未来状态,比如小明兜里 ...
- 详解Objective-C中委托和协议
Objective-C委托和协议本没有任何关系,协议如前所述,就是起到C++中纯虚类的作用,对于“委托”则和协议没有关系,只是我们经常利用协议还实现委托的机制,其实不用协议也完全可以实现委托. AD: ...
- Java 枚举(enum) 详解7种常见的用法
Java 枚举(enum) 详解7种常见的用法 来源 https://blog.csdn.net/qq_27093465/article/details/52180865 JDK1.5引入了新的类型— ...
- Android 布局学习之——Layout(布局)详解二(常见布局和布局参数)
[Android布局学习系列] 1.Android 布局学习之——Layout(布局)详解一 2.Android 布局学习之——Layout(布局)详解二(常见布局和布局参数) 3.And ...
- 详解Python中re.sub--转载
[背景] Python中的正则表达式方面的功能,很强大. 其中就包括re.sub,实现正则的替换. 功能很强大,所以导致用法稍微有点复杂. 所以当遇到稍微复杂的用法时候,就容易犯错. 所以此处,总结一 ...
- (转)详解Linux中SSH远程访问控制
详解Linux中SSH远程访问控制 原文:http://blog.51cto.com/dengqi/1260038 SSH:是一种安全通道协议,主要用来实现字符界面的远程登录,远程复制等功能(使用TC ...
- 用IDEA详解Spring中的IoC和DI(挺透彻的,点进来看看吧)
用IDEA详解Spring中的IoC和DI 一.Spring IoC的基本概念 控制反转(IoC)是一个比较抽象的概念,它主要用来消减计算机程序的耦合问题,是Spring框架的核心.依赖注入(DI)是 ...
- 详解JavaScript中的原型
前言 原型.原型链应该是被大多数前端er说烂的词,但是应该还有很多人不能完整的解释这两个内容,当然也包括我自己. 最早一篇原型链文章写于2019年07月,那个时候也是费了老大劲才理解到了七八成,到现在 ...
随机推荐
- 洛谷 P3239 [HNOI2015]亚瑟王(期望dp)
题面 luogu 题解 一道复杂的期望\(dp\) 思路来源:__stdcall 容易想到,只要把每张牌打出的概率算出来就可以求出\(ans\) 设\(fp[i]\)表示把第\(i\)张牌打出来的概率 ...
- AtCoder Beginner Contest 113 A
A - Discount Fare Time limit : 2sec / Memory limit : 1024MB Score: 100 points Problem Statement Ther ...
- 【CTF】某xss练手小游戏
http://test.xss.tv 1.http://47.94.13.75/test/level1.php?name=test 直接插入即可,如: http://47.94.13.75/test/ ...
- pycharm中使用正则表达式批量添加print括号,完美从python2迁移到python3
网络下载的python代码,版本参差,从python2.x迁移python3.x的过程中,存在print语法问题,即python2.x中print无括号,python3.x中print有括号. 逐行添 ...
- git push的一些坑
在安装git的时候我们一般会自己设置一个用户名和邮箱,这个一般设置为全局的用户名,如下所示 git config --global user.name "xxx" git conf ...
- java se系列(一)开发前奏
1. 软硬件知识 电子计算机:俗称电脑,是一种能够按照程序运行,自动.高速处理海量数据的现代化智能电子设备.由硬件和软件所组成,没有安装任何软件的计算机称为裸机 cpu:是一台计算机的运算核心和控制核 ...
- angular的基本要点
<body ng-app="Myapp"> <div ng-controller="firstcon"> <h1>hello ...
- pat1003. Emergency (25)
1003. Emergency (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue As an emerg ...
- NSTimer循环引用的问题
前言: 记得之前看过一个面试题问:ARC环境下的dealloc方法有什么用?问题解答是:代理指针置空,停止定时器timer,注销通知,释放掉实例变量.看着没什么问题,而且网上一收也是大概这样的答案.今 ...
- 代码重构----使用java有限状态机来消除太多的if else判断
1. 状态机基本概念 http://zh.wikipedia.org/wiki/%E6%9C%89%E9%99%90%E7%8A%B6%E6%80%81%E6%9C%BA 状态存储关于过去的信息,就是 ...