web-dev-qa-db-ja.com

REST AP​​Iトークンベースの認証

認証を必要とするREST AP​​Iを開発しています。認証自体はHTTPを介した外部Webサービスを介して行われるため、認証サービスを繰り返し呼び出すことを避けるためにトークンを分配すると考えました。それは私の最初の質問にきちんと私をもたらします:

これは、クライアントに各要求でHTTP基本認証を使用し、認証サービスのサーバー側への呼び出しをキャッシュすることを要求するよりも本当に良いですか?

基本認証ソリューションには、コンテンツの要求を開始する前にサーバーへの完全な往復を要求しないという利点があります。トークンのスコープはより柔軟になる可能性があります(つまり、特定のリソースまたはアクションにのみ権限を付与します)が、これは私の単純な使用例よりもOAuthコンテキストに適しているようです。

現在、トークンは次のように取得されます。

curl -X POST localhost/token --data "api_key=81169d80...
                                     &verifier=2f5ae51a...
                                     &timestamp=1234567
                                     &user=foo
                                     &pass=bar"

api_keytimestamp、およびverifierは、すべての要求に必要です。 「検証者」は以下によって返されます。

sha1(timestamp + api_key + shared_secret)

私の意図は、既知の関係者からの呼び出しのみを許可し、呼び出しが逐語的に再利用されるのを防ぐことです。

これで十分ですか?アンダーキル?オーバーキル?

トークンを手にすると、クライアントはリソースを取得できます。

curl localhost/posts?api_key=81169d80...
                    &verifier=81169d80...
                    &token=9fUyas64...
                    &timestamp=1234567

可能な限り単純な呼び出しでは、これはある種恐ろしく冗長に見えます。 shared_secretが(少なくとも)iOSアプリケーションに埋め込まれると考えると、そこから抽出できると思われますが、これは誤ったセキュリティ感覚を超えた何かを提供しているのでしょうか?

118
cantlin

すべてを分けて、それぞれの問題を個別に解決してみましょう。

認証

認証については、baseauthには、プロトコルレベルで成熟したソリューションであるという利点があります。これは、多くの「後で発生する可能性がある」問題がすでに解決されていることを意味します。たとえば、BaseAuthを使用すると、ユーザーエージェントはパスワードがパスワードであることを認識し、キャッシュしません。

認証サーバーの負荷

サーバーに認証をキャッシュするのではなく、ユーザーにトークンを分配する場合でも、認証情報のキャッシュと同じことを実行しています。唯一の違いは、キャッシュの責任をユーザーに委ねていることです。これは、ユーザーにとっては無駄な労力のように思えるので、提案どおりにサーバーで透過的に処理することをお勧めします。

送信セキュリティ

SSL接続を使用できる場合、それですべてです。接続は安全です*。誤って複数回実行されるのを防ぐために、複数のURLをフィルタリングするか、ユーザーにURLにランダムコンポーネント(「nonce」)を含めるように依頼できます。

url = username:[email protected]/api/call/nonce

それが不可能で、送信された情報が秘密でない場合、トークンアプローチで提案したように、リクエストをハッシュで保護することをお勧めします。ハッシュはセキュリティを提供するため、baseauthパスワードとしてハッシュを提供するようユーザーに指示できます。堅牢性を向上させるには、タイムスタンプの代わりにランダム文字列を「ノンス」として使用して、リプレイ攻撃を防止することをお勧めします(同じ秒の間に2つの正当な要求が行われる可能性があります)。個別の「共有シークレット」および「APIキー」フィールドを提供する代わりに、APIキーを共有シークレットとして使用し、レインボーテーブル攻撃を防ぐために変更されないソルトを使用できます。ユーザー名フィールドは、認証の一部であるため、ナンスも置くのに適した場所のようです。これで、次のようなクリーンな呼び出しができました。

nonce = generate_secure_password(length: 16);
one_time_key = nonce + '-' + sha1(nonce+salt+shared_key);
url = username:[email protected]/api/call

これが少し面倒であることは事実です。これは、プロトコルレベルのソリューション(SSLなど)を使用していないためです。そのため、ユーザーに何らかのSDKを提供することをお勧めします。これにより、少なくともユーザーが自分でSDKを実行する必要がなくなります。この方法でこれを行う必要がある場合、セキュリティレベルが適切であると判断します(ジャストライトキル)。

セキュアな秘密ストレージ

それはあなたが阻止しようとしている人に依存します。ユーザーの電話にアクセスできるユーザーがユーザー名にRESTサービスを使用できないようにする場合は、ターゲットOSで何らかのキーリングAPIを見つけてSDKを使用することをお勧めします(または実装者)キーをそこに保存します。それが不可能な場合は、少なくとも暗号化し、暗号化されたデータと暗号化キーを別々の場所に保存することで、秘密を取得するのを少し難しくすることができます。

代替クライアントの開発を防ぐために他のソフトウェアベンダーがAPIキーを取得しないようにする場合、encrypt-and-store-seperatelyアプローチalmostのみが機能します。これはホワイトボックス暗号であり、これまでのところ、このクラスの問題に対する真に安全なソリューションを思い付く人はいません。できることは、ユーザーごとに1つのキーを発行することです。これにより、不正なキーを禁止できます。

(*)EDIT:SSL接続 安全であると見なされるべきではない なし 確認するために追加の手順を実行します それら。

91
cmc

純粋なRESTful APIは、基になるプロトコル標準機能を使用する必要があります。

  1. HTTPの場合、RESTful APIは既存のHTTP標準ヘッダーに準拠する必要があります。新しいHTTPヘッダーを追加すると、RESTの原則に違反します。車輪を再発明せずに、ステータス応答コード、ヘッダーなどを含むHTTP/1.1標準のすべての標準機能を使用してください。 RESTFul Webサービスは、HTTP標準を活用し、これに依存する必要があります。

  2. RESTfulサービスはステートレスでなければなりません。サーバー上の以前のRESTリクエストの状態を記憶しようとするトークンベースの認証などのトリックは、RESTの原則に違反します。繰り返しますが、これは必須です。つまり、Webサーバーがサーバー上で何らかのセッションを確立しようとして、サーバーに要求/応答コンテキスト関連情報を保存する場合、Webサービスはステートレスではありません。そして、ステートレスでない場合、RESTFulではありません。

結論:認証/承認の目的で、HTTP標準承認ヘッダーを使用する必要があります。つまり、認証が必要な後続の各リクエストにHTTP承認/認証ヘッダーを追加する必要があります。 REST AP​​Iは、HTTP認証スキーム標準に従う必要があります。このヘッダーのフォーマット方法の詳細は、RFC 2616 HTTP 1.1標準–セクション14.8 RFC 2616の承認、およびRFC 2617 HTTP認証で定義されています。 :基本およびダイジェストアクセス認証。

Cisco Prime Performance Managerアプリケーション用のRESTfulサービスを開発しました。 RESTFul APIコンプライアンスの詳細については、Googleでそのアプリケーション用に書いたREST AP​​Iドキュメントを検索してください here 。その実装では、HTTP「基本」​​認証スキームを使用することにしました。 -そのREST AP​​Iドキュメントのバージョン1.5以降をチェックアウトし、ドキュメント内の承認を検索します。

15
Rubens Gomes

Webでは、ステートフルプロトコルは、ブラウザとサーバー間で(CookieヘッダーまたはURIの書き換えを介して)リクエストごとに交換される一時トークンを持つことに基づいています。そのトークンは通常、サーバー側で作成され、特定の有効期間を持つopaqueデータの一部であり、特定のWebユーザーエージェントを識別することのみを目的としています。つまり、トークンは一時的なものであり、その会話の間、Webサーバーがクライアントユーザーエージェントに代わって維持する必要があるSTATEになります。したがって、この方法でトークンを使用する通信はSTATEFULです。クライアントとサーバー間の会話がSTATEFULの場合、RESTfulではありません。

ユーザー名/パスワード(Authorizationヘッダーで送信)は、通常、ユーザーを識別する目的でデータベースに保存されます。ユーザーが別のアプリケーションを意味する場合があります。ただし、ユーザー名/パスワードはNEVERは特定のWebクライアントユーザーエージェントを識別するためのものです。 (HTTP Basic Authorizationに続く)Authorizationヘッダーでのユーザー名/パスワードの使用に基づくWebエージェントとサーバー間の会話は、WebサーバーのフロントエンドがSTATEを作成または維持していないため、STATELESSですinformation特定のWebクライアントユーザーエージェントに代わって。そして、RESTの私の理解に基づいて、プロトコルは、クライアントとサーバー間の会話はステートレスでなければならないことを明確に述べています。したがって、真のRESTfulサービスが必要な場合は、sension種類のトークン(Webサーバーで作成されたSessionトークンなど)ではなく、すべての単一の呼び出しのAuthorizationヘッダーでユーザー名/パスワード(前の投稿で言及したRFCを参照)を使用する必要があります、承認サーバーで作成されたOAuthトークンなど)。

いくつかのRESTプロバイダーと呼ばれるものが、HTTPヘッダーで「Authorization:Bearer」として渡されるOAuth1やOAuth2の受け入れトークンなどのトークンを使用していることを理解しています。ただし、これらのトークンをRESTfulサービスに使用すると、RESTが含む真のSTATELESSに違反するように思えます。これらのトークンは一時サーバー側で作成/維持されるデータの一部であり、特定のWebクライアントユーザーエージェントを、そのWebクライアント/サーバー会話の有効期間中特定するためです。したがって、これらのOAuth1/2トークンを使用しているサービスは、STATELESSプロトコルの真の意味に固執したい場合、RESTと呼ばれるべきではありません。

ルーベンス

2
Rubens Gomes