SQL Injection

SQL Injectionとは、以下のようにパラメータにSQLの演算子を挿入して、 不正な操作を実行することを指します。

SQL Injection

問題

まずは不正操作が実行できることを試してみましょう。
ログイン画面で以下のようなユーザID、パスワードを入力してみてください。

これらの文字列はユーザID,パスワードとして不正であるのにもかかわらず、 ログインできてしまいます。

これは、TodoServletクラスのgetUser(String userID, String password)メソッドで 以下のようなSQL文を発行してしまうことに起因します。

SELECT ID,NAME FROM TODO_USER WHERE ID='' OR (''='' AND PASSWORD='') OR ''=''

WHERE節の末尾の条件が常に成立してしまうために、このSELECT文が常にユーザ情報を返してしまいます。

回避方法

この問題を回避するためには、 SQL文に設定するパラメータが不正なものでないかをチェックする必要があります。

例えば、以下のようにユーザID,パスワードそれぞれに半角英数字のみを許容することで、 このような問題の発生を防ぐことができます。

/**
     * ユーザを取得します。
     * 
     * @param userID
     * @param password
     * @return
     * @throws ServletException
     */
    private User getUser(String userID, String password) throws SQLException {
        Statement statement = null;
        try {
            // パラメータのチェック
            if(isValidUserID(userID) == false) {
                return null;
            }
            if(isValidPassword(password) == false) {
                return null;
            }
            
...
    
    /**
     * 有効なユーザIDかどうかを判定します。
     * @param name
     * @return
     */
    private boolean isValidUserID(String name) {
        return isAlphaOrDigit(name);
    }
    
    /**
     * 有効なパスワードかどうかを判定します。
     * @param password
     * @return
     */
    private boolean isValidPassword(String password) {
        return isAlphaOrDigit(password);
    }
    
    /**
     * 文字列が半角英数字から構成されているかどうかを判定します。
     * @param str
     * @return
     */
    private boolean isAlphaOrDigit(String str) {
        for(int i = 0; i < str.length(); i ++) {
            char ch = str.charAt(i);
            if(isAlphaOrDigit(ch) == false) {
                return false;
            }
        }
        return true;
    }
    
    /**
     * 文字が半角英数字かどうかを判定します。
     * @param ch
     * @return
     */
    private boolean isAlphaOrDigit(char ch) {
        if('A' <= ch && ch <= 'Z') {
            return true;
        }
        if('a' <= ch && ch <= 'z') {
            return true;
        }
        if('0' <= ch && ch <= '9') {
            return true;
        }
        return false;
    }