REST APIトークンベースの認証


122

認証を必要とするREST APIを開発しています。認証自体は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アプリケーション、私はそれが抽出できると仮定思われるから、これでも製品は、誤った安心感を越えて何ですか?


2
sha1(timestamp + api_key + shard_secret)を使用する代わりに、hmac(shared_secret、timpestamp + api_key)を使用して、より良いセキュリティハッシュen.wikipedia.org/wiki/Hash-based_message_authentication_code
Miguel A. Carrasco

@ MiguelA.Carrascoそして2017年のbCryptは新しいハッシュツールであるというコンセンサスがあるようです。
ショーン・

回答:


94

すべてを分離して、問題ごとに個別に解決します。

認証

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

認証サーバーの負荷

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

伝送セキュリティ

SSL接続を使用できる場合、SSL接続は安全です*。偶発的な複数実行を防ぐために、複数のURLをフィルタリングするか、ユーザーにURLにランダムなコンポーネント(「ノンス」)を含めるように依頼できます。

url = username:key@myhost.com/api/call/nonce

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

nonce = generate_secure_password(length: 16);
one_time_key = nonce + '-' + sha1(nonce+salt+shared_key);
url = username:one_time_key@myhost.com/api/call

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

安全な秘密ストレージ

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

他のソフトウェアベンダーがAPIキーを取得できないようにして、代替クライアントの開発を防止しようとしている場合、ほぼ暗号化と保管を別々に行うアプローチのみが機能します。これはホワイトボックス暗号であり、これまでのところ、このクラスの問題に対する真に安全な解決策を考え出したものはありません。できることは、ユーザーごとに1つのキーを発行することで、乱用されたキーを禁止できます。

(*)編集: SSL接続は、検証のために追加の手順を実行しない限り安全であると見なされなくなりました。


cmcに感謝します。すべての良い点と素晴らしい食べ物を考えてください。S3 REST API認証メカニズムではなく、上記で説明したのと同様のトークン/ HMACアプローチを採用しました。
cantlin

トークンをサーバーにキャッシュする場合、それは基本的に古き良きセッションIDと同じではありませんか?セッションIDは存続期間が短く、高速キャッシュストレージ(実装している場合)にもアタッチされているため、リクエストごとにDBにアクセスする必要がありません。真のRESTfulでステートレスな設計にはセッションを含めるべきではありませんが、トークンをIDとして使用し、それでもDBにアクセスする場合は、代わりにセッションIDを使用するほうがよいのではないでしょうか。または、セッションデータ全体の暗号化または署名された情報を含むJSON Webトークンを使用して、真のステートレスデザインを実現できます。
JustAMartin 2017

16

純粋な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 APIは、HTTP認証スキーム標準に従う必要があります。このヘッダーのフォーマット方法の詳細は、RFC 2616 HTTP 1.1標準–セクション14.8 RFC 2616の承認、およびRFC 2617 HTTP認証:基本認証とダイジェストアクセス認証で定義されています。 。

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


8
「新しいHTTPヘッダーを追加するとRESTの原則に違反します」どうしてですか?そして、あなたがそうしているなら、あなたは、特定の期間の後に期限切れになるパスワードと特定の期間の後に期限切れになるトークンの違い(原理に関して)が正確に何であるかを説明するのにとても親切かもしれません。
より優れたオリーブ、2015年

6
ユーザー名+パスワードは、すべてのリクエストでクライアントとサーバー間で交換されるトークン(!)です。そのトークンはサーバー上で維持され、存続期間があります。パスワードの有効期限が切れた場合、新しいパスワードを取得する必要があります。「トークン」を「サーバーセッション」に関連付けているようですが、これは無効な結論です。それは実装の詳細になるため、無関係です。ユーザー名/パスワード以外のトークンのステートフルとしての分類は、純粋に人工的なものです。
より良いオリーブ

1
元の質問の一部である、基本認証よりもRESTfulで実装を行う理由を動機づけるべきだと思います。コードが含まれているいくつかの良い例にリンクすることもできます。この主題の初心者として、理論は多くの優れたリソースで十分に明確に見えますが、実装方法は明確ではなく、例は複雑です。何千回も実行されてきた何かをタイムリーに実装するにはカスタムコーディングが必要であるように思われるのはイライラします。
JPK 2016

13
-1「サーバー上の以前のRESTリクエストの状態を記憶しようとするトークンベースの認証などのトリックは、RESTの原則に違反しています。」トークンベースの認証は、以前のRESTリクエストの状態とは何の関係もなく、RESTのステートレス性に違反しません
Kerem Baydo Bayan 2016年

1
したがって、これによると、JSON Webトークンはユーザーの状態(クレーム)を格納できるため、REST違反です。とにかく、私はRESTに違反し、古き良きセッションIDを「トークン」として使用することを好みますが、最初の認証は、ユーザー名+パスで実行され、共有シークレットと非常に短期間のタイムスタンプを使用して署名または暗号化されます(そのため、誰かが再生しようとすると失敗しますそれ)。「企業風」のアプリケーションでは、セッションの利点を捨てるのが難しいため(ほとんどすべての要求で必要な一部のデータがデータベースに到達しないようにする)、真のステートレス性を犠牲にする必要がある場合があります。
JustAMartin 2017

2

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

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

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

ルーベンス

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.