SQLインジェクションは古くから知られながら、いまも情報漏洩の主要な原因であり続けている脆弱性です。 対策はほぼ確立されていて、プレースホルダ(パラメータ化クエリ)を使えているかどうかでほぼ決まります。
SQLインジェクションとは
SQLインジェクション:ユーザー入力を使ってSQL文を組み立てる箇所に攻撃者が細工した文字列を送り込み、開発者が意図しないSQLを実行させる脆弱性です。 文字列連結でSQLを組む設計が根本原因になります。
攻撃が成立すると、' OR '1'='1 のような入力による認証回避、テーブル全体を対象にした情報漏洩、UPDATEやDELETEを混ぜ込んだデータの改ざんや削除といった被害につながります。
データベースには個人情報や認証情報が集まるため、1箇所の穴が全社的な漏洩に直結します。
UNIONで別テーブルの内容を結果に混ぜ込む、条件の真偽で1文字ずつ抽出するなど手口は幅広く、攻撃を自動化するツールも普及しています。
SQLインジェクションが起きるパターン
典型的な混入箇所は次のとおりです。
- ユーザー入力を文字列連結やテンプレート展開でSQLに埋め込んでいる(
"SELECT * FROM users WHERE name = '" + input + "'") - ORMを使っていても、生SQLを渡すAPI(Railsの
where("... = #{params}")、find_by_sqlなど)に入力を直接差し込んでいる - ORDER BYのカラム名やLIMITの件数など、プレースホルダを使えない箇所に入力をそのまま渡している
- 画面にエラーや結果が出ないブラインドSQLiで、応答時間や真偽の差分から1文字ずつ情報を抜かれる
- 入力を一度エスケープしたつもりでも、多重デコードや文字コードの扱いで無効化されている
共通するのは「入力がデータではなくSQLの構文として解釈される経路がひとつでもあれば成立する」という点です。
対策法
原則は「入力をSQLの文字列に混ぜず、プレースホルダでデータとして渡す」ことです。
- プレースホルダ(パラメータ化クエリ、バインド機構)を使い、値とSQL構文を分離する。これが本命の対策になる
- ORMのクエリビルダを標準経路として使い、生SQLを渡すAPIは使用箇所を洗い出してレビューで管理する
- カラム名や並び順などプレースホルダを使えない箇所は、入力を許可リストの固定値に写像してから使う
- アプリが使うDBユーザーの権限を最小化し、不要なテーブルへのアクセスや管理者権限を与えない(被害軽減策)
- 詳細なSQLエラーを利用者に返さず、攻撃者にスキーマの手がかりを与えない
入力値検証は補助であり、本命はプレースホルダによる構文とデータの分離です。 既存コードでは生SQLと文字列連結の箇所を先に洗い出し、そこから順に潰すのが費用対効果の高い進め方になります。
Rails での事例
Active Record を普通に使っているかぎり、値は自動でエスケープされSQLインジェクションは起きません。 穴が開くのは、条件を文字列で組み立て、そこに入力を式展開したときです。
# 危険:params[:email] が SQL の構文として解釈される
User.where("email = '#{params[:email]}'")
' OR '1'='1 を渡されれば条件が常に真になり、'; DROP ... の類も通ります。
直し方は、プレースホルダに値を渡すことです。
# ? のプレースホルダ、または名前付きプレースホルダ
User.where("email = ?", params[:email])
User.where(email: params[:email]) # ハッシュ条件が最も安全で読みやすい
見落とされやすいのが、プレースホルダを使えない箇所です。
order に渡すカラム名や並び順は値ではないため、? で縛れません。
ここに入力を直接渡すと、カラム名経由のインジェクションになります。
# 危険:ソートキーをそのまま order に渡している
Post.order(params[:sort])
カラム名やソート方向は、許可リストの固定値へ写像してから使います。
SORT_COLUMNS = { "new" => "created_at", "title" => "title" }.freeze
column = SORT_COLUMNS.fetch(params[:sort], "created_at")
Post.order(column => :asc)
find_by_sql や execute で生SQLを書く箇所も同じ原則です。
文字列連結をやめ、バインド変数(sanitize_sql_array や ?)を通します。
まとめ
SQLインジェクション対策の要点を整理します。
- 成立条件は「入力がデータではなくSQLの構文として解釈される経路がひとつでもあること」。文字列連結でSQLを組む設計が根本原因になる
- 本命の対策はプレースホルダ(パラメータ化クエリ)による構文とデータの分離。入力値検証は補助にとどまる
- ORMを使っていても、生SQLを渡すAPIに入力を差し込めば同じ穴が開く。クエリビルダを標準経路にし、生SQLの使用箇所はレビューで管理する
- ORDER BYのカラム名などプレースホルダを使えない箇所は、許可リストの固定値に写像してから使う
- DBユーザーの権限最小化と詳細エラーの抑制で、万一のときの被害を小さくする
既存コードの点検は、文字列連結と生SQL系APIの使用箇所を洗い出すところから始めると、少ない工数で影響の大きい穴から順に塞げます。
関連して読める記事
XSSとCSRFも、SQLインジェクションと並ぶWebアプリケーションの定番の脆弱性です。自社コードの対策とあわせて、依存パッケージ経由で侵入されるサプライチェーン攻撃への備えも整理しておくと、Webアプリのセキュリティを一段広い視野で見直せます。

XSS(クロスサイトスクリプティング)とは?起きるパターンと対策法
XSS(クロスサイトスクリプティング)の仕組みと3つの型、混入しやすいパターン、そして出力時のエスケープを軸にした対策を整理します。React(TypeScript)の dangerouslySetInnerHTML と Rails(ERB)の raw / html_safe を題材に、危険なコードと安全な書き方を具体的なコード例で示します。
2026年7月22日
CSRF(クロスサイトリクエストフォージェリ)とは?起きるパターンと対策法
CSRF(クロスサイトリクエストフォージェリ)の仕組みと起きるパターン、CSRFトークンとSameSite Cookieを軸にした対策を整理します。Railsで検証を外してしまう skip_before_action の罠と、React(TypeScript)からのトークン送信ヘッダの組み込み方を、危険なコードと安全な書き方の具体例で示します。
2026年7月23日
CTOが今期決めるべきサプライチェーン攻撃対策——発見・抑制・ダメージコントロールの3層設計
npmのサプライチェーン攻撃は2025年以降、毎月の定常イベントになりました。悪意あるコードはCVEが付く前に正規の新バージョンとして配布されるため、脆弱性スキャンやDependabotでは防げません。抑制・発見・ダメージコントロールの3層の防衛線を、ツール購買ではなく開発標準の設計問題として、CTO・開発責任者向けに解説します。
2026年7月19日個別の脆弱性を横断して全体を見直したいときは、この記事を含む31の攻撃と脆弱性を設計・開発・運用のどの層で防ぐかという視点で整理した完全ガイドをどうぞ。

現代Webセキュリティ完全ガイド|31の攻撃と脆弱性を設計、開発、運用で防ぐ
XSS・SQLインジェクション・IDOR・サプライチェーン攻撃など31の攻撃と脆弱性を、ブラウザ・入力・認証・認可・状態・運用の六つの攻撃面に整理し、「信頼境界の設計」として捉え直す総論ガイドです。個別脆弱性の暗記ではなく、設計・開発・運用の各工程に防御を組み込む方法、事業リスクにもとづく優先順位づけ、自社の現在地を確認する15項目までを経営層・開発責任者向けにまとめます。
2026年7月29日