SpringMVCフレームワークでよく見られる脆弱性の防御を詳しく理解する


次にSpringMVCに付属するデータベース操作クラスjdbcTemplateを用いて例を示す.たとえば次のDaoには次の2つの関数があります.
関数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は一般的にこの方式を使用しています
//DB 2注入防止
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が防御のポイントです.この2つの関数の違いを見てみましょう.もし選択したら、私たちは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 "\
"; 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の2つの方法でsql注入を防御することを紹介しましたが、できるだけバインド変数の形式を使って注入を防ぎ、安全性が良いことをお勧めします.
0 x 02:クロスステーションスクリプト攻撃
クロスステーションスクリプト攻撃の防御について,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 != '
' && 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 "" + hex + ";";

}
jsにおける悪意のある文字の符号化jsの16進符号化またはjsunicode符号化を用いた符号化を見ることができます.
実は上の方法の多くは文字に対してhtmlエンティティの符号化を行って、htmlの16進数の符号化、jsの16進数の符号化、jsunicodeの符号化とurlの符号化は悪意のあるラベルの実行を防止します.興味があれば他の符号化方法を見てもいいですが、原理はほぼ同じで、一つ一つ紹介していません.