Back

SQL インジェクション入門ガイド(および防止方法)

SQL インジェクション入門ガイド(および防止方法)

アプリケーションに検索機能を実装しているとします。ユーザーが商品名を入力し、バックエンドがデータベースにクエリを実行して、結果が表示されます。シンプルな仕組みです――しかし、誰かがその検索ボックスに '; DROP TABLE products;-- と入力するまでは。

これがSQLインジェクションであり、Web開発における最も危険な脆弱性の一つです。OWASP Top 10では「インジェクション」が重大なセキュリティリスクとして挙げられており、SQLインジェクションはそのカテゴリの中核を占めています。データベースに触れるコードを書く場合――たとえ時々であっても――この攻撃がどのように機能し、どう防ぐかを理解する必要があります。

重要なポイント

  • SQLインジェクションは、攻撃者がユーザー入力を通じて悪意のあるコードを挿入し、データベースクエリを操作することで発生します
  • パラメータ化クエリ(プリペアドステートメント)が主要な防御手段であり、SQLコードとデータを分離します
  • ORMは生のクエリメソッドを使用する場合、自動的に安全とは限りません
  • テーブル名やカラム名などの動的な識別子にはホワイトリストを使用します。これらはパラメータ化できません
  • 多層防御を適用します:最小権限アカウント、集中化されたデータアクセス層、CIパイプラインでのセキュリティテスト

SQLインジェクションとは?

SQLインジェクションは、攻撃者がユーザー入力を通じて悪意のあるコードを挿入することで、アプリケーションのデータベースクエリを操作する攻撃です。入力をデータとして扱う代わりに、データベースがそれをコマンドとして実行してしまいます。

ログインフォームを考えてみましょう。バックエンドは次のようなクエリを構築するかもしれません:

SELECT * FROM users WHERE email = 'user@example.com' AND password = 'secret123'

ユーザー入力と文字列を連結してこのクエリを構築すると、攻撃者はパスワードとして ' OR '1'='1 を入力できます。結果のクエリは次のようになります:

SELECT * FROM users WHERE email = 'user@example.com' AND password = '' OR '1'='1'

'1'='1' は常に真であるため、クエリはすべてのユーザーを返し、認証を完全にバイパスします。

なぜSQLインジェクションが今でも重要なのか

最近のフレームワークがこの問題を解決していると思うかもしれません。しかし、少なくとも自動的には解決されていません。

SQLインジェクションの脆弱性は以下の場所に現れます:

  • フィルタパラメータを含むJSONを受け入れるAPIエンドポイント
  • 複数のクエリオプションを持つ検索フォーム
  • 動的なソートやフィルタリング機能を持つ管理ダッシュボード
  • ユーザー定義の条件を持つレポートビルダー

ユーザー入力がデータベースクエリに影響を与える場所はすべて、潜在的な攻撃対象となります。その結果は、データ盗難から完全なデータベース破壊まで多岐にわたります。

安全でないクエリ構築が発生する仕組み

根本原因は常に同じです:ユーザー入力をデータとしてではなく、信頼できるSQLコードとして扱うことです。

文字列連結が主な原因です:

// 脆弱 - 絶対にこれをしないでください
const query = `SELECT * FROM products WHERE name = '${userInput}'`;

テンプレートリテラルと文字列フォーマットも同じ問題を引き起こします:

# 脆弱 - 絶対にこれをしないでください
query = f"SELECT * FROM products WHERE category = '{category}'"

ORMも自動的に安全とは限りません、生のクエリメソッドを使用する場合:

// 脆弱 - 生のクエリはORMの保護をバイパスします
db.query(`SELECT * FROM users WHERE id = ${req.params.id}`);

パラメータ化クエリによるSQLインジェクション防止

SQLインジェクションに対する主要な防御手段は、パラメータ化クエリ(別名プリペアドステートメント)の使用です。これらはSQLコードとデータを分離し、ユーザー入力が決してコマンドとして実行されないことを保証します。

JavaScriptでの安全なアプローチは次のとおりです:

// 安全 - パラメータ化クエリ
const query = 'SELECT * FROM products WHERE name = ?';
db.query(query, [userInput]);

Pythonでは:

# 安全 - パラメータ化クエリ
cursor.execute("SELECT * FROM products WHERE name = %s", (user_input,))

データベースはパラメータをSQLコードではなく、リテラル値として扱います。たとえ誰かが '; DROP TABLE products;-- を入力しても、データベースはその正確な文字列を名前として持つ商品を検索するだけです。

パラメータ化クエリがカバーしないもの

識別子(テーブル名、カラム名)はパラメータ化できません。動的なソートが必要な場合:

// カラム名にはホワイトリストを使用
const allowedColumns = ['name', 'price', 'created_at'];
const sortColumn = allowedColumns.includes(userInput) ? userInput : 'name';
const query = `SELECT * FROM products ORDER BY ${sortColumn}`;

ストアドプロシージャは、内部で動的SQLを避けている場合にのみ安全です。文字列を連結するストアドプロシージャは同様に脆弱です。

エスケープだけでは不十分な理由

手動の文字列エスケープとサニタイゼーションだけでは不十分です。エラーが発生しやすく、データベース固有であり、正しく実装するのが容易ではありません。パラメータ化クエリは自動的かつ正確にエスケープを処理します。

多層防御

パラメータ化クエリが主要な防御手段ですが、追加の層も役立ちます:

  • 最小権限データベースアカウント:アプリケーションのデータベースユーザーは、本番環境でDROP、ALTER、CREATE USERなどの管理者権限を持つべきではありません
  • 集中化されたデータアクセス層:すべてのクエリをパラメータ化を強制する単一のモジュールを通してルーティングします
  • コードレビューとテスト:レビュープロセスとCIパイプラインにSQLインジェクションチェックを含めます

OWASP ASVSやCISAの「Secure by Design」イニシアチブからの最新のガイダンスは、後から追加するのではなく、開発プロセスにセキュリティを組み込むことを強調しています。

まとめ

SQLインジェクションが危険であり続けるのは、導入が容易で、悪用された場合に壊滅的な影響を与えるためです。修正は簡単です:すべてのデータベース操作にパラメータ化クエリを使用し、識別子をホワイトリストで検証し、ユーザー入力を決して信頼しないことです。

パラメータ化クエリをデフォルトにしましょう。将来のあなた自身――そしてユーザー――が感謝するでしょう。

よくある質問

はい。NoSQLインジェクションは、MongoDBのようなデータベースに影響を与える関連する脆弱性です。攻撃者はユーザー入力を通じてクエリ演算子を操作できます。防御手段は似ています:文字列からクエリを構築するのではなく、データベースドライバの組み込みクエリメソッドを使用し、クエリで使用する前にすべてのユーザー入力を検証します。

ORMは標準のクエリメソッドを使用する場合に保護を提供します。ただし、ほとんどのORMはこれらの保護をバイパスする生のクエリ関数を提供しています。生のSQLメソッドを使用したり、ORMクエリ内で文字列補間を使用したりすると、依然として脆弱です。ORMコード内でも常にパラメータ化メソッドを使用してください。

SQLMapやOWASP ZAPのような自動化ツールを使用して脆弱性をスキャンします。インジェクションペイロードを試みるセキュリティ重視のユニットテストを含めます。クエリでの文字列連結を特にチェックするコードレビューを実施します。本番環境へのデプロイ前に、重要なアプリケーションに対してペネトレーションテストを検討してください。

いいえ。入力検証は攻撃対象領域を減らすのに役立ちますが、唯一の防御手段であってはなりません。巧妙な攻撃者は検証ルールをバイパスできることがよくあります。パラメータ化クエリは、コードとデータを根本的に分離するため、主要な防御手段です。検証は追加の層として使用し、適切なクエリ構築の代替としないでください。

Gain control over your UX

See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay