モダンWebアプリのSQLインジェクション対策
SQLインジェクションは、依然として最も深刻なWebアプリケーションの脆弱性の一つです。攻撃者はこれを悪用してデータを窃取し、認証を回避し、さらにはシステムコマンドを実行します。もしあなたのアプリケーションがユーザー入力を連結してSQLクエリを構築しているなら、リスクにさらされています。この記事では、SQLインジェクションの仕組みを説明し、モダンWebアプリケーションでそれを防ぐための実践的な手順を紹介します。
SQLインジェクションはどのように発生するか
SQLインジェクションは、信頼できないデータがSQLコマンドの一部として解釈されるときに発生します。例えば、次のクエリで認証情報を確認するログインフォームを考えてみましょう:
SELECT * FROM users WHERE username = '$username' AND password = '$password';
攻撃者がユーザー名として ' OR '1'='1 を入力し、任意のパスワードを入力すると、クエリは次のようになります:
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = 'anything';
これはすべてのユーザーを返し、認証を回避します。同様の手法でデータを抽出したり、レコードを変更したり、テーブルを削除したりできます。
1. プリペアドステートメント(パラメータ化クエリ)を使用する
プリペアドステートメントはSQLコードとデータを分離します。データベースはまずクエリ構造を受け取り、次にパラメータを受け取るため、ユーザー入力がSQLコードとして扱われることはありません。これが最も効果的な防御策です。
PHPとPDOの例:
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute(['email' => $userInput]);
$user = $stmt->fetch();
JavaとJDBCの例:
String sql = "SELECT * FROM users WHERE email = ?";
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setString(1, userInput);
ResultSet rs = stmt.executeQuery();
検索フィールド、フィルター、並べ替えパラメータを含むすべてのユーザー提供データに対して、常にパラメータ化クエリを使用してください。
2. 入力を検証してサニタイズする
プリペアドステートメントが主要な対策ですが、入力検証は多層防御を強化します。入力が期待されるパターン(例:メール形式、数値ID)に一致することを検証し、予期しないものは拒否します。例えば、IDが整数であるべきなら、コード内でintにキャストします。引用符などの文字をブラックリスト方式で除外することは避けてください。攻撃者はそのようなフィルターを回避できます。
3. ORMを安全に使用する
Hibernate、Entity Framework、SequelizeなどのORMは、通常デフォルトでパラメータ化クエリを使用します。しかし、生のSQL断片を許可することがよくあります。生の文字列を受け入れるメソッドには注意してください:
- Sequelizeでは、
sequelize.query('SELECT * FROM users WHERE name = \'' + name + '\'')を避けてください。代わりに置換またはバインドパラメータを使用します。 - Hibernateでは、文字列連結ではなくHQLの名前付きパラメータを使用します。
安全なクエリ構築については、常にORMのドキュメントを確認してください。
4. プリペアドステートメントが不可能な場合はデータをエスケープする
動的SQL(例:動的テーブル名)を構築しなければならない稀なケースでは、データベースドライバのエスケープ関数を使用します。MySQLでは、mysqli_real_escape_string() が特殊文字をエスケープします。ただし、エスケープはプリペアドステートメントほど堅牢ではなく、最後の手段とすべきことを覚えておいてください。
5. データベースアカウントに最小権限を適用する
rootや完全な権限を持つユーザーでデータベースに接続しないでください。アプリケーション専用のデータベースユーザーを作成し、必要な権限(特定のテーブルに対するSELECT、INSERT、UPDATE、DELETE)のみを付与します。これにより、インジェクションが発生した場合の被害を限定できます。
6. Webアプリケーションファイアウォール(WAF)を使用する
WAFは一般的なSQLインジェクションパターンを検出してブロックできます。安全なコーディングの代替にはなりませんが、追加の層を提供します。多くのクラウドプロバイダーは、簡単に有効化できるマネージドWAFを提供しています。
7. 定期的なセキュリティテスト
自動スキャナーや手動ペネトレーションテストを使用して、アプリケーションのSQLインジェクション脆弱性をテストします。SQLMapなどのツールが問題の特定に役立ちます。セキュリティテストをCI/CDパイプラインに統合して、リグレッションを早期に発見しましょう。
防止テクニックの比較
| テクニック | 効果 | 実装の容易さ |
|---|---|---|
| プリペアドステートメント | 高 | 容易(ほとんどのドライバに組み込み) |
| 入力検証 | 中 | 普通 |
| ORMの安全な使用 | 高 | 意識していれば容易 |
| エスケープ | 中 | 容易だがエラーを起こしやすい |
| 最小権限 | 中 | 容易 |
| WAF | 低~中 | 容易(マネージド) |
FAQ
SQLインジェクションを防ぐ最も効果的な方法は何ですか?
パラメータ化クエリを伴うプリペアドステートメントを使用することが最も効果的な方法です。これにより、ユーザー入力がSQLコードとして解釈されることはありません。
入力検証だけでSQLインジェクションを防げますか?
いいえ。入力検証は優れた多層防御策ですが、それだけに頼るべきではありません。攻撃者は時々検証ルールを回避できるため、常にプリペアドステートメントを主要な防御として使用してください。
ORMは自動的にSQLインジェクションから安全ですか?
ORMは正しく使用すれば一般的に安全ですが、ユーザー入力を連結すると脆弱になる生のSQLクエリを許可することがよくあります。ORMメソッド内であっても、常にパラメータ化クエリを使用してください。
さらにセキュリティを強化するには、JSON formatterを使用して、悪意のあるコードを実行せずにAPIレスポンスを安全に検査および検証することを検討してください。