📕

Webアプリケーションセキュリティ入門 - Cyber Security Roadmap

2024/11/14に公開

Webアプリケーションセキュリティ

はじめに

今では、Webアプリケーションは仕事ではもちろん生活にも欠かせない存在です。Gmailでメールを確認したり、Amazonで買い物をしたりと、さまざまなサービスをブラウザ(SafariやGoogle Chromeなど)を使って利用できるのでとても便利です。しかし、その便利さの裏にはセキュリティリスクも潜んでいます。この記事では、Webアプリの仕組みや弱点について理解し、どんなリスクがあり、どのようなことを学ぶべきか整理していきます。

Webアプリケーションの仕組み

Webアプリケーションは、リモートサーバー(インターネットを通じてアクセスできるサーバー)上で動作し、ブラウザを通して利用することができます。たとえば、ショッピングサイトで商品を探したり購入したりする際、サーバーがデータを処理し、その結果をユーザーに返しています。

オンラインショッピングサイトを例に見た脆弱性と問題

Amazonのようなオンラインショッピングサイトを例にとってみます。こうしたWebアプリには、さまざまな情報が保存されています。

  • 商品データ:商品名、価格、画像、説明など
  • ユーザーデータ:名前、住所、電話番号など
  • 購入データ:購入履歴や支払い情報など

このようにして多くの情報を扱うアプリケーションにおいて、攻撃者がアプリの脆弱性を突くと、以下のような問題が発生する可能性があると考えています。

  • 商品情報が改ざんされ、ブランドイメージに悪影響が出る
  • ユーザー情報が悪用される
  • クレジットカード情報が不正利用され、金銭的な被害が発生する

もちろんのこと、こういったリスクは、運営者やユーザーにとって大きな問題を引き起こしかねません。

Webアプリケーションに潜む主なセキュリティリスク

ここでは、Webアプリケーションにおける代表的なセキュリティリスクを紹介します。

1. ログインに関するリスク

不正ログインが発生すると、以下のようなリスクが生じます。

  • 情報の漏洩と悪用
    アカウントに保存されている個人情報が盗まれ、不正利用される可能性があります。

  • 金銭的な被害
    オンラインショッピングでの不正購入や、銀行口座や仮想通貨ウォレットからの資金流出が発生する恐れがあります。

  • 信頼性の低下
    SNSやメールアカウントを悪用され、有害な投稿や詐欺メールの送信により、個人や組織の信用が損なわれます。

  • 二次被害の連鎖
    同一の認証情報を複数のサイトで使用している場合、不正ログインをきっかけに他のアカウントも乗っ取られる危険性があります。

  • 信用の喪失
    被害が公になると、個人や企業が社会的信用を失う可能性があります。特に、公人や企業の公式アカウントでは大きなダメージとなります。

不正ログイン攻撃への対策

概要

不正ログインを試みる攻撃にはさまざまな手法がありますが、いずれも大量の試行や既存の認証情報を用いて認証突破を目指すものです。

1. ブルートフォース攻撃/辞書攻撃
  • ブルートフォース攻撃: すべての可能な文字列を順番に試し、不正ログインを試みる手法。
  • 辞書攻撃: 一般的に使用されるパスワードリストを基に試行を行う手法。
2. ジョーアカウント探索/リバースブルートフォース攻撃
  • ジョーアカウント探索: ユーザー名の存在有無を確認する攻撃。エラーメッセージや応答時間の違いを利用します。
  • リバースブルートフォース攻撃: 固定のパスワード(例: "password123")を複数のアカウントに対して試行する手法。
3. パスワードスプレー攻撃/パスワードリスト攻撃
  • パスワードスプレー攻撃: よく使われるパスワードを少数選び、多数のアカウントで試行する攻撃。アカウントロックを回避しながら攻撃が行われます。
  • パスワードリスト攻撃: 流出した認証情報(ユーザー名とパスワードのペア)を利用して不正ログインを試みる手法。
基本的な対策
要件
  1. アカウントロック機能の実装

    • 認証失敗回数が指定回数を超えた際にアカウントをロックすることで、大量の試行を防ぎます。
  2. エラーメッセージの統一

    • 認証失敗時のエラーメッセージを統一し、ユーザー名やパスワードの存在可否を推測されないようにします。
  3. 応答時間の標準化

    • 認証処理の応答時間を固定し、タイミング攻撃を防ぎます。
  4. CAPTCHAの導入

    • 一定回数の認証失敗後にCAPTCHAを表示することで、自動化された攻撃を防ぎます。
  5. 二要素認証(2FA)の実装

    • パスワード突破だけではログインできないよう、追加認証を導入します。
  6. ログ監視の強化

    • 特定のユーザーIDやIPアドレスで異常な試行が行われた場合に即座に検知し、通知を行います。
  7. IPアドレスの制限

    • 特定地域外からのログイン試行や一定回数を超えたIPをブロックする仕組みを導入します。
仕様
  1. アカウントロック機能

    • ユーザーIDごとに認証失敗回数を記録し、上限回数(例: 5回)を超えた場合にアカウントをロック。
    • ロック解除方法は、管理者による解除または一定時間(例: 15分)経過後の自動解除。
  2. エラーメッセージの統一

    • メッセージ例: 「ユーザー名またはパスワードが間違っています。」
    • これにより、ユーザー名が存在するかどうかを攻撃者に特定されるリスクを軽減。
  3. 応答時間の標準化

    • 成功・失敗に関わらず、認証処理時間を一定(例: 2秒)に設定。
    • 必要に応じてランダムな遅延を追加し、攻撃者が時間差を利用できないようにする。
  4. CAPTCHAのトリガー

    • 認証失敗回数が一定回数を超えた場合、CAPTCHAを表示。
  5. 二要素認証

    • ログイン時にSMSまたは認証アプリを使用してワンタイムコードを入力するプロセスを追加。
  6. ログ監視

    • 異常な試行を検知した場合、以下の処理を自動化:
      • 試行回数が一定以上のIPを一時ブロック。
      • 管理者やユーザー本人に通知を送信。

弱いパスワードへの対策

概要

簡単に推測できる一般的な弱いパスワードは不正ログイン攻撃の標的となりやすいため、複雑で長いパスワードを設定する必要があります。

基本的な対策
要件
  1. 強力なパスワードポリシーの導入

    • ユーザーに対して、強固なパスワードを設定することを要求します。
    • パスワードの長さや構造に関する基準を設定することで、推測可能性を下げます。
  2. 弱いパスワードの使用禁止

    • 一般的に使用されるパスワードリストを利用し、それらの登録を防ぎます。
  3. パスワードの使い回し防止

    • 他のサービスで使用されたパスワードや過去に使用されたパスワードを再利用できないようにします。
  4. パスワード生成の推奨

    • ユーザーに対して、安全なパスワード生成ツールやパスワード管理ツールの使用を促します。
  5. 定期的なパスワード変更の必要性を検討

    • 一定期間ごとにパスワードを変更するポリシーを導入します。
仕様
  1. パスワードポリシー

    • 最低文字数: 12文字以上
    • 必須要素: 英大文字、小文字、数字、記号をそれぞれ1文字以上含む。
    • 辞書攻撃を防ぐため、連続する文字列(例: "abcd1234")や簡単なパターン(例: "qwerty", "111111")を禁止。
  2. 弱いパスワードの検知

    • パスワード登録時に、既知の弱いパスワードリストと照合し、該当する場合はエラーを表示。
  3. パスワードの使い回し防止

    • 同じアカウント内で過去に使用されたパスワードは再登録できないようにする。
    • サービス間での認証データの共有を避け、使い回しリスクを低減。
  4. パスワード生成の支援

    • ランダムなパスワードを生成するオプションを提供(例: 16文字以上、複数の要素を含む)。
    • 生成されたパスワードを安全に保存できる方法を案内。
  5. パスワード変更通知

    • パスワードが変更された場合、登録されたメールアドレスに通知を送信し、不正な変更を検知可能にする。

パスワードのプレーンテキスト保存はNG

概要

パスワードをプレーンテキスト(平文)で保存することは、重大なセキュリティリスクを引き起こします。プレーンテキストで保存されたパスワードが流出した場合、攻撃者はそのまま利用可能な状態でアカウントにアクセスできるため、被害が広範囲に及ぶ可能性があります。これを防ぐためには、データベースに保存する際に必ず暗号化やハッシュ化を施す必要があります。

基本的な対策
要件
  1. ハッシュ化アルゴリズムの使用

    • パスワードは復号可能な暗号化ではなく、一方向性ハッシュ化を行います。これにより、平文パスワードが復元されるリスクを排除します。
  2. 安全なハッシュアルゴリズムの選択

    • 弱いハッシュアルゴリズム(例: MD5, SHA-1)は避け、最新のセキュリティ標準に基づいたアルゴリズムを使用します(例: bcrypt, Argon2, PBKDF2)。
  3. ソルト(Salt)の追加

    • 同じパスワードが入力されても異なるハッシュ値が生成されるよう、ソルトを付与します。これにより、レインボーテーブル攻撃を防止します。
  4. ペッパー(Pepper)の利用

    • ハッシュ化とは別に、固定の秘密値(ペッパー)を用いてセキュリティを強化します。ペッパーはアプリケーションコード内または別の安全な場所に保存します。
仕様
  1. ハッシュ化の実装

    • パスワード登録時に、以下のプロセスを実行します:
      1. ランダムなソルトを生成。
      2. パスワードにソルトを付加してハッシュ化。
      3. ソルトとハッシュ値をデータベースに保存(ソルトをハッシュと一緒に保存)。
    • 使用アルゴリズム例: bcrypt(推奨)、Argon2、PBKDF2。
  2. ソルトの生成

    • ランダムなソルトを生成するため、セキュアな乱数生成器(例: cryptoライブラリ)を使用。
    • ソルトの長さは、少なくとも16バイトを推奨。
  3. ペッパーの導入

    • アプリケーション内で固定値(ペッパー)を設定し、パスワードに追加してハッシュ化。
    • ペッパーは公開されないよう、環境変数や専用の安全なストレージに保存。
  4. 認証プロセス

    • ユーザーの入力パスワードにソルトを付加し、保存されたハッシュ値と比較して認証を行う。
  5. ハッシュ化アルゴリズムのバージョン管理

    • 将来のアルゴリズム更新に備え、データベース内で使用したハッシュ方式のバージョンを記録。

2.SQLインジェクション攻撃

概要

SQLインジェクションは、アプリケーションが入力を適切に処理しない場合に発生する攻撃手法で、攻撃者がデータベースに不正なSQL命令を送り込むことで、データを盗んだり改ざんしたりします。検索フォームやログイン画面など、データベースと通信する入力欄を悪用することで、通常はアクセスできない機密情報(例: ユーザー情報や管理者パスワード)を取得する可能性があります。

基本的な対策

要件
  1. 入力値の適切なバリデーション

    • ユーザーが入力した値を厳密に検査し、不正な入力を遮断します。
  2. プリペアドステートメント(Prepared Statement)の使用

    • SQLクエリとデータを分離することで、動的なSQL生成におけるリスクを排除します。
  3. 特殊文字のエスケープ

    • 特殊文字(例: シングルクォート ' やダブルクォート ")を適切にエスケープし、不正な命令の実行を防ぎます。
  4. 最小限の権限でデータベースアクセス

    • アプリケーションがデータベースにアクセスする際、必要最低限の権限のみを付与します。
  5. エラーメッセージの抑制

    • データベースエラーが発生しても、エラーメッセージにSQL構造やシステム情報を含めないようにします。
仕様
  1. 入力値のバリデーション

    • 数値や文字列の形式を正規表現で検証します。例:
      • 数字のみの入力を許可: /^[0-9]+$/
      • 英数字のみを許可: /^[a-zA-Z0-9]+$/
    • 不正な入力が検出された場合、エラーメッセージを表示して処理を中断。
  2. プリペアドステートメント

    • 動的なSQLクエリを作成する際、パラメータを直接埋め込むのではなく、プリペアドステートメントを使用します。例(PHPの場合):
      $stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
      $stmt->bindParam(':username', $username);
      $stmt->bindParam(':password', $password);
      $stmt->execute();
      
  3. 特殊文字のエスケープ

    • データベース操作前に特殊文字を適切にエスケープすることで、クエリ構造が破壊されないようにします。例:
      $safe_input = mysqli_real_escape_string($connection, $user_input);
      
  4. 権限の最小化

    • データベースユーザーに以下のような最小限の権限を付与:
      • SELECT(データの読み取り)
      • INSERT(必要に応じてデータの追加)
    • DROPやALTERなどの危険なコマンドの実行権限は付与しない。
  5. エラーメッセージの抑制

    • SQLエラー発生時に詳細な情報を表示せず、一般的なエラーメッセージを返します:
      • 例: 「エラーが発生しました。システム管理者にお問い合わせください。」

3.クロスサイトスクリプティング(XSS)

概要

クロスサイトスクリプティング(XSS)は、Webページに悪意のあるスクリプトを埋め込むことで、他のユーザーの情報を盗んだり、意図しない操作を実行させる攻撃です。典型的な例として、コメント欄やフォーム入力に悪意のあるJavaScriptを挿入し、他のユーザーがそのページを閲覧した際にクッキー情報を盗む、または偽の画面を表示して情報を入力させるといった攻撃が挙げられます。

基本的な対策

要件
  1. ユーザー入力の適切なサニタイズとエスケープ

    • 入力値を信頼せず、HTML、JavaScript、CSSなどのコンテキストで適切にエスケープを行います。
  2. Content Security Policy (CSP) の導入

    • サーバーが許可したスクリプト以外の実行を防ぐセキュリティポリシーを設定します。
  3. 入力値のバリデーション

    • 想定されたフォーマットに従わない入力をブロックし、不正なデータの保存を防ぎます。
  4. HTTP-only属性の使用

    • クッキーにHTTP-only属性を設定し、JavaScriptからアクセスできないようにします。
  5. JavaScriptの安全な使用

    • eval()innerHTMLのような危険なメソッドを避け、安全なDOM操作を使用します。
仕様
  1. 入力値のサニタイズとエスケープ

    • 出力時に特殊文字をエスケープし、不正なスクリプトが実行されないようにする。
      • 例: HTMLでは以下の変換を行う:
        • <&lt;
        • >&gt;
        • "&quot;
        • '&#x27;
        • /&#x2F;
    • サニタイズには、信頼できるライブラリ(例: OWASP ESAPI, DOMPurify)を使用。
  2. CSPの設定

    • WebサーバーのレスポンスヘッダーにCSPを追加:
      • 例:
        Content-Security-Policy: script-src 'self'; object-src 'none';
        
    • 外部スクリプトを使用する場合は、信頼済みのドメインのみ許可。
  3. 入力値のバリデーション

    • 例: メールアドレスの入力を検証する場合:
      const emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
      if (!emailPattern.test(inputValue)) {
        alert("Invalid email address");
      }
      
  4. クッキーの保護

    • HTTP-only属性を設定:
      • 例:
        Set-Cookie: session_id=abcdef; HttpOnly; Secure;
        
  5. JavaScriptの安全な使用

    • innerHTML の代わりに textContentsetAttribute() を使用:
      • 例:
        element.textContent = userInput;
        

4.暗号化の不備

概要

暗号化の不備は、支払い情報や個人データなどの機密情報が第三者に盗まれる原因となります。データが暗号化されずに送信されている場合、通信経路上で攻撃者に盗聴されるリスクが高まります。この問題は、特に平文通信(HTTP)や弱い暗号化技術を使用している場合に顕著です。適切な暗号化技術を使用し、安全な通信プロトコル(例: HTTPS)を採用することで、このリスクを軽減できます。

基本的な対策

要件
  1. HTTPSの全面導入

    • Webサイト全体でHTTPSを使用し、通信内容を暗号化します。
  2. 強力な暗号化アルゴリズムの採用

    • 現代の標準に適合する暗号化アルゴリズム(例: AES-256、RSA-2048)を使用します。
  3. TLS(Transport Layer Security)の使用

    • 最新バージョンのTLS(例: TLS 1.3)を使用し、安全な通信を確保します。
  4. 証明書の正当性の確保

    • 有効なSSL/TLS証明書を使用し、サイトの信頼性を向上させます。
  5. データの暗号化保存

    • 通信だけでなく、データベースに保存される機密データも暗号化します。
仕様
  1. HTTPSの実装

    • サーバーでSSL/TLS証明書を設定し、HTTPリクエストをHTTPSにリダイレクト:
      例: Apacheの場合
      <VirtualHost *:80>
          ServerName example.com
          Redirect permanent / https://example.com/
      </VirtualHost>
      
  2. 強力な暗号化アルゴリズムの選択

    • データの暗号化にはAES-256、公開鍵暗号化にはRSA-2048以上を使用。
    • 弱いアルゴリズム(例: DES, RC4)は使用しない。
  3. TLSの設定

    • 最新のTLSプロトコルを使用し、旧式のバージョン(例: TLS 1.0, TLS 1.1)は無効化:
      例: Nginxの設定
      ssl_protocols TLSv1.2 TLSv1.3;
      ssl_prefer_server_ciphers on;
      
  4. 証明書の取得と管理

    • 信頼できる認証局(CA)からSSL/TLS証明書を取得し、定期的に更新。
    • 無料のLet's Encryptも適切に使用可能。
  5. データ保存時の暗号化

    • データベースに保存される個人情報や支払い情報を暗号化:
      例:
      • AESで暗号化:
        from Crypto.Cipher import AES
        cipher = AES.new(key, AES.MODE_CFB, iv)
        encrypted_data = cipher.encrypt(data)
        

5.アクセス制御の不備

概要

アクセス制御は、各ユーザーが利用できる機能やデータを制限する仕組みであり、適切に設定することでシステムの安全性を確保します。しかし、アクセス制御が不適切に実装されると、本来アクセスできないはずの機密情報にアクセスされたり、システムの不正利用を許してしまうリスクがあります。例えば、一般ユーザーが管理者権限の操作を実行したり、他のユーザーの個人情報を取得したりする可能性があります。

基本的な対策

要件
  1. ロールベースアクセス制御(RBAC)の導入

    • ユーザーの役割(例: 一般ユーザー、管理者)に応じて権限を設定し、適切なアクセスを許可します。
  2. 最低権限の原則(Principle of Least Privilege)の徹底

    • 各ユーザーやシステムプロセスに、必要最小限の権限のみを付与します。
  3. デフォルトでアクセス拒否

    • 明示的に許可されたリソース以外へのアクセスを拒否する設定をデフォルトとします。
  4. 水平・垂直なアクセス制御の実装

    • 水平制御:同じ権限のユーザー間でのデータアクセス制限を確保。
    • 垂直制御:異なる権限レベル間でのデータアクセスを制限。
  5. アクセス制御のサーバー側での適用

    • クライアントサイドではなく、サーバー側でアクセス制御を適用することで改ざんリスクを防ぎます。

仕様

  1. ロールベースアクセス制御(RBAC)の設定

    • 各ユーザーに役割を割り当て、役割ごとにリソースや操作権限を定義。
      例:
      • 一般ユーザー: 商品閲覧、注文の作成。
      • 管理者: 商品の追加、価格変更、ユーザー管理。
  2. 最低権限の原則

    • APIやデータベースにアクセスするシステムプロセスやユーザーに対して、必要な操作(例: SELECT, INSERT)のみを許可。
    • データベースの「DROP」や「ALTER」などの権限は管理者に限定。
  3. デフォルト拒否の実装

    • 例:
      def has_access(user, resource):
          if resource in user.allowed_resources:
              return True
          return False  # デフォルトでアクセスを拒否
      
  4. 水平・垂直制御の具体例

    • 水平制御:
      • ユーザーIDを条件に加えて、自分のデータ以外を取得できないようにする:
        SELECT * FROM orders WHERE user_id = ?;
        
    • 垂直制御:
      • 管理者のみが商品価格を更新可能にする:
        if user.role == "admin":
            update_price(product_id, new_price)
        else:
            raise PermissionError("アクセスが拒否されました。")
        
  5. サーバー側の制御

    • クライアントから送信されたデータ(例: 権限情報)を信用せず、サーバー側で検証を実施。
      例:
      if request.user.role != "admin":
          return HttpResponseForbidden("許可されていない操作です。")
      

まとめ

Webアプリケーションは非常に便利ですが、その分、セキュリティリスクも潜んでいます。特に個人情報や取引データを扱うオンラインショッピングサイトなどは、攻撃者にとって魅力的なターゲットです。攻撃的セキュリティの視点からWebアプリケーションの脆弱性やリスクを理解し、基本的な防御策を学ぶ必要があります。

Discussion