入力の検証や表現の問題は、メタキャラクター、代替エンコーディング、数値表現などによって引き起こされます。セキュリティの問題は、入力を信頼することに起因します。この問題に含まれるのは、「Buffer Overflow」、「Cross-Site Scripting」攻撃、「SQL Injection」などです。
...
String userName = ctx.getAuthenticatedUserName();
String itemName = request.getParameter("itemName");
String query = "FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ itemName + "'";
List items = sess.createQuery(query).list();
...
SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;
itemName
に単一引用符が含まれない場合のみクエリは正しく動作します。ユーザー名 wiley
を持つ攻撃者が文字列「name' OR 'a'='a
」を itemName
に入力すると、クエリは次のようになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
条件を追加すると、where 句は常に真 (true) の評価をします。そのため、このクエリは次のような単純なクエリと論理的に等しくなります。
SELECT * FROM items;
items
テーブルに格納されているすべてのエントリを返すようになりました。Example 1
で構築および実行されたクエリに悪意のある別の値が渡された場合の影響を検討します。ユーザー名 wiley
を持つ攻撃者が文字列「name'; DELETE FROM items; --
」を itemName
に入力すると、クエリは次の 2 つのクエリになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
--'
Example 1
と同様の技法を使用すると一般的な攻撃を有効にできます。攻撃者が文字列「name'); DELETE FROM items; SELECT * FROM items WHERE 'a'='a
」を入力すると、次の 3 つの有効なステートメントが作成されます。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
SELECT * FROM items WHERE 'a'='a';
#
文字を使用して定義されます。
<select id="getItems" parameterClass="MyClass" resultClass="items">
SELECT * FROM items WHERE owner = #userName#
</select>
#
文字は、iBatis が userName
変数を使用してパラメーター化されたクエリを作成することを示します。ただし、iBatis では、$
文字を使用して変数を SQL ステートメントに直接連結することもできるため、SQL Injection のチャンスが高まります。
<select id="getItems" parameterClass="MyClass" resultClass="items">
SELECT * FROM items WHERE owner = #userName# AND itemname = '$itemName$'
</select>
itemName
に単一引用符が含まれない場合のみクエリは正しく動作します。ユーザー名 wiley
を持つ攻撃者が文字列「name' OR 'a'='a
」を itemName
に入力すると、クエリは次のようになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
条件を追加すると、where 句は常に真 (true) の評価を行います。そのため、このクエリは次のような単純なクエリと論理的に等しくなります。
SELECT * FROM items;
items
テーブルに格納されているすべてのエントリを返すようになりました。Example 1
で構築および実行されたクエリに悪意のある別の値が渡された場合の影響を検討します。ユーザー名 wiley
を持つ攻撃者が文字列「name'; DELETE FROM items; --
」を itemName
に入力すると、クエリは次の 2 つのクエリになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
--'
Example 1
と同様のトリックを使用すると一般的な攻撃はできてしまいます。攻撃者が文字列「name'); DELETE FROM items; SELECT * FROM items WHERE 'a'='a
」を入力すると、次の 3 つの有効なステートメントが作成されます。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
SELECT * FROM items WHERE 'a'='a';
...
String userName = ctx.getAuthenticatedUserName();
String itemName = request.getParameter("itemName");
String sql = "SELECT * FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ itemName + "'";
Query query = pm.newQuery(Query.SQL, sql);
query.setClass(Person.class);
List people = (List)query.execute();
...
SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;
itemName
に単一引用符が含まれない場合のみクエリは正しく動作します。ユーザー名 wiley
を持つ攻撃者が文字列「name' OR 'a'='a
」を itemName
に入力すると、クエリは次のようになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
条件を追加すると、where 句は常に真 (true) の評価をします。そのため、このクエリは次のような単純なクエリと論理的に等しくなります。
SELECT * FROM items;
items
テーブルに格納されているすべてのエントリを返すようになりました。Example 1
で構築および実行されたクエリに悪意のある別の値が渡された場合の影響を検討します。ユーザー名 wiley
を持つ攻撃者が文字列「name'; DELETE FROM items; --
」を itemName
に入力すると、クエリは次の 2 つのクエリになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
--'
Example 1
と同様の技法を使用すると一般的な攻撃を有効にできます。攻撃者が文字列「name'); DELETE FROM items; SELECT * FROM items WHERE 'a'='a
」を入力すると、次の 3 つの有効なステートメントが作成されます。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
SELECT * FROM items WHERE 'a'='a';
owner
が一致するアイテムだけが表示されます。
...
string userName = ctx.getAuthenticatedUserName();
string query = "SELECT * FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ ItemName.Text + "'";
var items = dataContext.ExecuteCommand<Item>(query);
...
SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;
itemName
に単一引用符が含まれない場合のみクエリは正しく動作します。ユーザー名 wiley
を持つ攻撃者が文字列「name' OR 'a'='a
」を itemName
に入力すると、クエリは次のようになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
条件を追加すると、where 句は常に真 (true) の評価を行います。そのため、このクエリは次のような単純なクエリと論理的に等しくなります。
SELECT * FROM items;
items
テーブルに格納されているすべてのエントリを返すようになりました。Example 1
で構築および実行されたクエリに悪意のある別の値が渡された場合の影響を検討します。ユーザー名 wiley
を持つ攻撃者が文字列「name'); DELETE FROM items; --
」を itemName
に入力すると、クエリは次の 2 つのクエリになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
--'
Example 1
と同様の技法を使用すると一般的な攻撃を有効にできます。攻撃者が文字列「name'); DELETE FROM items; SELECT * FROM items WHERE 'a'='a
」を入力すると、次の 3 つの有効なステートメントが作成されます。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
SELECT * FROM items WHERE 'a'='a';
#
文字を使用して定義されます。
<select id="getItems" parameterType="domain.company.MyParamClass" resultType="MyResultMap">
SELECT *
FROM items
WHERE owner = #{userName}
</select>
#
文字と中カッコで囲んだ部分は、MyBatis が userName
変数を使用してパラメーター化されたクエリを作成することを示します。ただし、MyBatis では、$
文字を使用して変数を SQL ステートメントに直接連結することもできるため、SQL Injection のチャンスが高まります。
<select id="getItems" parameterType="domain.company.MyParamClass" resultType="MyResultMap">
SELECT *
FROM items
WHERE owner = #{userName}
AND itemname = ${itemName}
</select>
itemName
に単一引用符が含まれない場合のみクエリは正しく動作します。ユーザー名 wiley
を持つ攻撃者が文字列「name' OR 'a'='a
」を itemName
に入力すると、クエリは次のようになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
条件を追加すると、WHERE
句は常に真 (true) の評価を行います。そのため、このクエリは次のような単純なクエリと論理的に等しくなります。
SELECT * FROM items;
items
テーブルに格納されているすべてのエントリを返すようになりました。Example 1
で構築および実行されたクエリに悪意のある別の値が渡された場合の影響を検討します。ユーザー名 wiley
を持つ攻撃者が文字列「name'; DELETE FROM items; --
」を itemName
に入力すると、クエリは次の 2 つのクエリになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
--'
Example 1
と同様のトリックを使用すると一般的な攻撃はできてしまいます。攻撃者が文字列「name'); DELETE FROM items; SELECT * FROM items WHERE 'a'='a
」を入力すると、次の 3 つの有効なステートメントが作成されます。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
SELECT * FROM items WHERE 'a'='a';
owner
が一致するアイテムだけが表示されます。
...
string userName = ctx.GetAuthenticatedUserName();
string query = "SELECT * FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ ItemName.Text + "'";
List items = sess.CreateSQLQuery(query).List();
...
SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;
ItemName
に単一引用符が含まれない場合のみクエリは正しく動作します。ユーザー名 wiley
を持つ攻撃者が文字列「name' OR 'a'='a
」を ItemName
に入力すると、クエリは次のようになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
条件を追加すると、where 句は常に真 (true) の評価を行います。そのため、このクエリは次のような単純なクエリと論理的に等しくなります。
SELECT * FROM items;
items
テーブルに格納されているすべてのエントリを返すようになりました。Example 1
で構築および実行されたクエリに悪意のある別の値が渡された場合の影響を検討します。ユーザー名 wiley
を持つ攻撃者が文字列「name'; DELETE FROM items; --
」を ItemName
に入力すると、クエリは次の 2 つのクエリになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
--'
Example 1
と同様の技法を使用すると一般的な攻撃を有効にできます。攻撃者が文字列「name'; DELETE FROM items; SELECT * FROM items WHERE 'a'='a
」を入力すると、次の 3 つの有効なステートメントが作成されます。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
SELECT * FROM items WHERE 'a'='a';
owner
が一致するアイテムだけが表示されます。
...
string userName = identity.User;
string itemName = apiGatewayProxyRequest.QueryStringParameters['item'];
string statement = $"SELECT * FROM items WHERE owner = '{userName}' AND itemname = '{itemName}'";
var executeStatementRequest = new ExecuteStatementRequest();
executeStatementRequest.Statement = statement;
var executeStatementResponse = await dynamoDBClient.ExecuteStatementAsync(executeStatementRequest);
return displayResults(executeStatementResponse.Items);
...
SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;
itemName
に単一引用符が含まれない場合のみクエリは正しく動作します。ユーザー名 wiley
を持つ攻撃者が文字列「name' OR 'a'='a
」を itemName
に入力すると、クエリは次のようになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
条件を追加すると、where 句は常に真 (true) の評価を行います。そのため、このクエリは次のような単純なクエリと論理的に等しくなります。owner
が一致するアイテムだけが表示されます。
...
String userName = identity.getUser();
String itemName = apiGatewayProxyRequest.getQueryStringParameters('item');
String statement = String.format("SELECT * FROM items WHERE owner = '%s' AND itemname = '%s'", userName, itemName);
ExecuteStatementRequest executeStatementRequest = new ExecuteStatementRequest();
executeStatementRequest.setStatement(statement);
ExecuteStatementResponse executeStatementResponse = dynamoDBClient.executeStatement(executeStatementRequest);
return displayResults(executeStatementResponse.items());
...
SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;
itemName
に単一引用符が含まれない場合のみクエリは正しく動作します。ユーザー名 wiley
を持つ攻撃者が文字列「name' OR 'a'='a
」を itemName
に入力すると、クエリは次のようになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
条件を追加すると、where 句は常に真 (true) の評価を行います。そのため、このクエリは次のような単純なクエリと論理的に等しくなります。
...
String userName = ctx.getAuthenticatedUserName();
String itemName = request.getParameter("itemName");
String query = "SELECT * FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ itemName + "'";
ResultSet rs = stmt.execute(query);
...
SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;
itemName
に単一引用符が含まれない場合のみクエリは正しく動作します。ユーザー名 wiley
を持つ攻撃者が文字列「name' OR 'a'='a
」を itemName
に入力すると、クエリは次のようになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
条件を追加すると、where 句は常に真 (true) の評価をします。そのため、このクエリは次のような単純なクエリと論理的に等しくなります。
SELECT * FROM items;
items
テーブルに格納されているすべてのエントリを返すようになりました。Example 1
で構築および実行されたクエリに悪意のある別の値が渡された場合の影響を検討します。ユーザー名 wiley
を持つ攻撃者が文字列「name'; DELETE FROM items; --
」を itemName
に入力すると、クエリは次の 2 つのクエリになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
--'
Example 1
と同様の技法を使用すると一般的な攻撃を有効にできます。攻撃者が文字列「name'); DELETE FROM items; SELECT * FROM items WHERE 'a'='a
」を入力すると、次の 3 つの有効なステートメントが作成されます。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
SELECT * FROM items WHERE 'a'='a';
Example 1
を応用しています。
...
PasswordAuthentication pa = authenticator.getPasswordAuthentication();
String userName = pa.getUserName();
String itemName = this.getIntent().getExtras().getString("itemName");
String query = "SELECT * FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ itemName + "'";
SQLiteDatabase db = this.openOrCreateDatabase("DB", MODE_PRIVATE, null);
Cursor c = db.rawQuery(query, null);
...
mysql_real_escape_string()
などのエンコーディング関数を使用すると、ある程度は SQL Injection の脆弱性に対する攻撃を防ぐことはできますが、完全に防ぐことはできません。このようなエンコーディング関数に頼ることは、脆弱な拒否リストを使用して SQL Injection を阻止しようとするのと同じことであり、攻撃者によるステートメントの改変や任意の SQL コマンドの実行が可能となる場合があります。動的に解釈されるコードの特定のセクション内のどこに入力が現れるのかを静的に判断することは必ずしも可能ではないため、Fortify Secure Coding Rulepacks は検証された動的 SQL データを「SQL Injection: Poor Validation」の問題として提示します。これは、検証によってコンテキスト内での SQL Injection を十分に防ぐことができるとしても同様です。mysqli_real_escape_string()
の動作が変更される場合について示したものです。 SQL モードが「NO_BACKSLASH_ESCAPES」に設定されている場合、バックスラッシュ文字はエスケープ文字ではなく、通常の文字として扱われます [5]。 mysqli_real_escape_string()
はこのことを考慮するため、次のクエリは SQL Injection に対して脆弱となります。これは、データベースの設定によって "
が \"
にエスケープされなくなるためです。
mysqli_query($mysqli, 'SET SQL_MODE="NO_BACKSLASH_ESCAPES"');
...
$userName = mysqli_real_escape_string($mysqli, $_POST['userName']);
$pass = mysqli_real_escape_string($mysqli, $_POST['pass']);
$query = 'SELECT * FROM users WHERE userName="' . $userName . '"AND pass="' . $pass. '";';
$result = mysqli_query($mysqli, $query);
...
password
フィールドを空白のままにして、userName
に対して " OR 1=1;--
と入力すると、引用符はエスケープされず、クエリの結果は次のようになります。
SELECT * FROM users
WHERE userName = ""
OR 1=1;
-- "AND pass="";
OR 1=1
によって where 句は常に真 (true) の評価を行い、二重ハイフンによってステートメントの残りの部分はコメントとして扱われるため、このクエリは次のような単純なクエリと論理的に等しくなります。
SELECT * FROM users;
owner
が一致するアイテムだけが表示されます。
...
string userName = ctx.getAuthenticatedUserName();
string query = "SELECT * FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ ItemName.Text + "'";
IDataReader responseReader = new InlineQuery().ExecuteReader(query);
...
SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;
itemName
に単一引用符が含まれない場合のみクエリは正しく動作します。ユーザー名 wiley
を持つ攻撃者が文字列「name' OR 'a'='a
」を itemName
に入力すると、クエリは次のようになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
条件を追加すると、where 句は常に真 (true) の評価を行います。そのため、このクエリは次のような単純なクエリと論理的に等しくなります。
SELECT * FROM items;
items
テーブルに格納されているすべてのエントリを返すようになりました。Example 1
で構築および実行されたクエリに悪意のある別の値が渡された場合の影響を検討します。ユーザー名 wiley
を持つ攻撃者が文字列「name'); DELETE FROM items; --
」を itemName
に入力すると、クエリは次の 2 つのクエリになります。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
--'
Example 1
と同様の技法を使用すると一般的な攻撃を有効にできます。攻撃者が文字列「name'); DELETE FROM items; SELECT * FROM items WHERE 'a'='a
」を入力すると、次の 3 つの有効なステートメントが作成されます。
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
SELECT * FROM items WHERE 'a'='a';
cfgfile
から読み取ったデータを、strcpy()
を使用して inputbuf
にコピーしています。このコードは、inputbuf
が常に NULL ターミネータを含むと誤って想定しています。
#define MAXLEN 1024
...
char *pathbuf[MAXLEN];
...
read(cfgfile,inputbuf,MAXLEN); //does not null-terminate
strcpy(pathbuf,inputbuf); //requires null-terminated input
...
Example 1
のコードは、cfgfile
から読み取ったデータが想定どおり NULL ターミネートされている場合は正しく動作します。しかし、攻撃者がこの入力を変更して、想定されている null
文字を含まないようにできる場合、strcpy()
のコールは任意の null
文字に遭遇するまでメモリからのコピーを続けます。つまり、コピー先バッファがオーバーフローする確率が高くなります。また、攻撃者が inputbuf
の直後のメモリ内容を制御できる場合、アプリケーションはバッファ オーバーフロー攻撃にさらされた状態のままになる可能性があります。path
というバッファに保持されているシンボリック リンクの名前を readlink()
で展開し、シンボリック リンクが参照するファイルの絶対パスを buf
というバッファに格納しています。次に、結果として得られる値の長さが strlen()
を使用して計算されます。
...
char buf[MAXPATH];
...
readlink(path, buf, MAXPATH);
int length = strlen(buf);
...
Example 2
のコードは、readlink()
によって buf
に読み込まれた値が NULL ターミネートされないため、正しく動作しません。テストでは、このような脆弱性は見つかりません。buf
の使用されていない内容とその直後のメモリが null
になる可能性があり、strlen()
が正しく動作しているように見えるからです。しかし実環境では、strlen()
はスタック上で任意の null
文字に遭遇するまでメモリを検索し続けます。その結果 length
の値は buf
の値よりはるかに大きくなり、それ以降もこの値を使用し続けると Buffer Overflow を引き起こす可能性があります。snprintf()
を使用してユーザー入力文字列をコピーし、複数の出力文字列に配置します。sprintf()
に類似した追加のガードレール、特に最大出力サイズの指定を提供しているにもかかわらず、snprintf()
関数は、指定された出力サイズが想定される入力よりも大きい場合に、依然として文字列のターミネーション エラーの影響を受けやすくなります。文字列のターミネーション エラーは、メモリ リークやバッファ オーバーフローなどの下流の問題を引き起こす可能性があります。
...
char no_null_term[5] = getUserInput();
char output_1[20];
snprintf(output_1, 20, "%s", no_null_term);
char output_2[20];
snprintf(output_2, 20, "%s", no_null_term);
printf("%s\n", output_1);
printf("%s\n", output_2);
...
Example 3
のコードはメモリ リークを示します。output_2
に no_null_term
が設定されている場合、snprintf()
は null 文字が検出されるか指定されたサイズ制限に達するまで、no_null_term
の位置から読み取る必要があります。no_null_term
にはターミネーションがないため、snprintf
は output_1
のデータの読み取りを続け、最終的に snprintf()
の最初の呼び出しによって提供された null ターミネート文字に到達します。メモリ リークは、output_2
の printf()
によって示されます。これには、no_null_term
の文字シーケンスが 2 つ含まれています。null
文字でターミネートされたデータを含むメモリ領域として表現されています。多くの場合、古い文字列処理メソッドは文字列長の判断をこの null
文字に依存しています。NULL ターミネータを含んでいないバッファがそのような関数のいずれかに渡されると、その関数はバッファの末尾を越えて読み込みを続けます。ActionClass-validation.xml
に同じ名前のフィールド バリデーター定義が複数存在します。同じ名前の検証定義が重複していると、予期しない動作が発生する可能性があります。
<field name="emailField">
<field-validator type="email" short-circuit="true">
<message>You must enter a value for email.</message>
</field-validator>
<field-validator type="email" short-circuit="true">
<message>Not a valid email.</message>
</field-validator>
</field>
validators.xml
で発見されました。同じ名前の検証定義が複数あると、予期しない動作が発生する可能性があります。validators.xml
で定義する必要があります。バリデーター定義がない場合、検証が最新ではないことを意味します。validators.xml
で定義されていません。
<validators>
<validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
</validators>
<form-validation>
<formset>
<form name="ProjectForm">
...
</form>
<form name="ProjectForm">
...
</form>
</formset>
</form-validation>