Java-SQLインジェクションを防ぐためのエスケープ文字列
-
06-07-2019 - |
質問
JavaでいくつかのアンチSQLインジェクションを実行しようとしていますが、「replaceAll」を使用するのは非常に難しいと感じています。文字列関数。最終的には、既存の \
を \\
に変換し、"
を \"
に変換する関数が必要です。 、任意の '
から \'
、および任意の \ n
から \\ n
。 MySQLによるSQLインジェクションはブロックされます。
作業していたコードをジャッキアップしましたが、関数内のすべての \\\\\\\\\\\
が目を見張っています。誰かがたまたまこれの例を持っているなら、私はそれを大いに感謝します。
解決
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のドキュメントには、利用可能なさまざまなメソッドがすべてリストされています。データの設定および取得用。
他のヒント
SQLインジェクションを防ぐ唯一の方法は、パラメーター化されたSQLを使用することです。 SQLを生でハッキングする人々よりも賢いフィルターを構築することは、単に不可能です。
したがって、すべての入力、更新、およびwhere句にパラメーターを使用します。動的SQLは、ハッカーにとって単なる開かれた扉であり、ストアドプロシージャに動的SQLが含まれています。パラメータ化、パラメータ化、パラメータ化。
本当に防御オプション1:準備済みステートメント(パラメーター化されたクエリ)または Defenseオプション2:ストアドプロシージャ、独自のツールを構築せず、 OWASP Enterprise Securityを使用しますAPI 。 Googleコードでホストされている OWASP ESAPI から:
独自のセキュリティコントロールを作成しないでください!すべてのWebアプリケーションまたはWebサービスのセキュリティコントロールの開発に関して、車輪の再発明は時間の無駄と大きなセキュリティホールにつながります。 OWASPエンタープライズセキュリティAPI(ESAPI)ツールキットは、ソフトウェア開発者がセキュリティ関連の設計および実装の欠陥を防ぐのに役立ちます。
詳細については、 JavaでのSQLインジェクションの防止および SQLインジェクション防止に関するチートシート。
すべてのユーザー入力をエスケープするに特別な注意を払うa> OWASP ESAPI プロジェクトを紹介しています。
(これは元の質問の下でのOPのコメントに対する答えです。PreparedStatementは正規表現ではなく、この仕事のためのツールであることに完全に同意します。)
\ n
と言うとき、シーケンス \
+ n
または実際の改行文字を意味しますか? \
+ n
の場合、タスクは非常に簡単です:
s = s.replaceAll("['\"\\\\]", "\\\\s = s.replaceAll("\n", "\\\\n");
");
入力の1つのバックスラッシュに一致させるには、正規表現文字列に4つのバックスラッシュを入れます。出力にバックスラッシュを1つ入れるには、それらの4つを置換文字列に入れます。これは、Java文字列リテラルの形式で正規表現と置換を作成していることを前提としています。他の方法で作成する場合(たとえば、ファイルから読み込むなど)、二重にエスケープする必要はありません。
入力に改行文字があり、それをエスケープシーケンスに置き換える場合は、次のようにして入力を2回パスすることができます。
s = s.replaceAll("\n", "\\\\\\\\n");
または、2つのバックスラッシュが必要な場合もあります(これについてはあまり明確ではありません):
<*>PreparedStatementsは、すべてではないがほとんどの場合に使用する方法です。クエリまたはその一部を作成して、後で使用するために文字列として保存する必要がある場合があります。 OWASPサイトをご覧ください。
正規表現を使用して、SQLステートメントが PreparedStatement
。
最初にSQLインジェクションを防ぐ最も簡単な方法の1つは、 PreparedStatement
を使用することです。これは、文字列の連結に依存しないプレースホルダーを使用して、SQLステートメントに置き換えるデータを受け入れますデータベースに送信するSQLステートメントを作成します。
詳細については、準備済みステートメントの使用 Javaチュートリアルを開始するのに適した場所です。
準備済みのステートメントが最適なソリューションですが、本当に手動で行う必要がある場合は、 StringEscapeUtils
クラス。 escapeSql(String)
メソッドを使用できます。
import org.apache.commons.lang.StringEscapeUtils;
&#8230;
String escapedSQL = StringEscapeUtils.escapeSql(unescapedSQL);
レガシーシステムを扱っている場合、または PreparedStatement
にあまりにも短い時間で切り替えることができる場所が多すぎる場合-つまり、他の人によって提案されたベストプラクティスの使用に障害がある場合答えは、 AntiSQLFilter
以下のコードが必要です。一見すると、これは私が作成した古いコードのように見えるかもしれません。しかし、私がしたことは 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();
}
}
すべての場所に準備された文を適用できないレガシーシステムの場合、sqlmapがsqlインジェクションから防止するためのソリューションのテストロットを検索した後。
java-security-cross- site-scripting-xss-and-sql-injectionトピック ソリューションでした
iは@Richardのソリューションを試しましたが、私の場合はうまくいきませんでした。 フィルターを使用しました
このフィルターの目的は、リクエストを独自のコードにラップすることです 変換するラッパーMyHttpRequestWrapper:
特殊文字(&lt;、&gt;、&#8216;、&#8230;)を含むHTTPパラメーターをHTMLに org.springframework.web.util.HtmlUtils.htmlEscape(&#8230;)を介したコード 方法。注:Apache Commonsには同様のクラスがあります: org.apache.commons.lang.StringEscapeUtils.escapeHtml(&#8230;)SQL Apache Commonsクラスを介した注入文字(&#8216;、&#8220;、&#8230;) org.apache.commons.lang.StringEscapeUtils.escapeSql(&#8230;)
<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;
}
}
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;
}