中安威士:详解SpringMVC框架中常见漏洞的防御
下面我们就用利用SpringMVC自带的数据库操作类jdbcTemplate举例。比如下面Dao中有如下的两个函数。
函数save使用的是绑定变量的形式很好的防止了sql注入,而queryForInt_函数接收id参数直接对sql语句进行了拼接,测试时出现sql注入。
publicstaticvoidsave(Stringusername,Stringpassword){
jdbcTemplate.update("insertintotest_table(user_name,password)values(?,?)",
newObject[]{username,password});
}
publicstaticintqueryForInt_(Stringid){
returnjdbcTemplate.queryForInt("selectcount(0)fromtest_tablewhereid="+id);
}
#为了方便仅仅贴出了DAO层代码
所以,在java代码的开发过程中,我们尽量避免使用拼接sql语句的形式去执行数据库语句。如果需要使用拼接sql语句的形式进行数据库查询,那么OWASP提供了一个防御sql注入的Esapi包,这个包中的encodeForSQL方法能对sql注入进行很好的防御。
接着我们就分析下这个encodeForSQL方法。
首先我们介绍这个方法的使用,使用时调用如下,不同的数据库使用不到的方法。
//防止Oracle注入
ESAPI.encoder().encodeForSQL(newOracleCodec(),queryparam)
//防止mysql注入
ESAPI.encoder().encodeForSQL(newMySQLCodec(Mode.STANDARD),queryparam)//Mode.STANDARK为标准的防注入方式,mysql一般用使用的是这个方式
//防止DB2注入
ESAPI.encoder().encodeForSQL(newDB2Codec(),queryparam)
//防止Oracle注入的方法例子,为了方便仅仅给出sql语句的拼接部分
CodecORACLE_CODEC=newOracleCodec();
Stringquery="SELECTuser_idFROMuser_dataWHEREuser_name=‘"+ESAPI.encoder().encodeForSQL(ORACLE_CODEC,req.getParameter("userID"))+"’anduser_password=‘"+ESAPI.encoder().encodeForSQL(ORACLE_CODEC,req.getParameter("pwd"))+"’";
下面我们就用mysql为例字分析encodeForSQL函数做了什么防御。具体函数过
程就不跟踪了,直接分析最后调用了哪个方法。根据代码可知最后调用的是encodeCharacter方法。
publicStringencodeCharacter(char[]immune,Characterc){
charch=c.charValue();
//checkforimmunecharacters
if(containsCharacter(ch,immune)){
return""+ch;
}
//checkforalphanumericcharacters
Stringhex=Codec.getHexForNonAlphanumeric(ch);
if(hex==null){
return""+ch;
}
switch(mode){
caseANSI:returnencodeCharacterANSI(c);
caseSTANDARD:returnencodeCharacterMySQL(c);
}
returnnull;
}
上述方法中containsCharacter函数是不进行验证的字符串白名单,Codec.getHexForNonAlphanumeric函数查找字符传中是否有16进制,没有返回空值。
而encodeCharacterANSI和encodeCharacterMySQL才是防御的重点,我们看一下这两个函数的不同,如果选择的我们选择是Mode.ANSi模式,则字符串则进入下面的函数,可以看到这个函数对单撇号和双撇号进行了转义。
privateStringencodeCharacterANSI(Characterc){
if(c=='\'')
return"\'\'";
if(c=='\"')
return"";
return""+c;
}
如果选择的是Mode.STANDARD模式,则字符串则进入下面的函数,可以看到这个函数对单撇号和双撇号、百分号、反斜线等更多的符号进行了转换,所以使用时推荐使用标准模式。
privateStringencodeCharacterMySQL(Characterc){
charch=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(Stringinput)
xss输出点在html属性中
ESAPI.encoder().encodeForHTMLAttribute(Stringinput)
xss输出点在JavaScript代码中
ESAPI.encoder().encodeForJavaScript(Stringinput)
xss输出点在CSS代码中
ESAPI.encoder().encodeForCSS(Stringinput)
xss输出点在VBScript代码中
ESAPI.encoder().encodeForVBScript(Stringinput)
xss输出点在XPath中
ESAPI.encoder().encodeForXPath(Stringinput)
xss输出点在XML中
ESAPI.encoder().encodeForXML(Stringinput)
xss输出点在XML属性中
ESAPI.encoder().encodeForXMLAttribute(Stringinput)
直接对url进行URL编码
ESAPI.encoder().encodeForURL(Stringinput)
如果java输出在html页面,使用如下示例的方法即可。
Stringusername=ESAPI.encoder().encodeForHTML(req.getParameter("name"))
接下来我们就研究这个方法的具体实现。
publicStringencodeCharacter(char[]immune,Characterc){
//checkforimmunecharacters
if(containsCharacter(c,immune)){
return""+c;
}
//checkforalphanumericcharacters
Stringhex=Codec.getHexForNonAlphanumeric(c);
if(hex==null){
return""+c;
}
//checkforillegalcharacters
//ascii码中的非数字,字符的编码,一般为非打印字符,即不能转换的为uncoide的ascii字符,直接替换成\ufffd,显示的为?
if((c<=0x1f&&c!='\t'&&c!='\n'&&c!='\r')||(c>=0x7f&&c<=0x9f))
{
hex=REPLACEMENT_HEX;//Let'sentityencodethisinsteadofreturningit
c=REPLACEMENT_CHAR;
}
//checkifthere'sadefinedentity
//#恶意字符以实体的形式输出
StringentityName=(String)characterToEntityMap.get(c);
if(entityName!=null){
return"&"+entityName+";";
}
//returnthehexentityassuggestedinthespec,#如果是16进制就转换为html16进制实体字符输出
return"&#x"+hex+";";
}
恶意字符在js中的编码大家可以看到使用的是js的十六进制编码或者jsunicode编码进行的编码。
其实上面的方法大都是对字符进行html实体编码,html十六进制编码,js十六进制编码,jsunicode的编码和url编码来防止恶意标签的执行。如果感兴趣可以看一下其他的编码方法,原理大致相同就不在一一介绍。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。