【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