web-dev-qa-db-ja.com

Java-SQLインジェクションを防ぐためのエスケープ文字列

アンチSQLインジェクションをJavaに配置しようとしていますが、「replaceAll」文字列関数を使用することは非常に困難です。最終的には、既存の\\\に、"\"に、'\'に、そして\n\\nに変換し、文字列がMySQL SQLインジェクションによって評価されるときにブロックされる関数が必要です。

作業していたコードをジャッキアップしましたが、関数内の\\\\\\\\\\\のすべてが私の目を楽しませています。もし誰かがこれの例を持っているなら、私はそれを大いに感謝します。

140
Scott Bonner

PreparedStatementsは、SQLインジェクションを不可能にするための方法です。以下は、ユーザーの入力をパラメーターとしてとる簡単な例です。

public insertUser(String name, String email) {
   Connection conn = null;
   PreparedStatement stmt = null;
   try {
      conn = setupTheDatabaseConnectionSomehow();
      stmt = conn.prepareStatement("INSERT INTO person (name, email) values (?, ?)");
      stmt.setString(1, name);
      stmt.setString(2, email);
      stmt.executeUpdate();
   }
   finally {
      try {
         if (stmt != null) { stmt.close(); }
      }
      catch (Exception e) {
         // log this error
      }
      try {
         if (conn != null) { conn.close(); }
      }
      catch (Exception e) {
         // log this error
      }
   }
}

名前やメールに含まれる文字に関係なく、それらの文字はデータベースに直接配置されます。 INSERTステートメントには影響しません。

データ型ごとに異なるsetメソッドがあります。どのメソッドを使用するかは、データベースフィールドの種類によって異なります。たとえば、データベースにINTEGER列がある場合、setIntメソッドを使用する必要があります。 PreparedStatementドキュメント は、データの設定および取得に使用できるすべての異なるメソッドをリストします。

238
Kaleb Brasee

SQLインジェクションを防ぐ唯一の方法は、パラメーター化されたSQLを使用することです。 SQLを生計のためにハックする人々よりも賢いフィルターを構築することは不可能です。

したがって、すべての入力、更新、およびwhere句にパラメーターを使用します。動的SQLは、ハッカーにとって単純に開かれたドアであり、ストアドプロシージャに動的SQLが含まれています。パラメータ化、パラメータ化、パラメータ化。

44
Cylon Cat

Defense Option 1:Prepared Statements(Parameterized Queries) または Defense Option 2:Stored Procedures を実際に使用できない場合は、独自のツールを構築しないで、-を使用してください OWASP Enterprise Security API 。 Google Codeでホストされている OWASP ESAPI から:

独自のセキュリティコントロールを作成しないでください!すべてのWebアプリケーションまたはWebサービスのセキュリティコントロールの開発に関して、車輪の再発明は時間の無駄と大きなセキュリティホールにつながります。 OWASPエンタープライズセキュリティAPI(ESAPI)ツールキットは、ソフトウェア開発者がセキュリティ関連の設計および実装の欠陥から保護するのに役立ちます。

詳細については、 JavaでのSQLインジェクションの防止 および SQLインジェクション防止のチートシート を参照してください。

Defense Option 3:Escaping All User Supplied Input に特に注意してください OWASP ESAPI プロジェクトを導入します)。

34
Pascal Thivent

(これは元の質問の下でのOPのコメントに対する答えです。PreparedStatementは正規表現ではなく、この仕事のためのツールであることに完全に同意します。)

\nと言うとき、シーケンス\ + nか、実際の改行文字のことですか? \ + nの場合、タスクは非常に簡単です。

s = s.replaceAll("['\"\\\\]", "\\\\$0");

入力内の1つのバックスラッシュに一致させるには、正規表現文字列に4つのバックスラッシュを入れます。出力にバックスラッシュを1つ入れるには、それらの4つを置換文字列に入れます。これは、Java文字列リテラルの形式で正規表現と置換を作成していることを前提としています。それらを他の方法で作成する場合(たとえば、ファイルからそれらを読み取ることによって)、すべての二重エスケープを行う必要はありません。

入力に改行文字があり、それをエスケープシーケンスに置き換える場合は、次のようにして入力を2回パスすることができます。

s = s.replaceAll("\n", "\\\\n");

または、2つのバックスラッシュが必要な場合があります(これについてはあまり明確ではありません)。

s = s.replaceAll("\n", "\\\\\\\\n");
19
Alan Moore

PreparedStatementsは、すべてではありませんがほとんどの場合に使用する方法です。クエリまたはその一部を作成して、後で使用するために文字列として保存する必要がある場合があります。さまざまなプログラミング言語の詳細とAPIについては、- OWASPサイトSQLインジェクション防止チートシート をご覧ください。

13
user566057

正規表現を使用して、SQLステートメントが StatementNAME _ ではなく PreparedStatementNAME _ を介してデータベースに送信されているように聞こえるSQLインジェクションを引き起こす可能性のあるテキストを削除します。

そもそもSQLインジェクションを防ぐ最も簡単な方法の1つは、PreparedStatementname__を使用することです。これは、データを受け入れ、プレースホルダーを使用してSQLステートメントに置き換えます。

詳細については、 準備されたステートメントの使用 from The Java Tutorials が出発点として適しています。

9
coobird

プリペアドステートメントは最適なソリューションですが、本当に手動で行う必要がある場合は、Apache Commons-Langライブラリの StringEscapeUtils クラスを使用することもできます。 escapeSql(String) メソッドがあり、これを使用できます。

import org.Apache.commons.lang.StringEscapeUtils; … String escapedSQL = StringEscapeUtils.escapeSql(unescapedSQL);

8
ToBe_HH

以下のコードが必要です。一見すると、これは私が作成した古いコードのように見えるかもしれません。ただし、私が行ったのは http://grepcode.com/file/repo1.maven.org/maven2/mysql/mysql-connector-Java/5.1.31/com/mysql/のソースコードを見てjdbc/PreparedStatement.Java 。その後、setString(int parameterIndex、String x)のコードを注意深く調べて、エスケープする文字を見つけ、これを自分のクラスにカスタマイズして、必要な目的に使用できるようにしました。結局のところ、これがOracleがエスケープする文字のリストである場合、これを知ることはセキュリティ面で本当に安心です。次のメジャーJavaリリースのために、これに似たメソッドを追加するためにOracleがナッジを必要とするかもしれません。

public class SQLInjectionEscaper {

    public static String escapeString(String x, boolean escapeDoubleQuotes) {
        StringBuilder sBuilder = new StringBuilder(x.length() * 11/10);

        int stringLength = x.length();

        for (int i = 0; i < stringLength; ++i) {
            char c = x.charAt(i);

            switch (c) {
            case 0: /* Must be escaped for 'mysql' */
                sBuilder.append('\\');
                sBuilder.append('0');

                break;

            case '\n': /* Must be escaped for logs */
                sBuilder.append('\\');
                sBuilder.append('n');

                break;

            case '\r':
                sBuilder.append('\\');
                sBuilder.append('r');

                break;

            case '\\':
                sBuilder.append('\\');
                sBuilder.append('\\');

                break;

            case '\'':
                sBuilder.append('\\');
                sBuilder.append('\'');

                break;

            case '"': /* Better safe than sorry */
                if (escapeDoubleQuotes) {
                    sBuilder.append('\\');
                }

                sBuilder.append('"');

                break;

            case '\032': /* This gives problems on Win32 */
                sBuilder.append('\\');
                sBuilder.append('Z');

                break;

            case '\u00a5':
            case '\u20a9':
                // escape characters interpreted as backslash by mysql
                // fall through

            default:
                sBuilder.append(c);
            }
        }

        return sBuilder.toString();
    }
}
6
Richard

レガシーシステムを扱っている場合、またはPreparedStatementsに切り替える時間が多すぎる場合-つまり、他の回答で提案されたベストプラクティスを使用するのに障害がある場合は、試すことができます AntiSQLFilter

6
Bozho

From:[ソース]

public String MysqlRealScapeString(String str){
  String data = null;
  if (str != null && str.length() > 0) {
    str = str.replace("\\", "\\\\");
    str = str.replace("'", "\\'");
    str = str.replace("\0", "\\0");
    str = str.replace("\n", "\\n");
    str = str.replace("\r", "\\r");
    str = str.replace("\"", "\\\"");
    str = str.replace("\\x1a", "\\Z");
    data = str;
  }
return data;

}

0

StringEscapeUtilsescapeSqlが仕事をすることができると思います。まだ試してみましたが、信頼できるオプションのように見えます。

0
akostadinov

あらゆる場所で準備されたステートメントを適用できないレガシーシステムの場合、sqlmapがsqlインジェクションから防止するためにソリューションのテストロットを検索した後。

Java-security-cross-site-scripting-xss-and-sql-injectionトピック ソリューションでした

@Richardのソリューションを試しましたが、私の場合はうまくいきませんでした。フィルターを使用しました

このフィルターの目標は、要求を次のように変換する独自のコード化ラッパーMyHttpRequestWrapperにラッパーすることです。

org.springframework.web.util.HtmlUtils.htmlEscape(…)メソッドを介して、特殊文字(<、>、 ´、…)を含むHTTPパラメーターをHTMLコードに変換します。注:Apache Commonsには同様のクラスがあります:org.Apache.commons.lang.StringEscapeUtils.escapeHtml(…)SQLインジェクション文字( '、“、…)は、Apache Commonsクラスorg.Apache.commons.lang.StringEscapeUtilsを介して。 escapeSql(…)

<filter>
<filter-name>RequestWrappingFilter</filter-name>
<filter-class>com.huo.filter.RequestWrappingFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>RequestWrappingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>




package com.huo.filter;

import Java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletReponse;
import javax.servlet.http.HttpServletRequest;

public class RequestWrappingFilter implements Filter{

    public void doFilter(ServletRequest req, ServletReponse res, FilterChain chain) throws IOException, ServletException{
        chain.doFilter(new MyHttpRequestWrapper(req), res);
    }

    public void init(FilterConfig config) throws ServletException{
    }

    public void destroy() throws ServletException{
    }
}




package com.huo.filter;

import Java.util.HashMap;
import Java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.Apache.commons.lang.StringEscapeUtils;

public class MyHttpRequestWrapper extends HttpServletRequestWrapper{
    private Map<String, String[]> escapedParametersValuesMap = new HashMap<String, String[]>();

    public MyHttpRequestWrapper(HttpServletRequest req){
        super(req);
    }

    @Override
    public String getParameter(String name){
        String[] escapedParameterValues = escapedParametersValuesMap.get(name);
        String escapedParameterValue = null; 
        if(escapedParameterValues!=null){
            escapedParameterValue = escapedParameterValues[0];
        }else{
            String parameterValue = super.getParameter(name);

            // HTML transformation characters
            escapedParameterValue = org.springframework.web.util.HtmlUtils.htmlEscape(parameterValue);

            // SQL injection characters
            escapedParameterValue = StringEscapeUtils.escapeSql(escapedParameterValue);

            escapedParametersValuesMap.put(name, new String[]{escapedParameterValue});
        }//end-else

        return escapedParameterValue;
    }

    @Override
    public String[] getParameterValues(String name){
        String[] escapedParameterValues = escapedParametersValuesMap.get(name);
        if(escapedParameterValues==null){
            String[] parametersValues = super.getParameterValues(name);
            escapedParameterValue = new String[parametersValues.length];

            // 
            for(int i=0; i<parametersValues.length; i++){
                String parameterValue = parametersValues[i];
                String escapedParameterValue = parameterValue;

                // HTML transformation characters
                escapedParameterValue = org.springframework.web.util.HtmlUtils.htmlEscape(parameterValue);

                // SQL injection characters
                escapedParameterValue = StringEscapeUtils.escapeSql(escapedParameterValue);

                escapedParameterValues[i] = escapedParameterValue;
            }//end-for

            escapedParametersValuesMap.put(name, escapedParameterValues);
        }//end-else

        return escapedParameterValues;
    }
}
0
shareef