top of page

Vol.09 Webアプリ開発者向け!SQLインジェクション・XSSの防ぎ方

  • 執筆者の写真: atsu wada
    atsu wada
  • 4月7日
  • 読了時間: 16分

更新日:4月25日


この記事であなたがやるべきこと


後述する「SQLインジェクションとXSSの5つの対策パターン」を理解して、

自分が開発しているWebアプリケーションのコードを見直し、

脆弱性のないセキュアなアプリケーションを実装できるようになること




はじめに




「セキュリティ対策は分かっているけど、実装に追われてつい後回しに…」


Webアプリケーション開発者であれば、こんな経験はありませんか?機能の実装やデザインの調整に集中するあまり、セキュリティ対策がおろそかになりがちです。しかし、セキュリティホールを放置したままリリースすると、ユーザーの個人情報漏洩やシステム破壊など、取り返しのつかない事態を招くことがあります。


特にSQLインジェクションとクロスサイトスクリプティング(XSS)は、OWASPが公表する「Webアプリケーションの脆弱性トップ10」に長年ランクインし続けている代表的な脆弱性です。これらは比較的シンプルな対策で防げるにもかかわらず、多くのWebサイトで今でも発見され続けています。


この記事では、SQLインジェクションとXSSの仕組みをわかりやすく解説し、実際のコード例を交えながら効果的な対策方法を紹介します。明日からすぐに実践できる知識を身につけて、より安全なWebアプリケーション開発を目指しましょう。




SQLインジェクションとXSSの基本を理解しよう


まずは、これらの脆弱性がどのようなものなのか、基本的な仕組みを理解しましょう。



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

SQLインジェクションは、Webアプリケーションの入力フォームやURLパラメータなどから、悪意のあるSQLコードを注入(インジェクト)することで、データベースを不正に操作する攻撃手法です。


例えば、以下のようなログイン処理のSQLがあるとします:


ここで、ユーザー名の入力欄に ' OR '1'='1 と入力すると、SQLは以下のように変形します:


'1'='1' は常に真となるため、パスワードが正しくなくても認証をバイパスできてしまいます。さらに深刻な場合、データベースの内容を改ざんしたり、削除したりすることも可能です。



2. クロスサイトスクリプティング(XSS)とは?

XSSは、Webページに悪意のあるスクリプトを埋め込み、そのページを閲覧したユーザーのブラウザ上でスクリプトを実行させる攻撃手法です。主に以下の3種類があります。


  • 反射型XSS: URLパラメータなどの入力値がそのままHTMLに反映される脆弱性

  • 格納型XSS: 掲示板やコメント機能などで、入力された悪意あるスクリプトがサーバーに保存され、他のユーザーがページを閲覧した際に実行される脆弱性

  • DOM-basedXSS: JavaScriptにより動的に生成されるHTMLに起因する脆弱性


例えば、コメント欄に以下のようなコードを投稿されると:


このコメントを閲覧したユーザーのCookieが攻撃者に送信され、セッション情報が盗まれる可能性があります。



実際に起きた被害事例

  1. 大手ECサイトでのSQLインジェクション被害 ある大手ECサイトで、検索機能を悪用したSQLインジェクションにより、約10万件の顧客情報(氏名、住所、電話番号、クレジットカード情報の一部)が流出する事件が発生しました。原因は、ユーザーの入力値を適切にサニタイズせずにSQL文に直接組み込んでいたことでした。この事件後、同社は顧客への賠償金支払いや信頼回復のための対応に数億円の費用を要し、株価も大幅に下落しました。

  2. SNSサイトでの格納型XSS被害 あるSNSサイトでは、プロフィール欄に任意のHTMLが入力できる仕様になっていました。攻撃者はこの機能を悪用して悪意のあるJavaScriptを埋め込み、そのプロフィールページを閲覧した約5万人のユーザーアカウントが乗っ取られる事件が発生しました。攻撃者はユーザーのログイン状態を利用して、プロフィール変更や友人へのメッセージ送信などの操作を自動的に行いました。結果として、マルウェアを拡散させる大規模な攻撃へと発展しました。




SQLインジェクションとXSSの5つの対策パターン




1. パラメータ化クエリ(プリペアドステートメント)の使用

何をすべきか:

  • SQL文と入力値を分離して処理する

  • 言語やフレームワーク標準のパラメータ化クエリ機能を使用する

実装例(PHP + PDO):

実装例(Node.js + MySQL2):

注意点: MySQLのmysqlパッケージは非推奨となっています。mysql2などの代替パッケージを使用し、適切なプレースホルダー形式(?または名前付きプレースホルダー)を使い分けましょう。各データベースドライバによってプレースホルダーの形式が異なる場合があるので、使用するライブラリのドキュメントを必ず確認してください。



2. ORM(オブジェクト関係マッピング)の活用

何をすべきか:

  • 生のSQLを直接書かず、ORMを使用する

  • 多くのフレームワークに標準搭載されているORMを活用する

  • ORMのバージョンを常に最新に保つ

実装例(Laravel)

実装例(Django):

ORMを使用する際の注意点:

  • ORMはSQLインジェクション対策として効果的ですが、過信は禁物です

  • ORMが提供する「生SQL実行機能」(whereRaw、extra、where文字列など)を使用する場合は、必ずパラメータバインディングを行う

  • ORMのバージョンを最新に保ち、セキュリティアップデートを適用する

  • 複雑なクエリでORMの機能だけでは不十分な場合は、パラメータ化クエリを使用する。



3. コンテキスト依存のエスケープ処理

何をすべきか:

  • ユーザー入力の出力先コンテキストに合わせて適切なエスケープ処理を選択する

  • HTML、URL、JavaScript、CSSなど、異なるコンテキストには異なるエスケープ処理が必要

  • テンプレートエンジンのエスケープ機能を活用する

URL内のエスケープ(JavaScript):

JavaScript内のエスケープ(PHP):

フレームワークでのエスケープ(React):

重要な注意点:

  • 出力コンテキストごとに適切なエスケープ関数を選択する(HTMLエスケープだけでは不十分な場合がある)

  • フレームワークの自動エスケープ機能を無効化する機能(dangerouslySetInnerHTML、v-html、{!! $var !!}など)は特に注意して使用する

  • HTML属性内でもエスケープは必須(onclickなどのイベント属性は特に危険)。



4. Content Security Policy(CSP)の厳格な設定

何をすべきか:

  • CSPヘッダーを設定し、実行可能なスクリプトのソースを厳格に制限する

  • unsafe-inlineやunsafe-evalの使用を避ける

  • nonce(使い捨ての暗号的に強い乱数)やハッシュベースのCSP設定を使用する

厳格なCSP設定(Express.js):

nonceを使用したインラインスクリプト(HTMLテンプレート例):

HashベースのCSP(静的HTML向け例):


  • CSP設定の注意点:

  • unsafe-inline や unsafe-eval を許可すると、CSPによるXSS防御効果が大幅に低下するため、可能な限り避けましょう。

  • nonce はリクエストごとに動的に生成する必要があるため、静的なHTMLファイルには適用が難しいです(その場合は hash ベースを検討)。

  • CSPはHTTPヘッダーで送信することが推奨されます(<meta> タグよりも早く適用され、より強力です)。

  • CSPは多層防御の一部として捉え、エスケープ処理などの他のXSS対策と併用しましょう。

  • 最初は Content-Security-Policy-Report-Only ヘッダーを使って違反レポートのみを収集し、既存サイトへの影響を確認しながら段階的に導入するのが安全です。



5. ホワイトリスト方式による入力値の検証と浄化

何をすべきか:

  • 全てのユーザー入力(フォーム、URLパラメータ、ヘッダーなど)に対して、「許可するものだけを明示的に定義する」ホワイトリスト方式の検証を行う。

  • 入力値のフォーマット(例: メールアドレス形式、数値、日付)、長さ、文字種などを厳密に制限する。

  • 必要に応じて、入力値から危険な可能性のある文字やタグを除去・無害化する浄化(サニタイゼーション)を行う。

  • 「禁止するものだけを定義する」ブラックリスト方式は、未知の攻撃パターンに対応できないため避ける。

実装例(PHP):



バリデーションとサニタイゼーションの注意点

  • ブラックリスト方式は常に不完全です。未知の攻撃やエンコーディングによる回避策に対応できません。

  • ホワイトリスト方式では「許可する安全なもの」を明確に定義し、それ以外は全て拒否(または無害化)するという考え方が基本です。

  • フロントエンド(ブラウザ側)でのバリデーションは、ユーザー体験向上のためには有効ですが、容易にバイパスできるため、セキュリティ対策としては不十分です。バックエンド(サーバー側)での再検証が必須です。

  • 入力値のデータ型を期待通りに扱うことも重要です。文字列として受け取った数値を扱う場合は、明示的に数値型に変換し、変換エラーも考慮しましょう(意図しない型強制に注意)。

  • HTMLを許可する場合、strip_tagsのような単純な除去ではなく、sanitize-html(Node.js)やHTML Purifier(PHP)のような堅牢なHTMLサニタイザーライブラリを使用し、適切に設定することが推奨されます。ライブラリは常に最新版を使いましょう。




フレームワーク別の対策ポイント



多くのモダンなフレームワークは、これらの対策を支援する機能を提供しています。



Laravel (PHP)

  • SQLインジェクション対策: Eloquent ORMやクエリビルダを使う限り、通常は自動的にパラメータ化クエリが使用され安全です (User::where(...), DB::table(...))。生SQL (DB::select, DB::statement, whereRawなど) を使う場合は、必ず ? や名前付きプレースホルダー (:name) を使って値をバインドしてください。

  • XSS対策: Bladeテンプレートの {{ $variable }} は自動的に htmlspecialchars を適用しエスケープします。HTMLを意図的に出力したい場合のみ {!! $variable !!} を使用しますが、その際は渡すデータが安全であることを保証する必要があります。



Django (Python)

  • SQLインジェクション対策: Django ORM (User.objects.filter(...) など) を使えば安全です。生SQL (.raw(), .extra()) を使う場合は、パラメータをリストや辞書で渡すことで安全に実行できます (User.objects.raw('...', [param1, param2]))。SQL文字列内に直接 %s フォーマットなどで変数を埋め込むのは危険です。

  • XSS対策: Djangoテンプレートでは {{ variable }} はデフォルトでHTMLエスケープされます。エスケープを無効にするには |safe フィルターや {% autoescape off %} タグを使用しますが、信頼できるコンテンツに限定してください。



Express.js (Node.js)

  • SQLインジェクション対策: Express自体にはDB機能はありません。使用するORM(Sequelize, TypeORMなど)やクエリビルダ(Knex.jsなど)のドキュメントに従い、パラメータ化クエリ(プリペアドステートメント)機能を使ってください (sequelize.query('... WHERE id = ?', { replacements: [id] }), knex('users').where({ id: userId }))。

  • XSS対策: テンプレートエンジン(EJS, Pug, Handlebarsなど)の多くはデフォルトでHTMLエスケープ機能を持っています。CSPヘッダーの設定には helmet ミドルウェアが役立ちます。入力値の検証・浄化には express-validator や validator.js, sanitize-html などのライブラリを組み合わせて使いましょう。



Spring Boot (Java)

  • SQLインジェクション対策: Spring Data JPA (Hibernate) を使う場合、メソッド名からのクエリ生成や JPQL/HQL でパラメータを使う限り安全です (userRepository.findByEmail(email), entityManager.createQuery("... where u.email = :email").setParameter("email", email))。JdbcTemplate を使う場合も ? プレースホルダーとパラメータ配列を使えば安全です。

  • XSS対策: テンプレートエンジン(Thymeleaf, FreeMarkerなど)には通常エスケープ機能があります。Thymeleafなら th:text="${variable}" でエスケープされ、th:utext はアンエスケープ出力なので注意が必要です。Spring SecurityはCSPヘッダーの設定などもサポートしています。入力値の検証には Bean Validation (JSR 380) を使うのが一般的です。




よくある間違いとその回避方法




「フロントエンドでの検証だけで十分」と考える

  • よくある間違い: フォームの入力値をJavaScriptでチェックするだけで満足し、バックエンドでの検証を省略してしまう。

  • 正しい対応: フロントエンドの検証はユーザー補助であり、簡単にバイパス可能です。バックエンドでの再検証は必須。「信頼できない入力はすべて検証・浄化する」という原則を守りましょう。



WAF(Webアプリケーションファイアウォール)に頼りすぎる

  • よくある間違い: WAFを導入したからアプリケーション側の対策は不要だと考えてしまう。

  • 正しい対応: WAFは重要な防御層ですが、万能ではありません。未知の攻撃やアプリケーション固有のロジック欠陥までは防げない場合があります。アプリケーション自体のセキュリティ対策(セキュアコーディング) を基本とし、WAFは多層防御の一部と考えましょう。WAFのルールが最新か、適切に設定されているかの確認も重要です。



サードパーティライブラリの安全性を過信する

  • よくある間違い: 利用しているフレームワークやライブラリが、すべてのセキュリティ面倒を見てくれると思い込む。

  • 正しい対応: フレームワークやライブラリは多くの対策を提供しますが、開発者がそれを正しく使う必要があります(例: ORMの生SQL機能、テンプレートのエスケープ無効化)。また、ライブラリ自体に脆弱性が発見されることもあります。依存関係の脆弱性スキャンツール(npm audit, OWASP Dependency-Check, Snykなど)を定期的に実行し、ライブラリを最新の状態に保ちましょう。



HTML属性内でのエスケープを忘れる

  • よくある間違い: HTMLの本文 (<div>ここ</div>) はエスケープするが、属性値 (<a href="ここ">, <input value="ここ">) のエスケープを怠る、または不十分。

  • 正しい対応: HTML属性値に対しても、htmlspecialchars($_GET['query'], ENT_QUOTES, 'UTF-8') のように、シングルクォートとダブルクォートを含むHTML特殊文字を適切にエスケープする必要があります。コンテキストに応じたエスケープを常に意識しましょう。




脆弱性検査ツールとその活用法



実装した対策が有効かを確認するために、ツールを活用しましょう。



静的解析ツール(SAST - Static Application Security Testing)

  • コードを実行せずにソースコード自体を解析し、潜在的な脆弱性パターンを検出します。

  • 主なツール: SonarQube, Snyk Code, GitHub Code Scanning (CodeQL), ESLint (with security plugins), PHPStan/Psalm (with security extensions)など。



動的解析ツール(DAST - Dynamic Application Security Testing)

  • 実際にアプリケーションを動作させながら、外部からリクエストを送信して脆弱性を探します。

  • 主なツール: OWASP ZAP (Zed Attack Proxy), Burp Suite, Acunetix, Invicti (旧Netsparker)など。


依存関係チェックツール(SCA - Software Composition Analysis)

  • 利用している外部ライブラリやフレームワークに既知の脆弱性がないかをチェックします。

  • 主なツール: OWASP Dependency-Check, Snyk Open Source, GitHub Dependabot alerts, npm audit, composer auditなど。


これらのツールを開発プロセスやCI/CDパイプラインに組み込むことで、早期に問題を発見し、修正することができます。




今日からできる!対策チェックリスト


自分のWebアプリケーションが大丈夫か、以下の項目でチェックしてみましょう。



SQLインジェクション対策

  • パラメータ化クエリ(プリペアドステートメント)または安全なORMの使用が徹底されている

  • ユーザーからの入力をSQL文へ直接文字列連結していない

  • Webアプリケーションが接続するデータベースユーザーの権限は最小限に絞られている

  • データベースエラーの詳細情報(テーブル名、カラム名など)がエンドユーザーに表示されないようになっている



XSS対策

  • HTMLに出力する全てのユーザー入力に対して、適切なHTMLエスケープ処理が行われている

  • HTTPレスポンスに厳格なContent-Security-Policy(CSP)ヘッダーを設定している(または設定を検討している)

  • innerHTMLへのユーザー入力設定やdocument.writeの使用を避け、安全なDOM操作API(textContent, createElementなど)を使用している

  • 使用しているテンプレートエンジンの自動エスケープ機能を有効にし、意図しない限り無効化していない



入力検証・浄化

  • サーバーサイドで、全ての外部からの入力(フォーム、URL、ヘッダー等)に対してバリデーションを実施している

  • バリデーションは「許可するパターン」(ホワイトリスト)に基づいて行われている

  • 入力値の型(数値、文字列、真偽値など)を期待通りにチェック・変換している

  • HTML入力を許可する場合は、信頼できるHTMLサニタイザーライブラリで浄化している



監視・テスト

  • 可能であれば、自動化されたセキュリティテスト(SAST, DAST, SCA)をCI/CDパイプラインに組み込んでいる

  • 定期的にSASTツールでコードをスキャンしている

  • 定期的にSCAツールで依存ライブラリの脆弱性をチェックし、アップデートしている

  • 定期的に(またはリリース前に)DASTツールで脆弱性スキャンを実施している(特にステージング環境などで)




よくある疑問(Q&A)


Q1: 個人開発の小規模なWebアプリでもセキュリティ対策は必要ですか?

A: はい、絶対に必要です。規模の大小に関わらず、インターネットに公開されている限り攻撃対象となりえます。自動化されたスキャンボットは、サイトの規模に関係なく脆弱性を探しています。「小さいから大丈夫」「見つからないだろう」という考えは危険です。また、将来的にアプリが成長する可能性を考えれば、最初からセキュリティを意識した開発習慣を身につけることが重要です。



Q2: フレームワークを使っていれば自動的に安全ですか?

A: いいえ、フレームワークは安全な開発を助けてくれますが、自動的に全てが安全になるわけではありません。例えば、Laravelの {!! $variable !!} や Djangoの |safe のように、開発者が意図的にセキュリティ機能を無効化することも可能です。フレームワークが提供するセキュリティ機能を正しく理解し、適切に利用することが重要です。使い方を誤れば、フレームワークを使っていても脆弱性は生まれます。



Q3: すでに公開しているWebアプリにセキュリティ対策を後から導入するのは難しいですか?

A: 新規開発時よりは労力がかかることが多いですが、不可能ではありません。段階的に進めるのが現実的です。まずは、認証・認可、個人情報や決済情報の処理など、最もリスクの高い箇所から優先的に対策を見直し、修正していくと良いでしょう。OWASP ZAPなどのDASTツールで現状の脆弱性を把握することから始めるのも有効です。



Q4: モバイルアプリ(ネイティブアプリ)とWebアプリでは対策方法が異なりますか?

A: APIを介して通信する場合、そのAPI(サーバーサイド)のセキュリティ対策(SQLインジェクション、不適切なアクセス制御など)はWebアプリと同様に重要です。モバイルアプリ固有の注意点としては、アプリ自体のリバースエンジニアリング対策、デバイス内のデータ保護、APIキーなどの機密情報の管理、安全でない通信経路(非HTTPS)の使用などがあります。原則は似ていますが、プラットフォーム特有のリスクも考慮する必要があります。



Q5: セキュリティ対策のコストパフォーマンスを高めるには?

A: 以下の点が挙げられます。

  • 早期対応(シフトレフト): 設計・開発の初期段階からセキュリティを考慮に入れることで、後工程での手戻りを減らす。

  • 自動化: CI/CDパイプラインへのSAST/DAST/SCAツールの組み込みで、継続的なチェックを効率化する。

  • 教育と意識向上: 開発チーム全体のセキュリティスキルと意識を高める。

  • 安全な共通部品化: 認証や入力検証など、セキュリティ上重要な処理を安全な共通コンポーネントとして開発し、再利用する。

  • フレームワーク/ライブラリの活用: セキュリティ機能が充実したモダンなフレームワークやライブラリを適切に活用する。




まとめ:セキュリティは開発プロセスの一部として


SQLインジェクションとXSS対策は、「特別なこと」ではなく、日常的なWebアプリケーション開発の基本的なプロセスの一部として組み込むべきものです。この記事で紹介した対策を実践することで、多くの一般的な攻撃から、あなたのアプリケーションと、それを利用するユーザーを守ることができます。


特に重要なポイントをもう一度おさらいしましょう:

  • 入力は常に疑う: ユーザーから(あるいは外部システムから)の入力は、潜在的に悪意のあるものとして扱う。

  • 適切なツールと手法を活用する: プリペアドステートメント、ORM、テンプレートエンジンの自動エスケープ、CSP、検証・浄化ライブラリなど、言語やフレームワークが提供するセキュリティ機能を最大限に活用する。

  • 多層防御を心がける: 単一の対策に頼るのではなく、入力検証、処理中の対策(例: ORM)、出力時の対策(例: エスケープ、CSP)、インフラ側の対策(例: WAF)など、複数の防御壁を組み合わせる。

  • 継続的に学び、更新する: 攻撃手法も防御技術も進化します。常に最新の脆弱性情報やセキュリティのベストプラクティスに関心を持ち、知識をアップデートし続けましょう。

基本的なセキュリティ対策をしっかりと実施するだけで、多くの脆弱性を防ぐことができると言われています。「機能実装」と「セキュリティ対策」を別物と考えず、安全で信頼性の高いコードを書く習慣の一部として、セキュリティを自然に取り入れていきましょう。

最後までお読みいただき、ありがとうございました。この記事が、あなたのWebアプリケーション開発をより安全なものにする一助となれば幸いです!




​シリーズ

新卒エンジニア向けセキュリティ

新卒エンジニアの皆さんが押さえておくべきセキュリティの基礎を現場目線からわかりやすく解説しています。 ITセキュリティ分野について広く網羅しておりますのでこの記事さえ読んでもらえれば最低限エンジニアとしてのセキュリティの知識を得ることができるようになっています。 1. 社会人ITエンジニアとして最初に身につけるべきセキュリティの超基礎 2. 開発するなら知っておくべきセキュリティ設計の基本 3. 業務で使うツールのセキュリティリスクと対策 4. フリーWi-Fiは使っていいの?エンジニアが気をつけるべき通信の安全性 5. 新卒エンジニアのための脆弱性診断入門 6. ソースコード管理と情報漏えい対策 7. API開発時に気をつけるべきセキュリティの基本 8. ID・パスワードだけで大丈夫?認証・認可の基礎知識 9. Webアプリ開発者向け!SQLインジェクション・XSSの防ぎ方 10. もしもインシデントが発生したら?エンジニアのための緊急対応入門

インフラセキュリティ

サーバーやネットワークを扱ううえで欠かせないインフラ領域のセキュリティ知識を、現場目線でわかりやすく解説するシリーズです。 サーバーやネットワークを中心としたインフラセキュリティ分野を広く網羅しており、本シリーズを通じて、エンジニアとしてインフラの安全を確保するために必要な基礎知識を、実践的な視点からしっかりと身につけることができます。 1. ゼロから始めるサーバーセキュリティの入門 2. 記事一本で押さえるネットワークセキュリティの基礎 3. まずはこれだけ!クラウドセキュリティの第一歩

ゼロトラスト時代のセキュリティ

このシリーズでは、「信頼しないことを前提とした設計思想」であるゼロトラストを軸に、実務で役立つ考え方や実装のポイントを、わかりやすく解説していきます。 単なる技術やツールの紹介ではなく、「なぜその考え方が重要なのか」「どんな視点を持つべきか」といった、本質的な部分に焦点を当てて解説します。このシリーズを通じて、これからの時代に求められるセキュリティ思考を身につけていきましょう。 1. 基本概念と設計思想 2. IDaaS導入とアイデンティティ管理 3. マイクロセグメンテーションの実装 4. エンドポイント保護の強化 5. ゼロトラストへの移行ロードマップ
bottom of page