xss防御的最好方式_详解SpringMVC框架中常见漏洞的防御

ed596d24aee7511e1fde1774878b3370.png

下面我们就用利用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);
}

#为了方便仅仅贴出了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"))+"’";

下面我们就用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;
}

上述方法中containsCharacter函数是不进行验证的字符串白名单,Codec.getHexForNonAlphanumeric函数查找字符传中是否有16进制,没有返回空值。

而encodeCharacterANSI和encodeCharacterMySQL才是防御的重点,我们看一下这两个函数的不同,如果选择的我们选择是Mode.ANSi模式,则字符串则进入下面的函数,可以看到这个函数对单撇号和双撇号进行了转义。

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;
}

我们介绍了利用绑定变量和利用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)

如果java输出在html页面,使用如下示例的方法即可。

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 + ";";
}

恶意字符在js中的编码大家可以看到使用的是js的十六进制编码或者jsunicode编码进行的编码。

其实上面的方法大都是对字符进行html实体编码,html十六进制编码,js十六进制编码,jsunicode的编码和url编码来防止恶意标签的执行。如果感兴趣可以看一下其他的编码方法,原理大致相同就不在一一介绍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值