文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

中安威士:详解SpringMVC框架中常见漏洞的防御

2024-04-02 19:55

关注

下面我们就用利用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编码来防止恶意标签的执行。如果感兴趣可以看一下其他的编码方法,原理大致相同就不在一一介绍。


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯