web-dev-qa-db-ja.com

REST APIで、ユーザーが自分のリソースを変更/操作することのみが許可されていることを承認するための最良のソリューション

バックグラウンド:

現在、REST API、ノードw/expressを使用して構築している最中であり、モバイルアプリと最終的に(最新のブラウザベースの)Webサイトによって消費されます。

私は、ユーザーが自分のリソースを変更することのみが許可されるように、ユーザーの更新/アクション要求を承認する最良の方法を特定しようとしています。アクションは準高頻度で発生するため、懸念事項です。

注:この使用例では、エンティティーの所有権を譲渡することはできません。

可能なソリューション:

Solution:reddisのようなものによってバックアップされたサーバーセッションで各ユーザーのリソースのリストを保存および維持します。

懸念事項:サーバー側セッションの永続化には、特に複数のサーバーでスケーリングする独自の複雑さのセットがあります。これもRESTに違反しています。 do-sessions-really-violate-restfulness 詳細については。

解決策:ユーザーの更新/アクションクエリの前に読み取りクエリを実行します。 IEこのユーザーのアイテムを提供し、リストにある場合は更新を続行します。

Concern(s):ユーザーがリソースの代わりに操作するたびに追加の読み取りのオーバーヘッド。

解決策:ユーザーIDをdbレイヤーに渡して、それを更新条件付きの一部にするか、Postgresの行のようなものを使いたい場合データバックエンドに応じて、そのリソースのレベルセキュリティ。

Concern(s):これは、リソースがリクエストしているユーザーのものであるかどうかを確認するために、リクエストライフサイクルの少し遅いようです。エラーは、データバックエンドからずっとスローされる必要があります。同じように、認証とロールベースの承認はリクエストのライフサイクルの最初に行われることが多いので、少しずれているかもしれません。実装は、データバックエンドにも依存します。また、データバックエンドにビジネスロジックを示します。

解決策:クライアント側が一種のセッションに署名しました。 JWTまたは暗号化/署名されたCookieを使用します。基本的に、ユーザーのリソースIDのリストを含む信頼されたセッションを維持します。

Concern(s):クライアント側セッションのサイズ。不要な場合でも、すべてのリクエストで送信されるという事実。複数のアクティブなセッション/クライアントの可能性を導入すると、メンテナンスが非常に複雑になります。リソースが別のクライアントに追加されたときに、クライアント側の状態をどのように更新しますか。

解決策:リソースがフェッチされたときに、署名された更新トークン(JWT)またはURLをリソースとともにクライアントに渡します。リソースが更新/アクションされたときに期待します。署名されたトークンには、ユーザーIDとリソースIDが含まれ、これらに対して簡単に確認できます。

Concern(s):リソースの所有権を譲渡できる場合は複雑になりますが、私の場合は問題ありません。更新前の読み取りよりも複雑になります。ちょっと変?

最終的な考え:

私は最後の解決策に傾いていますが、それが頻繁に発生することはないので、何か不足しているのではないかと思いますか?あるいは、私が知らないデザインパターンの一部かもしれません。

8
Ashtonian

私のリストには、ユースケースに最も適した2つのソリューションがあると思います。更新前の読み取りクエリ(ソリューション2)と読み取り要求を含む更新トークンの送信(ソリューション5)。

パフォーマンスが心配な場合は、予想されるリソースに対する読み取りと更新の数に応じて、どちらかを決定します。読み取りよりもはるかに多くの更新が予想される場合は、リソースの所有者を特定するために追加のデータベースフェッチを実行する必要がないため、ソリューション5の方が明らかに優れています。

ただし、更新トークンを配布することによるセキュリティへの影響を忘れないでください。ソリューション2では、認証が安全であると仮定すると、サーバー上のリソースの所有者を決定するため、リソースの更新もおそらく安全です。

ソリューション5では、有効な署名を確認することを除いて、クライアントが行う主張を再確認しません。クリアテキストリンク(SSLなしなど)を介して更新を行う場合、トークン内のリソースとユーザーIDのエンコードだけでは安全ではありません。 1つは、攻撃をリプレイできるようにすることです。そのため、リクエストごとに成長するナンスを含める必要があります。また、更新トークンのタイムスタンプ/有効期限をエンコードしない場合、基本的には、そのトークンを持っているすべてのユーザーに無期限の更新アクセスを付与します。最後に、ナンスが必要ない場合は、フェッチされたリソースのhmacを少なくとも含める必要があります。これにより、更新トークンは、フェッチされたときのリソースの状態に対してのみ有効になります。これにより、リプレイ攻撃がより困難になり、更新トークンはリソースが特定の状態にある場合にのみ有効になるため、更新トークンの被害に関する知識がさらに制限されます。

安全なリンクを介して通信している場合でも、ナンス(または少なくともリソースの状態のhmac)と更新トークンの有効期限を追加するのは賢いことだと思います。アプリケーションドメインはわかりませんが、悪意のあるユーザーに対処すると、自分のリソースへの無制限の更新アクセスの力であらゆる種類の大混乱を引き起こす可能性があります。

Nonceを追加すると、データベースのリソースごとに列が追加され、最後のフェッチ要求で送信したnonceの値が格納されます。リソースのシリアル化されたjson表現に対してhmacを計算できます。その後、メッセージ本文の一部としてではなく、追加のHTTPヘッダーとしてトークンを送信できます。

ああ、私はURLではなくトークンを使用します。 RESTでは、特定のリソースの更新URLは、リソースの取得元のURLと同じでなければなりません。すべての認証と承認に関連するものをHTTPヘッダーに移動します。 PUTリクエストのAuthorizationヘッダーで更新トークンを提供できます。

リソースフェッチごとに増加するノンスを追加するには、リソースフェッチリクエストごとにデータベースの更新(ノンスの新しい値)も必要になることに注意してください(ただし、リソースの状態が実際に変更されたときに、ナンスの更新のみで済む場合もあります) 、そのため、パフォーマンスの観点からは解放されます)。ただし、その情報を一時メモリに保持し、フェッチと更新要求の間にサーバーが再起動されたときにクライアントに再試行させるだけの場合は除きます。

ソリューション4の補足事項(クライアント側セッション):ユーザーが作成できるリソースの数によっては、セッションサイズは問題にならない場合があります。クライアント側のセッション更新の問題もかなり簡単に解決できる可能性があります。リソースがクライアントから受信したセッションデータにリストされていないためにリソースへの更新リクエストが失敗したが、ユーザーは正しい場合は、そのクライアントからのセッションデータが古いかどうかをバックエンドで確認します。更新されている場合は、更新を許可し、更新リクエストへの回答とともに更新されたCookieを送り返します。これにより、ユーザーが古いローカルセッションデータでクライアントからリソースを更新しようとした場合(または悪意のあるユーザーがddosを実行しようとしたが、レート制限によって救助された場合)にのみデータベースルックアップが発生します。ただし、解決策4は他の解決策よりも複雑であり、他のアイデアの1つを使用した方がよいことに同意します。ソリューション4には、考慮すべきさまざまなセキュリティ上の考慮事項もあります。この多くの承認状態をクライアントに保存するには、セキュリティを完全に保つ必要があります。

1
Pascal

あなたが検討するかもしれない別の解決策:

作成したリソースを別のユーザーに転送できない場合は、ユーザーIDをリソースIDにエンコードすることを検討してください。

例えば。ユーザー「alice」に属するすべてのリソースの名前は「alice-1」、「alice-2」などです。

次に、「eve」が「alice-1」という名前のリソースにアクセスできるかどうかを確認するのは簡単です。

リソースの所有権を公開したくない場合は、次のように暗号化ハッシュ関数を使用できます。

  1. あなたのサーバーだけが知っているパスワードを与える
  2. そしてユーザー名u(例えば 'alice')
  3. そして、リソース名r(例: '1')

hmac(password, u | '-' | r )を生成します。連結です。結果をリソース名rとともにリソースのIDとして使用します。必要に応じて、ソルト値を追加します(パスワードを保存する場合など)。

更新リクエストが特定のリソースIDのユーザー「alice」から来る場合、リソースIDからrを抽出し、hmac(password、 'alice' | '-' | r)を計算して、それをリソースIDの残りの部分と比較します。一致する場合、アリスはリソースの更新を許可されます。一致しない場合、アリスは承認されません。また、パスワードがないと、誰がどのリソースを所有しているかがわかりません。

それは安全だと思いますが、hmacsの品質をブラッシュアップする必要があるかもしれません。

0
Pascal

管理する必要がある2つの個別の問題があるようです。 (1)リクエストを生成しているユーザーをどのように認証していますか? (2)そのユーザーに関連付けられているバックエンドオブジェクトをどのようにして知ることができますか。私はまた、あなたのユースケースに適しているかもしれない観察結果を持っています(3)

(1)RESTfulな方法でのユーザー認証は決して美しくありません。私はHTTP認証/承認ヘッダーとHTTP 401応答を使用して認証をトリガーする傾向があります。つまり、すべてのリクエストでユーザーを認証する可能性があります。これはモバイルアプリケーションであるため、独自のカスタム認証スキームを作成するオプションがあります。これにより、将来のリクエストで完全な認証の詳細ではなくセッショントークンを提供できます。

(2)オブジェクトのセットに関連付けられているユーザーは、データストア内の実際のオブジェクトと共に保存する必要がある重要なデータです。ビジネスロジックの一部として、オブジェクトの所有者を確認し、オブジェクトにアクセスするIDと比較する必要があります。これは、リクエストを検証するビジネスレイヤーの一部として、コードで明示的に行います。データベースヒットが十分に大きな問題である場合、memcacheまたは(非常に大量の高可用性サイトの場合はriak)などのキー値ストアにこの情報をキャッシュし、キャッシュがコールドの場合にのみデータベースにヒットすることは理にかなっています。このユーザー/オブジェクトの組み合わせ。

(3)ユーザーとオブジェクトの数が十分に少ない場合は、ユーザーIDとオブジェクトIDを同じフィールドに結合し、このフィールドをオブジェクトテーブルのデータベースの主キーとして使用できます。例えば64ビットの数値は、ユーザーIDに24ビットのスペース、オブジェクトIDに40ビットのスペースを提供します。そうすれば、オブジェクトを照会するときに、それがユーザーにとって確実であることが100%保証されます。

0
Michael Shaw