web-dev-qa-db-ja.com

REST認証とAPIキーの公開

私はRESTを読んでいます。そして、SOについてだけでなく、他の多くのサイトやブログでも多くの質問があります。この特定の質問が尋ねられたのを見たことはありませんが...何らかの理由で、私はこの概念に心を包むことができません...

RESTful APIを構築していて、それを保護したい場合、私が見た方法の1つはセキュリティトークンを使用することです。他のAPIを使用したとき、トークンと共有秘密がありました...意味があります。私が理解していないのは、残りのサービス操作へのリクエストがjavascript(XHR/Ajax)を介して行われていることです。 APIキーをコピーしてから、キーとシークレットを使用してその人物になりすましますか?

91
tjans

apiシークレットは明示的に渡されません。シークレットは現在のリクエストのsignを生成するために使用されます。サーバー側では、サーバーは同じプロセスに従って2つのsign =が一致した場合、リクエストは正常に認証されます-signのみがリクエストを通過し、シークレットは通過しません。

22
James.Xu

パートナーが登録したドメインでのみ使用できるAPIを公開しています。そのコンテンツは一部公開されています(ただし、既知のドメインでのみ表示されることが望ましい)が、ほとんどはユーザーに対してプライベートです。そう:

  • whatが表示されることを確認するには、ユーザーがログインする必要がありますが、これは個別に処理されます。

  • whereデータが表示されるかどうかを判断するには、公開APIキーを使用して既知のドメインへのアクセスを制限し、とりわけプライベートユーザーデータが [〜#〜 ] csrf [〜#〜]

このAPIキーは実際に誰にも見えます。他の方法でパートナーを認証することはありません。また、 REFERERは不要 です。それでも、それは安全です:

  1. get-csrf-token.js?apiKey=abc123がリクエストされた場合:

    1. データベースでキーabc123を検索し、そのキーの有効なドメインのリストを取得します。

    2. CSRF検証Cookieを探します。存在しない場合は、安全なランダム値を生成し、それを HTTPのみ セッションCookieに入れます。 Cookieが存在した場合、既存のランダムな値を取得します。

    3. APIキーからCSRFトークンを作成し、Cookieからランダムな値を作成し、 sign it を作成します。 (サーバー上にトークンのリストを保持するのではなく、値に署名します。両方の値は署名されたトークンで読み取り可能です、それで問題ありません。)

    4. 応答をキャッシュしないように設定し、Cookieを追加して、次のようなスクリプトを返します。

      var apiConfig = apiConfig || {};
      if(document.domain === 'expected-domain.com' 
            || document.domain === 'www.expected-domain.com') {
      
          apiConfig.csrfToken = 'API key, random value, signature';
      
          // Invoke a callback if the partner wants us to
          if(typeof apiConfig.fnInit !== 'undefined') {
              apiConfig.fnInit();
          }
      } else {
          alert('This site is not authorised for this API key.');
      }
      

    ノート:

    • 上記は、サーバー側のスクリプトがリクエストを偽造することを防ぐものではなく、ドメインがブラウザによってリクエストされたifと一致することを保証するだけです。

    • JavaScriptの同じOriginポリシー は、ブラウザーがXHR(Ajax)を使用してJavaScriptソースを読み込んで検査できないようにします。代わりに、通常のブラウザは<script src="https://our-api.com/get-csrf-token.js?apiKey=abc123">(または動的な同等物)を使用してのみロードでき、コードを実行します。もちろん、サーバーはnotをサポートする必要があります Cross-Origin Resource Sharing または生成されたJavaScriptのJSONPもサポートしません。

    • ブラウザスクリプトは、上記のスクリプトをロードする前にdocument.domainの値を変更できます。ただし、同じOriginポリシーでは、subdomain.example.comexample.comに書き換える、またはmyblog.wordpress.comwordpress.comに書き換える、または一部のブラウザーではbbc.co.ukco.uk に書き換えるなど、removingプレフィックスのみを短縮できます。

    • サーバー側のスクリプトを使用してJavaScriptファイルを取得すると、サーバーもCookieを取得します。ただし、サードパーティのサーバーは、ユーザーのブラウザにそのCookieをドメインに関連付けることはできません。したがって、サーバー側スクリプトを使用してフェッチされたCSRFトークンと検証Cookieは、ブラウザではなく、後続のサーバー側コールでのみ使用できます。ただし、このようなサーバー側の呼び出しにはユーザーCookieが含まれないため、パブリックデータのみを取得できます。これは、サーバー側のスクリプトがパートナーのWebサイトから直接取得できるデータと同じです。

  2. ユーザーがログインするときに、ユーザーCookieを任意の方法で設定します。 (JavaScriptが要求される前に、ユーザーがすでにログインしている可能性があります。)

  3. サーバーへの後続のすべてのAPIリクエスト(GETおよびJSONPリクエストを含む)には、CSRFトークン、CSRF検証Cookie、および(ログオンしている場合)ユーザーCookieを含める必要があります。サーバーは、リクエストを信頼するかどうかを判断できます。

    1. 有効なCSRFトークンが存在することにより、JavaScriptが期待されるドメインから読み込まれたことを確認しますifブラウザーによって読み込まれます。

    2. CSRFトークンの存在without検証Cookieは偽造を示します。

    3. CSRFトークンとCSRF検証Cookieの両方が存在しても、何も保証されません。これは、偽造されたサーバー側リクエスト、またはブラウザからの有効なリクエストのいずれかです。 (サポートされていないドメインから作成されたブラウザからのリクエストであってはなりません。)

    4. ユーザーCookieの存在は、ユーザーがログオンしていることを保証しますが、ユーザーが特定のパートナーのメンバーであることも、ユーザーが正しいWebサイトを表示していることも保証しません。

    5. ユーザーCookieの存在without CSRF検証Cookieは偽造を示します。

    6. ユーザーCookieの存在により、ブラウザーを介して現在の要求が行われます。 (ユーザーが未知のWebサイトで資格情報を入力しないと仮定し、ユーザーが自分の資格情報を使用してサーバー側の要求を行うことを気にしないと仮定します。)also 、そのブラウザでCSRF検証Cookieも受信されました。次に、alsoに有効な署名を持つCSRFトークンがある場合、and CSRF検証Cookieの乱数はそのCSRFトークンの乱数と一致し、そのためのJavaScriptトークンは、CSRF Coo​​kieが設定されたのと同じ以前のリクエストの間に受信されたため、ブラウザも使用しました。また、これは、トークンが設定される前に上記のJavaScriptコードが実行されたこと、およびその時点でドメインが指定されたAPIキーに対して有効だったことを意味します。

      そのため、サーバーは署名付きトークンのAPIキーを安全に使用できるようになりました。

    7. いずれかの時点でサーバーがリクエストを信頼しない場合、403 Forbiddenが返されます。ウィジェットは、ユーザーに警告を表示することでそれに対応できます。

署名済みCSRFトークンと比較するため、CSRF検証Cookieに署名する必要はありません。 Cookieに署名しないと、各HTTPリクエストが短くなり、サーバーの検証が少し速くなります。

生成されたCSRFトークンは無期限に有効ですが、検証Cookieとの組み合わせでのみ有効であるため、ブラウザーが閉じられるまで効果的です。

トークンの署名の有効期間を制限できます。ユーザーがログアウトするときに、CSRF検証Cookieを削除して、 OWASPの推奨事項 を満たすことができます。また、複数のパートナー間でユーザーごとの乱数を共有しないようにするには、Cookie名にAPIキーを追加します。ただし、ユーザーが複数のウィンドウで同じサイトを閲覧し、単一のCookieを共有している場合(更新時にすべてのウィンドウで更新されるため、新しいトークンが要求されたときにCSRF検証Cookieを簡単に更新することはできません他のウィンドウのJavaScriptトークンは、その単一のCookieと一致しなくなります)。

OAuthを使用している場合は、 OAuthおよびクライアント側ウィジェット も参照してください。ここからJavaScriptのアイデアが得られました。 サーバー側 APIの使用では、JavaScriptコードに依存してドメ​​インを制限できないため、公開APIキーの代わりに秘密キーを使用しています。

59
Arjan

この質問には回答がありますが、明確にするために、共有秘密認証は次のように機能します。

  1. クライアントには公開キーがあり、これは誰とでも共有できますが、重要ではないため、JavaScriptに埋め込むことができます。これは、サーバー上のユーザーを識別するために使用されます。
  2. サーバーには秘密鍵があり、この秘密は保護されなければなりません。したがって、共有キー認証では、秘密キーを保護できることが必要です。したがって、別のサービスに直接接続するパブリックjavascriptクライアントは、シークレットを保護するためにサーバー仲介者が必要なため、不可能です。
  3. サーバーは、秘密鍵(秘密鍵は一種の塩のようなもの)とタイムスタンプを含むアルゴリズムを使用して要求に署名し、その後、要求をサービスに送信します。タイムスタンプは、「リプレイ」攻撃を防ぐためのものです。リクエストの署名は、約n秒のみ有効です。署名に含まれていたタイムスタンプの値を含むタイムスタンプヘッダーを取得することで、サーバー上でそれを確認できます。そのタイムスタンプが期限切れの場合、リクエストは失敗します。
  4. サービスは、署名だけでなく、プレーンテキストで署名されたすべてのフィールドも含む要求を取得します。
  5. 次に、サービスは共有秘密キーを使用して同じ方法でリクエストに署名し、署名を比較します。
9
chris

APIキーではなく、セッションキーを意味すると思います。この問題は、httpプロトコルから継承され、 セッションハイジャック として知られています。通常の「回避策」は、他のWebサイトと同様に、httpsに変更することです。

RESTサービスセキュアを実行するには、https、およびおそらくクライアント認証を有効にする必要があります。しかし、結局のところ、これはREST idea。RESTは決してセキュリティについて語りません。

1
PeterMmm

サーバー側で行うことは、ログインまたはサインアップ時にクライアントに送り返される期限切れのセッションIDを生成することです。クライアントは、そのセッションIDを共有シークレットとして使用して、後続のリクエストに署名できます。

セッションIDは一度だけ渡され、これはSSL経由でなければなりません。

例を参照してください here

セッションハイジャックを防ぐために、リクエストに署名する際にナンスとタイムスタンプを使用します。

1
Iain Porter

私は元の状況で質問に答えようとします。質問は、「JavaScriptに配置するのに安全な秘密(API)キーはありますか。

私の意見では、システム間の認証の目的に反するため、非常に安全ではありません。キーはユーザーに公開されるため、ユーザーは許可されていない情報を取得できます。一般的な休憩では、通信認証はAPIキーにのみ基づいているためです。

私の意見では、JavaScript呼び出しは基本的に、残りの呼び出しを行う責任がある内部サーバーコンポーネントに要求を渡すことです。内部サーバーコンポーネントでは、サーブレットが許可ベースのファイルシステムなどのセキュリティで保護されたソースからAPIキーを読み取り、HTTPヘッダーに挿入して外部の残りの呼び出しを行うとします。

これがお役に立てば幸いです。

1
MG Developer