CXX.SQL.INJECT

SQL コマンドで使用される特殊要素の不適切な中立化による SQL インジェクション

このチェッカーは、SQL インジェクション脆弱性にフラグを立てます。SQL ステートメントが未検証の入力を使用して作成されている場合、攻撃アプリケーションによって使用されたデータベース上でこのアプリケーションの特権を使用して、悪意のあるユーザーは文字列を挿入して任意の SQL ステートメントを実行することができます。

脆弱性とリスク

コードへの入力が適切に検証されないと、攻撃者は、任意の SQL ステートメントを実行可能な形式で入力を作成することができます。この種のチャンスがある場合、攻撃者は次のことを行えます。

  • アプリケーションのデータベース内の機密データを読み取る
  • アプリケーションのデータベース内のデータを書き換える
  • 任意のコマンド (データベース内のすべての情報の削除など) を実行する

軽減と防止

この指摘を回避するには、次のことを実行するのがベストです。

  • SQL ステートメントを作成する場合、定数文字列のみを使用する
  • 入力を使用するパラメーター化された SQL ステートメントを作成する場合、安全なライブラリを使用する
  • SQL ステートメントで入力を使用する前に検証コードを追加する

脆弱コード例 1

以下は、SQLite C/C++ API の使用による SQL インジェクション脆弱性が含まれた例です。

コピー
  void cwe_89_example_2(sqlite3 *database)
  {
      const char *userName;
      const char *itemName;
      char *sql;
      gets(userName);
      gets(itemName);
      strcat(sql, "SELECT * FROM items WHERE owner = '");
      strcat(sql, userName);
     strcat(sql, "' AND itemname = '");
     strcat(sql, itemName);
     strcat(sql, "'");
     char *errMsg = 0;
     sqlite3_exec(database, sql, NULL, 0, &errMsg);
 }

Klocwork は、14 行目について、安全でない SQL クエリ文字列がコマンドとして使用されていることを示す、SQL インジェクションレポートを生成します。このケースの SQL インジェクションは、`itemName` に対して `“name'; DELETE FROM items; --”` という入力を使用すると発生する可能性があります。これは、SQL クエリ内の予約文字 (`itemName` という文字列の一重引用符など) が適切にエスケープされないことが原因です。このケースでは、ユーザー名が `'wiley'` の場合、この SQL 文字列が解析され、以下の 3 つのステートメントとして実行されます。

SELECT * FROM items WHERE owner = 'wiley' AND itemname = 'name';
DELETE FROM items;
--'

`DELETE` ステートメントの実行では、データベース内の `'items'` テーブルからすべてのエントリが削除されます。

修正コード例 1

未検証のユーザー入力を使用し、予約文字をエスケープせずに SQL クエリを作成するのは危険です。このため、SQLite C/C ++ API の他の関数を使用して、SQL クエリからプリペアドステートメントオブジェクトを作成し、予約文字が適切に処理される SQL クエリのパラメーターにユーザー入力をバインドする必要があります。たとえば、`sqlite3_prepare_v2` を使用してプリペアドステートメントオブジェクトを作成し、`sqlite3_bind*` 関数を使用してユーザー入力からのクエリのパラメーターをバインドできます。この方法を使用すると、`"DELETE from items"` コマンドの作成と実行を妨げる、予約された SQL クエリ文字 (一重引用符など) が適切にエスケープされます。これは次のように行うことができます。

コピー
  void cwe_89_example_2_fixed(sqlite3 *database)
  {
      const char *userName;
      const char *itemName;
      const char *sql = "SELECT * FROM items WHERE owner = (?)AND itemname = (?)";
      gets(userName);
      gets(itemName);
      sqlite3_stmt *statement;
      if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) != SQLITE_OK) {
         printf("sqlite3_prepare_v2 failure: %s", sqlite3_errmsg(database));
         return;
     }
     if (sqlite3_bind_text(statement, 1, userName, -1, SQLITE_TRANSIENT) != SQLITE_OK) {
         printf("sqlite_3_bind_text unable to bind argument 1: %s", sqlite3_errmsg(database));
         sqlite3_finalize(statement);
         return;
     }
     if (sqlite3_bind_text(statement, 2, itemName, -1, SQLITE_TRANSIENT) != SQLITE_OK) {
         printf("sqlite_3_bind_text unable to bind argument 2: %s", sqlite3_errmsg(database));
         sqlite3_finalize(statement);
         return;
     }
     if (sqlite3_step(statement) != SQLITE_DONE) {
         printf("sqlite_3_step failure: %s", sqlite3_errmsg(database));
     }
     sqlite3_finalize(statement);
 }

セキュリティトレーニング

Secure Code Warrior が提供しているアプリケーションセキュリティトレーニング教材。

拡張機能

このチェッカーは、Klocwork knowledge base (ナレッジベース) を利用して拡張できます。詳細については、C/C++ knowledge base (ナレッジベース) リファレンス を参照してください。