【ESAPI学习研究】SQL注入


最近笔者最近在进行Java安全SDK的研究编写,过程中对OWASP的ESAPI做了一些研究,收获颇多。

ESAPI (OWASP企业安全应用程序接口)是一个免费、开源的、网页应用程序安全控件库,它使程序员能够更容易写出更低风险的程序。ESAPI接口库被设计来使程序员能够更容易的在现有的程序中引入安全因素。ESAPI库也可以成为作为新程序开发的基础。除了语言方面的差异,所有的OWASP ESAPI版本都具有如下相同的基本设计结构:
1、都有一整套安全控件接口。例如,这些安全接口中定义了发送给不同安全控件的参数类型;
2、每个安全控件都有一个参考实现。 这些实现不是基于特定组织或者特定程序的。例如:基于字符串的输入验证;
3、程序开发者可以有选择的实现自己的安全控件接口。可能这些接口类中的应用逻辑是由您的组织开发的或者为您公司定制的。例如:企业认证。

该版本ESAPI下载地址:https://github.com/ESAPI/esapi-java-legacy

SQL注入

该版本的ESAPI中,SQL注入主要通过编码转义的方式进行防御修复。编码的方法主要位于codecs文件夹中。

Codec


org.owasp.esapi.codecs包中,提供了Codec接口。Codec接口定义了一组用于编码和解码应用程序级编码方法的方法,例如HTML实体编码和URL编码。这些编解码器允许逐字符解码。

  • encode
  • encodeCharacter
  • decode
  • decodeCharacter
  • getHexForNonALphanumeric
    查找任何非字母数字字符的十六进制值
  • toOctal
  • toHex
  • containsCharacter

AbstractCodec


抽象类AbstractCodec实现了Codec接口.

  • AbstractCodec
    类中定义了一个大小为256的hex字符数组,用来标记要编码的字符。默认构造方法中对数组进行初始化,存储字符的十六进制字符以节省时间。如果不应该对该字符进行编码,则存储null。
public AbstractCodec() {      
    for ( char c = 0; c < 0xFF; c++ ) {            
        if ( c >= 0x30 && c <= 0x39 || c >= 0x41 && c <= 0x5A || c >= 0x61 && c <= 0x7A ){                  
            hex[c] = null;
        } else {                  
            hex[c] = toHex(c).intern();            
        }      
    }
 }
  • encode
    input调用encodeCharacter方法进行逐字符编码
    • codePointAt
      返回字符串中指定索引处的字符的Unicode值
    • isBmpCodePoint
      确定指定的字符(Unicode代码点)是否在Basic Multilingual Plane(BMP,Unicode中的基于多文种平面)中
@Overridepublic 
String encode(char[] immune, String input) {
    StringBuilder sb = new StringBuilder();
    for(int offset  = 0; offset < input.length(); ) {
        final int point = input.codePointAt(offset);
        if (Character.isBmpCodePoint(point)) {
            //We can then safely cast this to char and maintain legacy behavior.
            sb.append(encodeCharacter(immune, new Character((char) point)));
        } else {
            sb.append(encodeCharacter(immune, point));
        }            
        offset += Character.charCount(point);      
    }      
    return sb.toString();
}
  • encodeCharacter
    这里只是实现了对input中的字符进行合法性判断
@Overridepublic 
String encodeCharacter( char[] immune, int codePoint ) {
    String rval = "";      
    if(Character.isValidCodePoint(codePoint)){
        rval = new StringBuilder().appendCodePoint(codePoint).toString();
    }      
    return rval;
}
  • decodeCharacter
  • getHexForNonAlphanumeric
public String getHexForNonAlphanumeric(char c) {
    if(c<0xFF)
        return hex[c];
    return toHex(c);
}

public String getHexForNonAlphanumeric(int c) {
    if (c<0xFF) {
        return hex[c];
    } else {
        return toHex(c);
    }
}
  • toOctal
  • toHex
  • containsCharacter
public boolean containsCharacter( char c, char[] array ) {
    for (char ch : array) {
        if (c == ch) return true;
    }      
    return false;
}

AbstarctCharacterCodec


这个抽象类继承了AbstractCodec类,重写了decode方法

@Override
public String decode(String input) {
    StringBuilder sb = new StringBuilder();
    PushbackSequence<Character> pbs = new PushbackString(input);
    while (pbs.hasNext()) {
        Character c = decodeCharacter(pbs);
        if (c != null) {
            sb.append(c);
        } else {
            sb.append(pbs.next());
        }      
    }      
    return sb.toString();
}

AbstractIntegerCodec


这个抽象类继承了AbstractCodec类,重写了decode方法

@Override
public String decode(String input) {
    StringBuilder sb = new StringBuilder();
    PushbackSequence<Integer> pbs = new PushBackSequenceImpl(input);
    while (pbs.hasNext()) {
        Integer c = decodeCharacter(pbs);
        if (c != null && Character.isValidCodePoint(c)) {
            sb.appendCodePoint(c);
        } else {
            sb.appendCodePoint(pbs.next());
        }      
    }      
    return sb.toString();
}

MySQLCodec


该类实现了MySQL数据库中的转义编码器。可以满足MySQL中的两种SQL Mode中的字符编码转义(MySQL中的sql mode设置将会影响MySQL的语法)。

  • ANSI
    将所有的 '(单引号) 替换为 ''(两个单引号)
  • Standard
    字符 Ascii码 转义后字符
    NUL 0x00 \0
    BS 0x08 \b
    TAB 0x09 \t
    LF 0x0a \n
    CR 0x0d \r
    SUB 0x1a \Z
    " 0x22 \"
    % 0x25 %
    ' 0x27 \'
    \ 0x5c \\
    _ 0x5f \_

所有其他ASCII值小于256的其他非字母数字字符都转为\c字符

MySQLCodec类继承于AbstractCharacterCodec抽象类

  • MySQLCodec
    根据sql mode初始化MySQL codec
public MySQLCodec( int mode ) {      this.mode = Mode.findByKey(mode);}

public MySQLCodec( Mode mode ) {    this.mode = mode;}
  • encodeCharacter
    根据不同的sql mode调用不同的encodeCharacter方法对字符进行编码
  • encodeCharacterANSI
    ANSI模式下的编码方法
private String encodeCharacterANSI( Character c ) {
    if ( c == '\'' )
        return "\'\'";
    return ""+c;
}
  • encodeCharacterMySQL
    标准模式下的编码方法
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;
}
  • decodeCharacter
    根据不同的sql mode调用不同的decodeCharacter方法对字符进行解码
  • decodeCharacterANSI
    ANSI模式下的字符解码方法
  • decodeCharacterMySQL
    标准模式下的字符解码方法

OracleCodec


OracleCodec类继承了AbstractCharacterCodec类,为Oracle数据库中的字符编码器。这个方法只能对位于单引号中的数据进行安全过滤(写的不全)。

  • encodeCharacter
    将所有的 '(单引号) 替换为 ''(两个单引号)
public String encodeCharacter( char[] immune, Character c ) {
    if ( c.charValue() == '\'' )
        return "\'\'";
    return ""+c;
}
  • decodeCharacter

DB2Codec


DB2Codec类继承了AbstractCharacterCodec类,为DB2数据库中的字符编码器,但是只能在有限的情况下进行安全过滤(写的不全)。

  • encodeCharacter
    将所有的 '(单引号) 替换为 ''(两个单引号);将 ;(分号) 转为 .(点号)
public String encodeCharacter(char[] immune, Character c) {
    if (c.charValue() == '\'')
        return "\'\'";      
    if (c.charValue() == ';')
        return ".";
    return "" + c;
}
  • decodeCharacter


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM