详解Javaweb中常见漏洞的防御

上一篇给大家介绍了SpringMVC中常见的客户端数据输入点,这一篇给大家讲解下java中常见漏洞的防御方法。

0x01.sql注入

下面我们就用利用SpringMVC自带的数据库操作类jdbcTemplate举例。比如下面Dao中有如下的两个函数。

函数save使用的是绑定变量的形式很好的防止了sql注入,而queryForInt_函数接收id参数直接对sql语句进行了拼接,测试时出现sql注入。

[java] view plain copy print?

  1. public static void save(String username,String password) {
  2. jdbcTemplate.update("insert into test_table(user_name,password) values(?,?)",
  3. new Object[]{username,password});
  4. }
  5. public static int queryForInt_(String id){
  6. return jdbcTemplate.queryForInt("select count(0) from test_table where id = " + id);
  7. }
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方法。

首先我们介绍这个方法的使用,使用时调用如下,不同的数据库使用不到的方法。

[java] view plain copy print?

  1. //防止Oracle注入
  2. ESAPI.encoder().encodeForSQL(new OracleCodec(),queryparam)
  3. //防止mysql注入
  4. ESAPI.encoder().encodeForSQL(new MySQLCodec(Mode.STANDARD),queryparam) //Mode.STANDARK为标准的防注入方式,mysql一般用使用的是这个方式
  5. //防止DB2注入
  6. ESAPI.encoder().encodeForSQL(new DB2Codec(),queryparam)
  7. //防止Oracle注入的方法例子,为了方便仅仅给出sql语句的拼接部分
  8. Codec ORACLE_CODEC = new OracleCodec();
  9. 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方法。

[java] view plain copy print?

  1. public String encodeCharacter( char[] immune, Character c ) {
  2. char ch = c.charValue();
  3. // check for immune characters
  4. if ( containsCharacter( ch, immune ) ) {
  5. return ""+ch;
  6. }
  7. // check for alphanumeric characters
  8. String hex = Codec.getHexForNonAlphanumeric( ch );
  9. if ( hex == null ) {
  10. return ""+ch;
  11. }
  12. switch( mode ) {
  13. case ANSI: return encodeCharacterANSI( c );
  14. case STANDARD: return encodeCharacterMySQL( c );
  15. }
  16. return null;
  17. }
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模式,则字符串则进入下面的函数,可以看到这个函数对单撇号和双撇号进行了转义。

[java] view plain copy print?

  1. private String encodeCharacterANSI( Character c ) {
  2. if ( c == ‘\‘‘ )
  3. return "\‘\‘";
  4. if ( c == ‘\"‘ )
  5. return "";
  6. return ""+c;
  7. }
    private String encodeCharacterANSI( Character c ) {
        if ( c == ‘\‘‘ )
            return "\‘\‘";
        if ( c == ‘\"‘ )
            return "";
        return ""+c;
    }

如果选择的是Mode.STANDARD模式,则字符串则进入下面的函数,可以看到这个函数对单撇号和双撇号、百分号、反斜线等更多的符号进行了转换,所以使用时推荐使用标准模式。

[java] view plain copy print?

  1. private String encodeCharacterMySQL( Character c ) {
  2. char ch = c.charValue();
  3. if ( ch == 0x00 ) return "\\0";
  4. if ( ch == 0x08 ) return "\\b";
  5. if ( ch == 0x09 ) return "\\t";
  6. if ( ch == 0x0a ) return "\\n";
  7. if ( ch == 0x0d ) return "\\r";
  8. if ( ch == 0x1a ) return "\\Z";
  9. if ( ch == 0x22 ) return "\\\"";
  10. if ( ch == 0x25 ) return "\\%";
  11. if ( ch == 0x27 ) return "\\‘";
  12. if ( ch == 0x5c ) return "\\\\";
  13. if ( ch == 0x5f ) return "\\_";
  14. return "\\" + c;
  15. }
    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的防御方式是根据输出点的不同在不同的输出点进行相应的编码。我们看一下使用方法:

[java] view plain copy print?

  1. xss输出点在html网页中
  2. ESAPI.encoder().encodeForHTML(String input)
  3. xss输出点在html属性中
  4. ESAPI.encoder().encodeForHTMLAttribute(String input)
  5. xss输出点在JavaScript代码中
  6. ESAPI.encoder().encodeForJavaScript(String input)
  7. xss输出点在CSS代码中
  8. ESAPI.encoder().encodeForCSS(String input)
  9. xss输出点在VBScript代码中
  10. ESAPI.encoder().encodeForVBScript(String input)
  11. xss输出点在XPath中
  12. ESAPI.encoder().encodeForXPath(String input)
  13. xss输出点在XML中
  14. ESAPI.encoder().encodeForXML(String input)
  15. xss输出点在XML属性中
  16. ESAPI.encoder().encodeForXMLAttribute(String input)
  17. 直接对url进行URL编码
  18. 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页面,使用如下示例的方法即可。

[java] view plain copy print?

  1. String username = ESAPI.encoder().encodeForHTML(req.getParameter("name"))
String username = ESAPI.encoder().encodeForHTML(req.getParameter("name"))

接下来我们就研究这个方法的具体实现。

[java] view plain copy print?

  1. public String encodeCharacter( char[] immune, Character c ) {
  2. // check for immune characters
  3. if ( containsCharacter(c, immune ) ) {
  4. return ""+c;
  5. }
  6. // check for alphanumeric characters
  7. String hex = Codec.getHexForNonAlphanumeric(c);
  8. if ( hex == null ) {
  9. return ""+c;
  10. }
  11. // check for illegal characters
  12. //ascii码中的非数字,字符的编码,一般为非打印字符,即不能转换的为uncoide的ascii字符,直接替换成\ufffd,显示的为?
  13. if ( ( c <= 0x1f && c != ‘\t‘ && c != ‘\n‘ && c != ‘\r‘ ) || ( c >= 0x7f && c <= 0x9f ) )
  14. {
  15. hex = REPLACEMENT_HEX;  // Let‘s entity encode this instead of returning it
  16. c = REPLACEMENT_CHAR;
  17. }
  18. // check if there‘s a defined entity
  19. //#恶意字符以实体的形式输出
  20. String entityName = (String) characterToEntityMap.get(c);
  21. if (entityName != null) {
  22. return "&" + entityName + ";";
  23. }
  24. // return the hex entity as suggested in the spec,#如果是16进制就转换为html16进制实体字符输出
  25. return "&#x" + hex + ";";
  26. }
    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代码页面,使用如下示例的方法即可。

[java] view plain copy print?

  1. String username = ESAPI.encoder().encodeForJavaScript(req.getParameter("name"))
String username = ESAPI.encoder().encodeForJavaScript(req.getParameter("name"))

我们研究一下encodeForJavaScript方法:

[java] view plain copy print?

  1. public String encodeCharacter( char[] immune, Character c ) {
  2. // check for immune characters
  3. if ( containsCharacter(c, immune ) ) {
  4. return ""+c;
  5. }
  6. // check for alphanumeric characters
  7. String hex = Codec.getHexForNonAlphanumeric(c);
  8. if ( hex == null ) {
  9. return ""+c;
  10. }
  11. // Do not use these shortcuts as they can be used to break out of a context
  12. // if ( ch == 0x00 ) return "\\0";
  13. // if ( ch == 0x08 ) return "\\b";
  14. // if ( ch == 0x09 ) return "\\t";
  15. // if ( ch == 0x0a ) return "\\n";
  16. // if ( ch == 0x0b ) return "\\v";
  17. // if ( ch == 0x0c ) return "\\f";
  18. // if ( ch == 0x0d ) return "\\r";
  19. // if ( ch == 0x22 ) return "\\\"";
  20. // if ( ch == 0x27 ) return "\\‘";
  21. // if ( ch == 0x5c ) return "\\\\";
  22. // encode up to 256 with \\xHH,编码成js十六进制的形式
  23. String temp = Integer.toHexString(c);
  24. if ( c < 256 ) {
  25. String pad = "00".substring(temp.length() );
  26. return "\\x" + pad + temp.toUpperCase();
  27. }
  28. // otherwise encode with \\uHHHH,编码成jsunicode编码格式
  29. String pad = "0000".substring(temp.length() );
  30. return "\\u" + pad + temp.toUpperCase();
  31. }
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编码来防止恶意标签的执行。如果感兴趣可以看一下其他的编码方法,原理大致相同就不在一一介绍。

原文地址:https://www.cnblogs.com/firstdream/p/8309464.html

时间: 2024-08-30 04:59:58

详解Javaweb中常见漏洞的防御的相关文章

详解javaweb中jstl如何循环List中的Map数据_java - JAVA

文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 详解javaweb中jstl如何循环List中的Map数据 第一种方式: 1:后台代码(测试) List<Map<String, Object>> list = new ArrayList<Map<String,Object>>(); Map<String, Object> map = null; for (int i = 0; i < 4; i++) { ma

详解Android中那些酷炫返回方式的实现

Android手机都会有返回键,不管是实体键,还是虚拟键.Android用户主要也都是通过这个返回键操控页面返回方式的,不比IOS逼格甚高的只保留一个操作键.这种方式是最普遍的返回方式,还有一种也是比较常见的,那就是页面内部自己响应.绝大多数APP每个页面的设计图顶部左侧都会有一个返回键图标,偶尔也有奇葩的设计放在底部左侧,点击这个图标即finish掉当前页面.简单的介绍完了最常见的两种方式,下面为大家介绍两种更友好的交互方式. 拿大家比较常用的三款社交软件的交互来说.腾讯微博的返回方式除去上述

Android 布局学习之——Layout(布局)详解二(常见布局和布局参数)

[Android布局学习系列]   1.Android 布局学习之——Layout(布局)详解一   2.Android 布局学习之——Layout(布局)详解二(常见布局和布局参数)   3.Android 布局学习之——LinearLayout的layout_weight属性   4.Android 布局学习之——LinearLayout属性baselineAligned的作用及baseline    Layout Parameters(布局参数): 在XML文件中,我们经常看到类似与lay

详解Objective-C中委托和协议

Objective-C委托和协议本没有任何关系,协议如前所述,就是起到C++中纯虚类的作用,对于“委托”则和协议没有关系,只是我们经常利用协议还实现委托的机制,其实不用协议也完全可以实现委托. AD:[活动]Web和APP兼容性实战 Win10训练营免费报名 Objective-C中委托和协议是本文要介绍的内容,委托和协议是两个概念,协议实际上相当于C++中的纯虚类的概念,只定义并只能由其它类来实现.而委托类似于Java中的接口.(Objective-C实现委托这种机制是利用协议来实现的,这种说

详解C中volatile关键字

volatile 影响编译器编译的结果,指出,volatile 变量是随时可能发生变化的,与volatile变量有关的运算,不要进行编译优化,以免出错,(VC++ 在产生release版可执行码时会进行编译优化,加volatile关键字的变量有关的运算,将不进行编译优化.). 例如: volatile int i=10; int j = i; ... int k = i; volatile 告诉编译器i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的可执行码会重新从i

详解Python中re.sub--转载

[背景] Python中的正则表达式方面的功能,很强大. 其中就包括re.sub,实现正则的替换. 功能很强大,所以导致用法稍微有点复杂. 所以当遇到稍微复杂的用法时候,就容易犯错. 所以此处,总结一下,在使用re.sub的时候,需要注意的一些事情. 解释具体的注意事项之前,先把其具体的解释贴出来: re.sub re.sub(pattern, repl, string, count=0, flags=0) Return the string obtained by replacing the

(转)详解Linux中SSH远程访问控制

详解Linux中SSH远程访问控制 原文:http://blog.51cto.com/dengqi/1260038 SSH:是一种安全通道协议,主要用来实现字符界面的远程登录,远程复制等功能(使用TCP的22号端口).SSH协议对通信双方的数据传输进行了加密处理,其中包括用户登录时输入的用户口令. 在RHEL 5系统中使用的是OpenSSH服务器由openssh,openssh-server等软件包提供的(默认已经安装),并以将sshd添加为标准的系统服务. SSH提供一下两种方式的登录验证:

详解Linux中的日志及用日志来排查错误的方法

这篇文章主要介绍了详解Linux中的日志及用日志来排查错误的方法,是Linux入门学习中的基础知识,需要的朋友可以参考下 Linux 系统日志许多有价值的日志文件都是由 Linux 自动地为你创建的.你可以在 /var/log 目录中找到它们.下面是在一个典型的 Ubuntu 系统中这个目录的样子: 一些最为重要的 Linux 系统日志包括: /var/log/syslog 或 /var/log/messages 存储所有的全局系统活动数据,包括开机信息.基于 Debian 的系统如 Ubunt

用IDEA详解Spring中的IoC和DI(挺透彻的,点进来看看吧)

用IDEA详解Spring中的IoC和DI 一.Spring IoC的基本概念 控制反转(IoC)是一个比较抽象的概念,它主要用来消减计算机程序的耦合问题,是Spring框架的核心.依赖注入(DI)是IoC的另外一种说法,只是从不同的角度描述相同的概念.看完这两句,是不是不但没懂,反而更迷惑了,别急,往下看: IoC的背景 我们都知道,在采用面向对象方法设计的软件系统中,它的底层实现都是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑. 如果我们打开机械式手表的后盖,就会看到与