SQLインジェクションとXSSの違い ― 仕組みと対策
SQL
SQLインジェクションとXSSの違いは、入力が実行される場所
SQLインジェクションとXSSの違いは、入力欄に入れた文字列を、誰が命令として実行してしまうかです。SQLインジェクションではサーバのデータベースが、XSS(クロスサイトスクリプティング)では利用者のブラウザが実行します。
ログイン画面や検索欄、掲示板のコメント欄のように、利用者の入力を受け取る画面ならどこでも起こりえます。どちらの攻撃も、データのつもりで受け取った入力が命令として読まれることで起きます。入力がSQL文に混ざればSQLインジェクション、Webページに混ざればXSSです。
実行する側が違うので、被害を受ける相手も変わります。SQLインジェクションで狙われるのは、データベースに保存された情報です。XSSで狙われるのは、そのページを見ている利用者です。
2つの違いを並べて比べる
Webアプリは、HTTPの要求で利用者から入力を受け取り、その入力を使ってSQL文やHTMLを作ります。2つの攻撃の違いは、この入力がどこに混ざるかです。
SQL文に混ざった入力は、データベースで実行されます。HTMLに混ざった入力は、ブラウザで実行されます。混ざる先が違うので、対策も違います。
| 比べる | SQLインジェクション | XSS |
|---|---|---|
| 入力が混ざる先 | SQL文(データベースへの命令文) | HTML(Webページの中身) |
| 実行する側 | データベース | 利用者のブラウザ |
| 被害を受けるもの | データベースに保存された情報 | ページを見ている利用者 |
| 主な被害 | 情報の閲覧、改ざん、消去、不正ログイン | 偽のページの表示、Cookieの取得 |
| 根本的な対策 | プレースホルダでSQL文を組み立てる | 出力するときにエスケープする |
| 追加の対策 | エラーメッセージを出さない、データベースの権限を絞る | HttpOnly属性、CSP |
ログイン画面で、対策の有無を比べる
次の図は、ログイン画面を例に、入力が処理されるまでを3段階で示します。題材と実装を切り替えると、同じ段階で、対策の有無による違いを比べられます。
ログイン画面のパスワード欄に、引用符と OR を含む文字列を入れて送ります。Webアプリは、この文字列をそのまま受け取ります。
Webアプリは、入力を文字列としてSQL文につなぎ合わせます。入力の先頭の引用符が、パスワードの文字列の終わりとして読まれます。その後ろの OR 以降は、パスワードではなく、SQL文の続きになります。
AND は OR より先に計算されます。前半はパスワードが合わず偽ですが、OR の後ろの「1 = 1」は常に真です。全体が真になるので、users表のすべての行が条件に合います。
入力は、対策なしの場合とまったく同じです。Webアプリは、この文字列をそのまま受け取ります。
Webアプリは、値を入れる場所を「?」にした雛形を作ります。入力は、SQL文につなぎ合わせず、値として別に渡します。この組み立て方をプレースホルダと呼びます。
データベースは、2つ目の値全体を、パスワードとして比べる1つの文字列として扱います。引用符や OR が入っていても、命令としては読まれません。パスワードが合う行はありません。
攻撃者は、この文字列をユーザー名に含めたリンクを用意します。利用者がそのリンクを開くと、この文字列がユーザー名としてWebアプリに届きます。
ユーザー名が存在しないので、Webアプリはログイン失敗の画面を作ります。このとき、ユーザー名をHTMLへそのまま埋め込みます。入力のタグが、ページの本物のタグと区別されません。
ブラウザは、サイトから届いたページの一部として、script要素を実行します。この例の alert は、画面に小さな窓を出すだけです。同じ仕組みで、ページの表示を書き換えたり、保護されていないCookieを読み取ったりするスクリプトも実行できます。
入力は、対策なしの場合とまったく同じです。この文字列が、ユーザー名としてWebアプリに届きます。
Webアプリは、埋め込む前に、「<」を「<」に、「>」を「>」に置き換えます。この置き換えをエスケープと呼びます。ページの本物のタグは、そのまま残ります。
ブラウザは、「<」を記号の「<」という文字として表示します。タグとしては読まないので、スクリプトは実行されません。
入力した文字列は、対策なしでは命令として読まれ、対策ありでは値や文字として扱われます。パスワードをそのまま照合する、説明用に単純にした例です。
SQLインジェクションの仕組み
Webアプリは、利用者の入力をもとにSQL文を組み立てます。SQL文とは、データベースへの命令文です。この組み立て方に問題があると、入力した文字列が命令の一部として読まれます。
原因は、入力を文字列としてSQL文につなぎ合わせる書き方です。入力に含まれる引用符が、文字列の終わりとして読まれると、その後ろが命令になります。前の図のステップ2がこの状態です。
情報処理推進機構(IPA)は、次のような被害を挙げています。非公開情報の閲覧、情報の改ざんや消去、認証を回避した不正ログインです。個人情報をデータベースに保存するサイトは、特に注意が必要です。
SQLインジェクションの対策:プレースホルダ
根本的な対策は、プレースホルダでSQL文を組み立てることです。SQL文の雛形には、値を入れる場所の記号だけを置きます。実際の値は、あとから機械的に割り当てます。
プレースホルダには、2つの方式があります。静的プレースホルダは、雛形をデータベースに先に渡し、値をデータベース側で割り当てます。動的プレースホルダは、アプリ側のライブラリが値をエスケープして、雛形にはめ込みます。
IPAは、どちらでも脆弱性を解消できると述べています。そのうえで、原理的に脆弱性の可能性がなくなる静的プレースホルダのほうが優れていると説明しています。静的プレースホルダは、SQLの規格では準備された文と呼ばれます。
テーブル名や並び順のように、プレースホルダにできない部分もあります。Webの安全性に取り組む団体OWASPは、この部分を許可した候補から選ばせるよう勧めています。入力は、そのまま使いません。入力の引用符を自分で置き換える方法は、データベースごとに違い、漏れやすくなります。そのため、OWASPは使わないよう強く勧めています。
保険として、被害を小さくする対策もあります。エラーメッセージをブラウザに表示しないこと、データベースのアカウントに必要最小限の権限だけを与えることです。この2つは、プレースホルダの代わりにはなりません。
XSSの仕組み
Webアプリの中には、利用者の入力をWebページに出力するものがあります。例は、検索語の表示や入力内容の確認画面、掲示板です。出力の仕方に問題があると、入力した文字列が、スクリプトとしてページに埋め込まれます。スクリプトとは、ブラウザの中で動くプログラムです。
ブラウザは、埋め込まれたスクリプトを、サイトから届いたページの一部として実行します。IPAの説明のとおり、影響はサイトではなく、ページを見ている利用者に及びます。被害には、本物のサイト上に偽のページが表示されることや、ブラウザに保存されたCookieを取得されることがあります。Cookieにセッションの情報が入っていると、利用者へのなりすましにつながります。Cookieとセッションの関係は、Cookieの記事にあります。
入力が届く経路で、XSSは大きく2種類に分けられます。反射型は、罠のリンクなどで送られた入力を、エラー表示や検索結果にそのまま映し返すものです。格納型は、掲示板のコメント欄のように、入力がサーバに保存されるものです。保存された入力は、そのページを開いた人に表示されます。前の図で扱ったのは、反射型です。
ほかに、ブラウザ内のJavaScriptが入力をページへ書き込むときに起きるDOMベースもあります。次の節からの対策は、サーバが入力を出力する場合に絞ります。スクリプトを動かさずに、ログイン中のブラウザに要求だけを送らせる攻撃はCSRFと呼びます。これは、XSSとは別の攻撃です。
XSSの対策:エスケープ
根本的な対策は、ページに出力する前に、特別な意味を持つ記号を別の表記に置き換えるエスケープです。置き換えた表記は、ブラウザが記号の文字として表示し、タグとしては読みません。
| 置き換える | 置き換えた |
|---|---|
| & | & |
| < | < |
| > | > |
| " | " |
これらの表記は、HTMLの本文に出力するときのものです。タグの属性値に出力するときは、属性値をダブルクォートで囲みます。記号を置き換える範囲は、出力する場所で変わります。
リンク先のURLを出力するときは、httpまたはhttpsで始まるものだけを許可します。「javascript:」で始まるURLからは、スクリプトが動く場合があるためです。
多くのWebフレームワークは、テンプレートに値を出力するときに、自動でエスケープします。ただし、エスケープを無効にする機能を使うと、この保護は外れます。
掲示板のように、HTMLの入力を許したいときは、エスケープするとタグまで文字になります。この場合は、危険な部分だけを取り除くサニタイズを使います。OWASPは、DOMPurifyのようなライブラリを勧めています。危険な文字列を自分で探して取り除く方法は、見落としやすく、IPAも単独では勧めていません。
XSSの追加の対策:HttpOnlyとCSP
エスケープを漏らした場合に備えて、被害を小さくする対策があります。
1つ目は、Cookieに付けるHttpOnly属性です。この属性を付けたCookieは、ページ内のスクリプトから読めません。スクリプトが実行されても、そのCookieは盗まれません。ただし、Cookie以外の被害は残ります。
2つ目は、CSP(Content Security Policy)です。サーバがHTTPレスポンスヘッダで、読み込んでよいスクリプトの出どころをブラウザに伝えます。許可していない出どころのスクリプトは、読み込まれません。ページの中に直接書かれたスクリプトも、許可しない設定にできます。
OWASPは、CSPを主な防御にせず、追加の層として使うよう勧めています。設定を誤りやすく、許可の仕方によっては、対策にならないためです。
WAFでどこまで防げるか
WAFは、Webアプリへの通信の中身を検査し、攻撃のパターンを遮断するファイアウォールです。SQLインジェクションやXSSの通信を、アプリの手前で止められます。仕組みはファイアウォールの記事で説明しています。誤検知や置き場所は、WAFの記事を読むと分かります。
ただし、WAFが見つけられるのは、既知の攻撃のパターンです。OWASPによると、新しい回避方法が定期的に見つかるので、WAFは当てになりません。脆弱性の根本原因にも対処しないと述べています。Webに限らず通信の中の攻撃を探す仕組みとの違いは、IDSとIPSの違いの記事で比べています。
そのため、WAFはプレースホルダやエスケープの代わりにはなりません。アプリ側の対策に加える層として使います。リバースプロキシと組み合わせる形は、プロキシの記事にあります。クラウドのWAFの例は、AWS WAFとShieldの記事にあります。
練習問題(全7問)
正解:SQLインジェクション
入力が
正解:利用者の
XSSでは、
正解:プレースホルダで
プレースホルダは、
正解:<
「<」は
正解:Cookieを
HttpOnly属性を
正解:WAFは
WAFは、
正解:httpまたは
URLは、
選んでから「採点する」を押すと、解説つきで答え合わせができます。
参考にした資料
- IPA 安全なウェブサイトの作り方(SQLインジェクションとXSSの仕組み、プレースホルダとエスケープ、HttpOnly)
- OWASP SQL Injection Prevention Cheat Sheet(プレースホルダと、そのほかの防御)
- OWASP Cross Site Scripting Prevention Cheat Sheet(出力のエンコード、サニタイズ、CSP、WAFの位置づけ)
- OWASP Cross Site Scripting(反射型・格納型・DOMベース)
- RFC 6265 HTTP State Management Mechanism(Cookieの HttpOnly 属性)
- W3C Content Security Policy Level 3(CSPの仕様)
関連記事
おすすめの本 PR
セキュリティとデータベースは、基本情報技術者試験の対策本でまとめて学べます。

