80

クロスサイトリクエストフォージェリ(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なしでは機能しないという事実にもかかわらず、このアプローチに何か問題がありますか?

補遺:

4

6 に答える 6

32

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

してはいけないこと:

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

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

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

あなたがすべきこと:

  1. OWASP のガイダンスに従います。

  2. 具体的には、これが REST アプリケーションの場合、CSRF トークンの二重提出を要求できます。これを行う場合は、親ドメイン (example.com) ではなく、特定のフルドメイン ( www.mydomain.com ) に定義し、取得している「samesite」Cookie 属性も利用するようにしてください。人気。

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

注: @krubo が以下で述べているように、二重提出手法にはいくつかの弱点があることがわかっています (二重提出を参照)。この弱点には次のことが必要です。

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

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


新しい更新 07/06/2020

二重送信を行うための私の新しいお気に入りの方法は、以前のようにリクエストの本文で暗号化ランダム文字列を作成して渡すことです。ただし、Cookie をまったく同じ値にするのではなく、Cookie を証明書によって署名されている文字列のエンコードされた値にします。これは、サーバー側で検証するのは簡単ですが、攻撃者が模倣するのははるかに困難です. 以前の記事で概説した同じサイトの Cookie 属性とその他の保護を引き続き使用する必要があります。

于 2015-09-21T16:31:51.920 に答える
9

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

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

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

より多くの保護が必要な場合は、セッションごと、フォームごとに一意の ID を生成します。

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

于 2010-03-07T06:25:19.580 に答える
6

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

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

CSRF に耐性を持たせたい独自の認証システムを実装する前に、 「クロスサイト フォージェリに対する堅牢な防御」を非常に注意深く読みました。実際、私は独自の認証システムを実装することをまったく考え直します。

于 2010-03-06T20:24:04.137 に答える
3

CSRF防止チートシートには、安らかなサービスで使用できる方法がいくつかあります。最も RESTful なステートレス CSRF 軽減策は、Origin またはHTTP リファラーを使用して、信頼できるドメインからリクエストが発信されていることを確認することです。

于 2012-04-06T00:18:29.083 に答える
0

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 はリソースまたは複数のリソースの表現になる必要があり、リソースに対して POST/PUT/DELETE メソッドを呼び出すことは決してありません。たとえばGET http://my.client.com/resource/delete -> DELETE http://my.api.com/resource、非常に悪い解決策です。しかし、CSRF を妨害したい場合、これは非常に基本的なスキルです。

于 2013-12-05T10:12:19.270 に答える