セキュリティについて考慮すべき事柄

  • リビジョンの URL スラッグ: Persona/Security_Considerations
  • リビジョンのタイトル: Security considerations
  • リビジョンの ID: 315759
  • 作成日:
  • 作成者: Marsf
  • 現行リビジョン いいえ
  • コメント

このリビジョンの内容

あなたの Web サイトに Persona のサポートを追加する場合、Persona ができるだけセキュリティの重荷を負います。しかしながら、セキュリティのある面においては、あなたの Web サイトでしか対処できません。以下は、そのリストです。

セキュリティのベストプラクティス

あなたのサーバ上でアサーションを検証する

Persona を使う時は、ID アサーションが {{ domxref("navigator.id.watch()") }} の onlogin 関数に渡されます。アサーションは、常に あなたの検証サーバに渡してください。その検証結果を基に、あなたのサーバがユーザに追加の許可を与えるかどうかを決定してください:

// navigator.id.watch({ ... 内
onlogin: function(assertion) {
  // ユーザがログインしようとしています! ここで必要なことは:
  // 1. アサーションを検証のためにバックエンドに送信し、セッションを作成する。
  // 2. UI を更新する。
},

ユーザのブラウザで実行される JavaScript を使用してアサーションを検証しようとすると、悪意のあるユーザがローカルのインジェクションコードであなたのサイトのユーザに偽装し、JavaScript コードを覆すことができてしまいます。これは、コードが実行されるユーザのブラウザをあなたが完全に制御できないため、可能となります。

繰り返しますが、アサーションは、常に あなたの検証サーバに渡してください。リモート検証 API を使用する場合でも同じです。

audience 引数を明記する

アサーションを検証するには、POST リクエストを https://verifier.login.persona.org/verify に送信します。このリクエストには audience と呼ばれる引数が含まれます:

assertion=<ASSERTION>&audience=https://mysite.com:443"

audience 引数は必須です。常に、あなたのコード内かコードの設定内に audience を明記してください。特に次のことに注意してください:

  • ユーザのブラウザが送信した Host ヘッダを信頼してはいけません。
  • ユーザのブラウザが送信した明示的な引数を信頼してはいけません。ただし、document.location など、あなたの JavaScript で生成されたものを除きます。

ユーザのブラウザから伝えられた audience を信頼してしまうと、悪意のある Web サイトが 自身の Web サイトのアサーションを再利用して あなたの Web サイトにログインすることが可能になります。

SSL 証明書を検証する

アサーションを検証するには、POST リクエストを https://verifier.login.persona.org/verify に送信します。この HTTPS リクエストで、サーバから送られた証明書を信頼されたルート証明書に照らし合わせて確実に検証しなければなりません。これをしない場合、攻撃者が verifier.login.persona.org になりすまして偽の検証結果を返すことができます。

使用しているライブラリが証明書の検証リクエストを正しく行い、適切なルート証明書でそれを初期化しているか確認してください。

例えば、Python 2.7 の標準の urllib2 モジュール は、サーバ証明書を検証しません。代わりに、Python 2.x の "requests" モジュールや "urllib3" モジュール、または Python 3.x の標準の http.client.HTTPSConnection クラスの使用を推奨します。Perl の場合は、libwww-perl のバージョン 6.0 以降を使用してください。使用している言語やライブラリ、オペレーティングシステムによりますが、信頼された CA ルートと verifier.login.persona.org で使用されている単独の CA のどちらかのリストを提供する必要があるかもしれません。

CSRF プロテクションを実装する

CSRF (Cross-Site Request Forgery) ログイン攻撃では、攻撃者がクロスサイトリクエストフォージェリを利用して、ユーザを攻撃者の資格情報を使った Web サイトにログインさせます。

例えば: ユーザが form 要素を含む悪意のある Web サイトを訪れたとします。この form の action 属性には、攻撃者のユーザ名とパスワードを含む http://www.google.com/login への HTTP POST リクエストがセットされています。ユーザが form を送信すると、リクエストが Google に送信され、Google サーバがユーザのブラウザに Cookie をセットします。これで、ユーザが知らないうちに、攻撃者の Google アカウントへのログインが成功してしまいます。

この攻撃は、ユーザの個人情報を集めるために使われます。例えば、Google の Web History 機能は、ユーザによるすべての Google 検索の検索語を記録します。ユーザが攻撃者の Google アカウントにログインし、攻撃者が Web History 機能を有効にすると、ユーザはこれらすべての情報を攻撃者に与えることになります。

CSRF ログイン攻撃とその防御手段は、Robust Defenses for Cross-Site Request Forgery (PDF) に詳しく解説されています。これらは Persona に限ったことではありません。ほとんどのログイン機構は、このような攻撃への潜在的な脆弱性を持っています。

CSRF ログイン攻撃からサイトを護るために使える手段には、様々なテクニックがあります。上記のドキュメントを参照してください。

取り得るアプローチ方法の一つは、サーバ内に秘密の ID を作成してブラウザと共有し、ログインリクエストを行う時にそれをブラウザから提供してもらうことです。例えば:

  1. ユーザがサイトを訪れたらすぐに (ログインする前に) ユーザのセッションをサーバ上に作成し、セッション ID をブラウザの Cookie に格納します。
  2. サーバ上で 10 文字以上のランダムな英数字の文字列を生成します。UUID をランダムに生成するとよいでしょう。これは CSRF トークンです。このトークンをセッションに格納します。
  3. CSRF トークンを JavaScript や HTML 内の隠し form 変数に埋め込むことによってブラウザに渡します。
  4. AJAX サブミッションや form の POST に CSRF トークンに含めてください。
  5. サーバ側では、アサーションを受け取る前に、送信された CSRF トークンがセッションに格納された CSRF トークンと一致するか確認します。

さらなる向上

コンテントセキュリティポリシー (CSP)

コンテントセキュリティポリシー (CSP) は、クロスサイトスクリプティング (XSS) やデータインジェクション攻撃を含む、特定の攻撃の検出と軽減を助けるセキュリティの追加レイヤーです。これらの攻撃は、データの盗難からサイトの破壊、マルウェアの拡散まで、すべての攻撃に使われます。

あなたのサイトで CSP を使う場合は、サイトポリシーで Persona を有効にする必要があるでしょう。あなたのポリシーに依存しますが、次のことが必要です:

  • インラインの javascript: URI を削除し、追加のスクリプトファイルから読み込んだコードに置き換えてください。このスクリプトファイルでは、対象の要素をその ID を基に見つけ、{{ domxref("element.onclick", "onclick") }} を設定するか、{{ domxref("element.addEventListener()", "addEventListener()") }} を呼び出すことにより、要素にスクリプトを結び付けます。
  • https://login.persona.orgscript-srcframe-src の両方を許可し、あなたのサイトがリモートの include.js ファイルを読み込んでフォールバックの Persona 実装と通信できるようにしてください。

Apache コンフィギュレーションには、次の行を含めることになるでしょう:

Header set X-Content-Security-Policy: "default-src 'self'; frame-src 'self' https://login.persona.org ; script-src 'self' https://login.persona.org"

このリビジョンのソースコード

<p>あなたの Web サイトに Persona のサポートを追加する場合、Persona ができるだけセキュリティの重荷を負います。しかしながら、セキュリティのある面においては、あなたの Web サイトでしか対処できません。以下は、そのリストです。</p>
<h2 id="Best_Practices">セキュリティのベストプラクティス</h2>
<h3 id="Verify_assertions_on_your_server">あなたのサーバ上でアサーションを検証する</h3>
<p>Persona を使う時は、ID アサーションが {{ domxref("navigator.id.watch()") }} の <code>onlogin</code> 関数に渡されます。アサーションは、<em>常に</em> あなたの検証サーバに渡してください。その検証結果を基に、あなたのサーバがユーザに追加の許可を与えるかどうかを決定してください:</p>
<pre class="brush:js;">
// navigator.id.watch({ ... 内
onlogin: function(assertion) {
  // ユーザがログインしようとしています! ここで必要なことは:
  // 1. アサーションを検証のためにバックエンドに送信し、セッションを作成する。
  // 2. UI を更新する。
},
</pre>
<p>ユーザのブラウザで実行される JavaScript を使用してアサーションを検証しようとすると、悪意のあるユーザがローカルのインジェクションコードであなたのサイトのユーザに偽装し、JavaScript コードを覆すことができてしまいます。これは、コードが実行されるユーザのブラウザをあなたが完全に制御できないため、可能となります。</p>
<p>繰り返しますが、アサーションは、<em>常に</em> あなたの検証サーバに渡してください。リモート検証 API を使用する場合でも同じです。</p>
<h3 id="Explicitly_specify_the_audience_parameter">audience 引数を明記する</h3>
<p>アサーションを検証するには、POST リクエストを <code>https://verifier.login.persona.org/verify</code> に送信します。このリクエストには <code>audience</code> と呼ばれる引数が含まれます:</p>
<pre>
<code>assertion=&lt;ASSERTION&gt;&amp;audience=https://mysite.com:443"</code>
</pre>
<p><code>audience</code> 引数は必須です。常に、あなたのコード内かコードの設定内に audience を明記してください。特に次のことに注意してください:</p>
<ul>
  <li>ユーザのブラウザが送信した Host ヘッダを信頼してはいけません。</li>
  <li>ユーザのブラウザが送信した明示的な引数を信頼してはいけません。ただし、<code>document.location</code> など、あなたの JavaScript で生成されたものを除きます。</li>
</ul>
<p>ユーザのブラウザから伝えられた audience を信頼してしまうと、悪意のある Web サイトが <em>自身の</em> Web サイトのアサーションを再利用して <em>あなたの</em> Web サイトにログインすることが可能になります。</p>
<h3 id="Verify_SSL_certificates">SSL 証明書を検証する</h3>
<p>アサーションを検証するには、POST リクエストを <code>https://verifier.login.persona.org/verify</code> に送信します。この HTTPS リクエストで、サーバから送られた証明書を信頼されたルート証明書に照らし合わせて確実に検証しなければなりません。これをしない場合、攻撃者が <code>verifier.login.persona.org</code> になりすまして偽の検証結果を返すことができます。</p>
<p>使用しているライブラリが証明書の検証リクエストを正しく行い、適切なルート証明書でそれを初期化しているか確認してください。</p>
<p>例えば、Python 2.7 の標準の <a class="external" href="http://docs.python.org/release/2.7.3/library/urllib2.html#urllib2.urlopen" title="http://docs.python.org/release/2.7.3/library/urllib2.html#urllib2.urlopen">urllib2 モジュール</a> は、サーバ証明書を検証しません。代わりに、Python 2.x の "<a class="external" href="http://pypi.python.org/pypi/requests">requests</a>" モジュールや "<a class="external" href="http://pypi.python.org/pypi/urllib3" title="http://pypi.python.org/pypi/urllib3">urllib3</a>" モジュール、または Python 3.x の標準の <code>http.client.HTTPSConnection</code> クラスの使用を推奨します。Perl の場合は、<code>libwww-perl</code> のバージョン 6.0 以降を使用してください。使用している言語やライブラリ、オペレーティングシステムによりますが、信頼された CA ルートと <code>verifier.login.persona.org</code> で使用されている単独の CA のどちらかのリストを提供する必要があるかもしれません。</p>
<h3 id="Implement_CSRF_protection">CSRF プロテクションを実装する</h3>
<p>CSRF (Cross-Site Request Forgery) ログイン攻撃では、攻撃者がクロスサイトリクエストフォージェリを利用して、ユーザを攻撃者の資格情報を使った Web サイトにログインさせます。</p>
<p>例えば: ユーザが <code>form</code> 要素を含む悪意のある Web サイトを訪れたとします。この form の <code>action</code> 属性には、攻撃者のユーザ名とパスワードを含む <a class="external" href="http://www.google.com/login" title="http://www.google.com/login">http://www.google.com/login</a> への HTTP POST リクエストがセットされています。ユーザが form を送信すると、リクエストが Google に送信され、Google サーバがユーザのブラウザに Cookie をセットします。これで、ユーザが知らないうちに、攻撃者の Google アカウントへのログインが成功してしまいます。</p>
<p>この攻撃は、ユーザの個人情報を集めるために使われます。例えば、Google の <a class="link-https" href="https://www.google.com/history/">Web History</a> 機能は、ユーザによるすべての Google 検索の検索語を記録します。ユーザが攻撃者の Google アカウントにログインし、攻撃者が Web History 機能を有効にすると、ユーザはこれらすべての情報を攻撃者に与えることになります。</p>
<p>CSRF ログイン攻撃とその防御手段は、<a class="external" href="http://www.adambarth.com/papers/2008/barth-jackson-mitchell-b.pdf">Robust Defenses for Cross-Site Request Forgery</a> (PDF) に詳しく解説されています。これらは Persona に限ったことではありません。ほとんどのログイン機構は、このような攻撃への潜在的な脆弱性を持っています。</p>
<p>CSRF ログイン攻撃からサイトを護るために使える手段には、様々なテクニックがあります。上記のドキュメントを参照してください。</p>
<p>取り得るアプローチ方法の一つは、サーバ内に秘密の ID を作成してブラウザと共有し、ログインリクエストを行う時にそれをブラウザから提供してもらうことです。例えば:</p>
<ol>
  <li>ユーザがサイトを訪れたらすぐに (ログインする前に) ユーザのセッションをサーバ上に作成し、セッション ID をブラウザの Cookie に格納します。</li>
  <li>サーバ上で 10 文字以上のランダムな英数字の文字列を生成します。UUID をランダムに生成するとよいでしょう。これは CSRF トークンです。このトークンをセッションに格納します。</li>
  <li>CSRF トークンを JavaScript や HTML 内の隠し form 変数に埋め込むことによってブラウザに渡します。</li>
  <li>AJAX サブミッションや form の POST に CSRF トークンに含めてください。</li>
  <li>サーバ側では、アサーションを受け取る前に、送信された CSRF トークンがセッションに格納された CSRF トークンと一致するか確認します。</li>
</ol>
<h2 id="Enhancements">さらなる向上</h2>
<h3 id="Content_Security_Policy_(CSP)">コンテントセキュリティポリシー (CSP)</h3>
<p><a href="/docs/Security/CSP" title="/docs/Security/CSP">コンテントセキュリティポリシー</a> (CSP) は、クロスサイトスクリプティング (XSS) やデータインジェクション攻撃を含む、特定の攻撃の検出と軽減を助けるセキュリティの追加レイヤーです。これらの攻撃は、データの盗難からサイトの破壊、マルウェアの拡散まで、すべての攻撃に使われます。</p>
<p>あなたのサイトで CSP を使う場合は、サイトポリシーで Persona を有効にする必要があるでしょう。あなたのポリシーに依存しますが、次のことが必要です:</p>
<ul>
  <li>インラインの <code>javascript:</code> URI を削除し、追加のスクリプトファイルから読み込んだコードに置き換えてください。このスクリプトファイルでは、対象の要素をその ID を基に見つけ、{{ domxref("element.onclick", "onclick") }} を設定するか、{{ domxref("element.addEventListener()", "addEventListener()") }} を呼び出すことにより、要素にスクリプトを結び付けます。</li>
  <li><code>https://login.persona.org</code> に <code>script-src</code> と <code>frame-src</code> の両方を許可し、あなたのサイトがリモートの <code>include.js</code> ファイルを読み込んでフォールバックの Persona 実装と通信できるようにしてください。</li>
</ul>
<p>Apache コンフィギュレーションには、次の行を含めることになるでしょう:</p>
<pre>
<span class="diff-content"><span class="idiff">Header set X-Content-Security-Policy: "default-src 'self'; frame-src 'self' https://login.persona.org ; script-src 'self' https://login.persona.org"</span></span></pre>
Revert to this revision