web-dev-qa-db-ja.com

RESTfulアプリケーションでCSRFを防ぐ方法は?

クロスサイトリクエストフォージェリ(CSRF)は、通常、次のいずれかの方法で防止されます。

  • リファラーをチェックする-RESTfulだが信頼できない
  • トークンをフォームに挿入し、トークンをサーバーセッションに保存します-実際にはRESTfulではありません
  • 不可解なワンタイムURI-トークンと同じ理由でRESTfulではありません
  • このリクエストのパスワードを手動で送信します(HTTP認証で使用されるキャッシュされたパスワードではありません)-RESTfulですが便利ではありません

私の考えは、ユーザーシークレット、暗号化されているが静的なフォームID、およびJavaScriptを使用してトークンを生成することです。

_<form method="POST" action="/someresource" id="7099879082361234103">
    <input type="hidden" name="token" value="generateToken(...)">
    ...
</form>
_
  1. _GET /usersecret/john_doe_は、認証されたユーザーからJavaScriptによってフェッチされます。
  2. 応答:_OK 89070135420357234586534346_このシークレットは概念的に静的ですが、セキュリティを向上させるために毎日/時間を変更できます。これが唯一の機密事項です。
  3. JavaScriptで不可解な(ただしすべてのユーザーに静的な)フォームIDを読み取り、ユーザーシークレットと共に処理します:generateToken(7099879082361234103, 89070135420357234586534346)
  4. 生成されたトークンとともにフォームをサーバーに送信します。
  5. サーバーはユーザーシークレットとフォームIDを知っているため、クライアントが送信する前と同じgenerateToken関数を実行して、両方の結果を比較することができます。両方の値が等しい場合のみ、アクションが許可されます。

JavaScriptなしでは機能しないという事実にもかかわらず、このアプローチには何か問題がありますか?

補遺:

74
deamon

ここには多くの答えがあり、それらの多くに問題があります。

すべきでないこと:

  1. JavaScriptからセッショントークンを読み取る必要がある場合、何かひどく間違っていることになります。セッション識別子Cookieは常にHTTPOnlyが設定されているため、スクリプトでは使用できません。

    この1つの保護により、XSSの影響が大幅に軽減されます。これは、攻撃者がログインユーザーセッショントークンを取得できなくなるためです。これは、すべての意図と目的において、アプリケーションの資格情報と同等です。 1つのエラーが王国にキーを与えることは望ましくありません。

  2. セッション識別子は、ページのコンテンツに書き込まないでください。これは、HTTPOnlyを設定したのと同じ理由です。つまり、csrfトークンをセッションIDにすることはできません。それらは異なる値である必要があります。

すべきこと:

  1. OWASPのガイダンス に従ってください:

  2. 特に、これがRESTアプリケーションである場合 CSRFトークンの二重送信が必要 。これを行う場合は、特定のフルに定義してください-domain(www.mydomain.com)であり、親ドメイン(example.com)ではありません。また、人気が高まっている「samesite」Cookie属性も利用します。

暗号的にランダムなものを作成し、ASCII 16進数またはBase64エンコードで保存し、サーバーがページを返すときにCookieとしてフォームに追加します。サーバー側でCookieが値はフォームの値と一致します。あなたはCSRFを殺し、ユーザーへの余分なプロンプトを避け、より多くの脆弱性にさらされませんでした。

注:@kruboがダブルサブミッション技術を以下に述べているように、 いくつかの弱点があることが判明しています(ダブルサブミッションを参照) 。この弱点には以下が必要です。

  1. 親ドメインをスコープとするCookieを定義します。
  2. HSTSの設定に失敗しました
  3. 攻撃者はユーザーとサーバーの間のネットワークの場所を制御します

弱点は、「現実世界のセキュリティリスク」というよりも「クールデフコントーク」のカテゴリーにあると思います。いずれにせよ、二重送信を使用する場合は、自分自身を完全に保護するためにいくつかの追加手順を実行しても問題はありません。

25
Doug

私はこれを正しくしていますか:

  • Cookie経由でログインしたユーザーのCSRFに対する保護が必要です。
  • 同時に、Basic用のRESTfulインターフェイス、OAuthおよびアプリからの認証済みリクエストのダイジェスト)が必要です。

それでは、なぜユーザーがCookie経由でログインしているかCSRFのみを適用するをチェックしないのはなぜですか?

よくわかりませんが、別のサイトがBasic authやヘッダーなどを偽造することは可能ですか?

私の知る限り、CSRFはCookieについて? RESTful認証はCookieでは発生しません。

10
antitoxic

認証/承認を行うには、サーバー上に何らかの状態が必要です。ただし、httpセッションである必要はなく、分散キャッシュ(memcachedなど)またはデータベースに保存できます。

認証にCookieを使用する場合、最も簡単な解決策はCookie値を二重送信することです。フォームを送信する前に、CookieからセッションIDを読み取り、非表示フィールドに保存してから送信します。サーバー側で、リクエストの値がセッションID(Cookieから取得した)と同じであることを確認します。別のドメインからの邪悪なスクリプトは、CookieからセッションIDを読み取ることができないため、CSRFを防ぐことができます。

このスキームは、セッション全体で単一の識別子を使用します。

さらに保護したい場合は、セッションごと、フォームごとに一意のIDを生成します。

また、JSでトークンを生成しないでください。誰でもコードをコピーして別のドメインから実行し、サイトを攻撃できます。

9

静的フォームIDはまったく保護を提供しません。攻撃者は自分でそれを取得できます。覚えておいてください、攻撃者はクライアントでJavaScriptを使用することに制約されていません。サーバー側の静的フォームIDを取得できます。

提案されている防御を完全に理解しているとは思いません。 GET /usersecret/john_doe から来る?そのページはJavaScriptの一部ですか?それは文字通り提案されたURLですか?その場合、usernameはシークレットではないと想定しています。つまり、ブラウザまたはプラグインのバグがクロスドメインGETリクエストを許可している場合、evil.ruはユーザーシークレットを回復できます。クロスドメインGETを実行できるユーザーに取得を許可するのではなく、認証時にCookieにユーザーシークレットを保存しないのはなぜですか?

私は、CSRFに耐性を持ちたいと思う独自の認証システムを実装する前に、 "クロスサイトフォージェリに対する堅牢な防御" と読みました。実際、私は自分の認証システムの実装をまったく考え直します。

6
Scott Wolchok

CSRF Prevention Cheat Sheet には、安らかなサービスで使用できるいくつかの方法があります。最もRESTfulなステートレスCSRF緩和策は、Originまたは HTTPリファラー を使用して、信頼できるドメインから要求が発信されるようにします。

3
rook

JavaScriptなしでは機能しないという事実にもかかわらず、このアプローチには何か問題がありますか?

クライアントに送信する場合、ユーザーシークレットはシークレットではありません。通常、このようなシークレットを使用してハッシュを生成し、フォームと共に送信し、比較のためにそれらを送り返します。

RESTfulにしたい場合、リクエストにはその処理方法に関するすべての情報が含まれている必要があります。これを行う方法:

  • RESTクライアントでcsrfトークンCookieを追加し、フォームで非表示の入力で同じトークンを送信します。サービスとクライアントが異なるドメインにある場合、資格情報を共有する必要があります。あなたは2つのトークンを比較する必要があり、それらが同じであれば、リクエストは有効です...

  • RESTサービスを使用してcsrfトークンCookieを追加し、同じトークンをリソースの表現(非表示の入力など)で送信できます。他のすべては、の終わりと同じです。前のソリューション。このソリューションはRESTfulnessのエッジにあります(クライアントがCookieを変更するためにサービスを呼び出さない限り問題はありません。Cookieがhttpのみの場合、クライアントはそれを知らないはずです。各フォームに異なるトークンを追加し、Cookieに有効期限を追加すると、より複雑なソリューションを実行できます。有効期限をフォームとともに送り返すこともできるため、理由を知ることができます。トークンの検証が失敗したとき。

  • サービスのリソース状態にユーザーシークレット(ユーザーごとに異なる)を設定できます。表現を構築することにより、各フォームのトークン(および有効期限)を生成できます。実際のトークン(および有効期限、メソッド、URLなど)とユーザーシークレットからハッシュを生成し、フォームでそのハッシュを送信することもできます。もちろん「ユーザーシークレット」は秘密にしておくので、フォームで送信することはありません。その後、サービスがリクエストを取得した場合、リクエストパラメータとユーザーシークレットからハッシュを再度生成し、それらを比較できます。一致しない場合、リクエストは無効です...

あなたのRESTクライアントがjavascriptで注入可能な場合、それらはどれもあなたを保護しません。そのため、すべてのユーザーコンテンツをHTMLエンティティに対してチェックし、それらをすべて削除するか、innerHTMLの代わりに常にTextNodesを使用する必要があります。 SQLインジェクションやHTTPヘッダーインジェクションからも保護する必要があります。サイトを更新するために単純なFTPを使用しないでください。

GETリクエストは、常にサービスとクライアントのいずれかによる読み取り用であることに言及するのを忘れていました。サービスによって、これは明らかです。ブラウザでURLを設定すると、1つまたは複数のリソースの表現が必要になり、リソースでPOST/PUT/DELETEメソッドを呼び出すことはありません。例えば ​​GET http://my.client.com/resource/delete -> DELETE http://my.api.com/resourceは非常に悪い解決策です。しかし、CSRFを妨げたい場合、これは非常に基本的なスキルです。

0
inf3rno