web-dev-qa-db-ja.com

アクセス制御はコントローラーまたはリポジトリー層に実装する必要がありますか?

データベースからデータを返すHTTP APIを使用したプロジェクトがあります。 APIに到達するために通過するレイヤーは次のようになります。

DB->リポジトリ->コントローラ

リクエスタの権限に基づいて返される結果を制限しようとしています。これをリポジトリ層またはコントローラ層に含める必要がありますか?その理由は?

5
Joundill

理想的には、本当に高レベルのセキュリティが必要な場合、DB自体が、要求者が権限を持たないデータを返さないようにする必要があります。したがって、この場合の答えは「どちらでもない」でしょう-DB自体が適切なセキュリティモデルを提供し、特定のリクエスタの権限を認識し、データを制限する必要があります。

ただし、今日のほとんどの実際のアプリケーションは独自のユーザー/ロール/権限モデルを実装しているため、DB内にセキュリティモデルを簡単に実装することは困難です。だから私は責任を定義するための2つの可能な方法を見ます:

  • アプローチ1は、リポジトリからできるだけ多くのビジネスロジックを除外することです。この場合、リポジトリは返される結果セットを制限するためのツールを提供するだけであり、リクエスタがどの権限を持っているかを知りません。したがって、ここでは、コントローラが権限の評価を担当する場合があります。これは、アクセス許可がDBクエリの結果セットに影響を与えるコード内の場所が数箇所しかない場合に実行可能です。

  • アプローチ2は、結果セットをリポジトリで直接制限することです。リポジトリが呼び出されて再利用されるシステムに多くの異なる場所があり、それらのすべてが異なる結果セットを返すことを含む同じ許可モデルに従う必要がある場合、このアプローチは理にかなっています。

アプローチ2は、リポジトリを2つの異なるレイヤーに分割することで実装できることに注意してください。1つはアクセス許可についての知識のない一般的なレイヤーで、もう1つはセキュリティを実装する最上位のレイヤー(「装飾」リポジトリまたは「プロキシリポジトリ」)です。モデル:

DB -> Generic Repositories -> Secured Repositories -> Controller

ある種のセキュリティ要件がこの追加のレイヤー(コメントで示唆されているように、場合によっては追加のレイヤー)を正当化するかどうかを自分で判断する必要があります。これには「万能」の解決策はありません。システム全体を念頭に置いて、そのような設計上の決定を慎重に行わなければなりません。

9
Doc Brown

セキュリティは、コントローラの前にフレームワークによって適用される必要があります。

ほとんどのフレームワークでは、これはコントローラーメソッドへのアクセスを許可することを意味します。

コントローラ

コントローラは認証されたユーザーの詳細にアクセスでき、これらをパラメータとして使用して、それに応じてデータをフィルタリングできます。

[Authorize(Role='editor')] //framework binding to requests limits access
GetMyArticles()
{
     var userId = context.user.Id; //required data extracted from auth token/user context
     ....
}

リポジトリ

リポジトリの責任はデータを返すことなので、GetDataByUserIdなどのメソッドを持つことができますが、セキュリティを行うべきではありません。

例えば

...
this.repo.GetArticlesByUserId(userId)

[〜#〜]ない[〜#〜]

this.repo.GetArticles(user);

ユーザーオブジェクトまたはコンテキストをリポジトリに渡すと、単一の責任のルールに違反するため、試行しないでください。

データベース

ほとんどのデータベースには、ビジネスロジック用の十分な粒度のセキュリティコントロールがなく、Web APIは通常、ある種の中央サインオントークンを使用します。すべてのWebユーザーをデータベースのセキュリティで複製したり、資格情報をAPIに送信して、ユーザーとしてログオンできるようにする必要はありません。

例えば.

var connstr = appsettings[connstringWithServiceUser]
var repo = new repo(connstr)

[〜#〜]ない[〜#〜]

var connstr = "database;user" + context.user + context.password

これにより、ユーザーのパスワードが公開されます。あなたはそれにアクセスするべきではありません。

ALSO NOT

var connstr = "database;user=thisProcess"
...impersonate calling user

これは正しく引き抜くのが非常に難しいです。 Kerberosマルチホップ偽装と行レベルのデータベースセキュリティが必要です。

データベースのセキュリティモデルは、Webサイトのすべてのフロントエンドユーザーを保持するようには設計されていません。それらすべてをそこに置くことは監査の悪夢であり、あなたのセキュリティを低下させます。

さらに、DBの行レベルの権限を介して、「ユーザーは自分の記事しか表示できない」ビジネスロジックを実装する必要があります。それをテストする幸運なユニット。それは非常に悪い考えです、そして、私はそれが賛成票を得ることを信じることができません。

Auth→Controller→Repo→DB

Web APIのほぼ普遍的なソリューションです。

4
Ewan

セキュリティは階層化されています

母校の真実は、Bad People™がセキュリティを回避しようとすることです。

セキュリティを追加することにより、合法的にそこにいない誰かが何かをすることをできる限り真に困難にし、同時にシステムを合法的に毎日使用する人々が経験する邪魔の量を減らしたいと考えています。

境界

あなたの写真は完全に正確ではなく、次のようになります。

DB -> Repository -> Controller -> User

->は境界です。それはまだ単純化ですが、ネットワークやその他の問題を通じて可能になります。

ポイントは、各境界が良いものを通過させる必要がある一方で、悪いものを実行することを困難/不可能にすることです。

すべてのセキュリティをusercontrollerの間に配置することもできますが、誰かがそれを迂回した場合、彼らはフィールドデーになります。

同様に、DatabaseRepositoryの間にすべてのセキュリティを配置することはできません。サービス自体が公開され、それに渡されるすべてのデータが、おそらくそれを使用するすべての人が利用できるようになる必要があるため、すでに遅れています。これはおそらく期待するのは妥当ではありません。

DB->リポジトリ

実際のデータベースエンジンは、Repositoryに権限を適用する必要があります。明らかに、Repositoryは何も実行できません。

  • テーブルの作成/ドロップ、
  • インデックスの追加、
  • これらのテーブルの行を更新し、
  • これらの他のテーブルへの行の挿入、
  • 等...

エンジンはそのリポジトリに必要な権限のみを付与し、それ以上は付与しないでください。

リポジトリ->コントローラ

リポジトリも同様に、コントローラがそれに対して何もできないことを保証する必要があります。

  • 特に他のレコードにリンクされている場合は、顧客レコードを削除します
  • すでに支払われた注文に対して複数の支払いを挿入する
  • ViewOrderコントローラーにユーザーレコードの挿入を許可します。

これは、一部にはビジネスの健全性チェックを実装することによって行われ、一部にはコントローラにそうする権利があるかどうかを確認することによって行われます。これは通常、発信者がリストにあり、必要な権限を持っていることを確認する何らかのホワイトリストチェックによって、またはそれ自体を検証できるいくつかのアクセス許可オブジェクトによって実装されます。

コントローラー->ユーザー

コントローラ自体は、ユーザーが必要な権限を持っていることを確認する必要があります。通常、何らかのログイン方法、または証明書によって行われます。ログイン/証明書が必須である場合、ユーザーのアクセス許可がチェックされ、コントローラーにアクセスできるかどうかが確認されます。

これにより、セキュリティの層が提供されます。

たとえば、管理者アカウントに違反が発生し、OrderViewControllerが何らかのエクスプロイトで使用された場合、コントローラーは特権X、Y、Zしか持っていないため、リポジトリは奇妙な要求を拒否します。

コントローラーをバイパスしてリポジトリ内に到達した場合、すべてのデータをドロップしたり、簡単に自分のデータを格納したりすることはできません。

1
Kain0_0

採用するアプローチは、選択する認証方法によって異なります。認証は、最初の呼び出しでユーザー名とパスワードを使用してユーザーの身元を確認するのと同じくらい簡単で、その後の検証のためにトークンを発行する場合があります。

トークンに格納される情報の量によって、アクセス制御がコントローラーレベルで行われるか、モデル/リポジトリレベルで行われるかが決まります。アプリケーションに2種類のユーザーがいるとします

  1. 管理ユーザー-これは、データベースに保存されているすべての種類のレコードにアクセスできます
  2. 通常のユーザー-これは自分が作成したレコードのみにアクセスできます

認証トークンにユーザーの識別子に関する情報しか含まれていない場合、トークンからユーザーに関する完全な情報を抽出するには、データベースにクエリを実行する必要があり、データベースとの対話はコントローラーの機能ではなく、ここで説明するようにリポジトリーで実行する必要があります質問では 詳細に進む前に最上位レイヤーの権限を確認してください

認証トークンにユーザーのアクセスとその役割に関するすべての情報が含まれている場合、その場合はコントローラーレベル自体で、トークンを復号化してすべての権限を抽出し、トークンから取得した情報に基づいてリクエストを拒否して受け入れます。

ここで質問 アクセス許可とロールをJWTのペイロードに含める必要があります で述べたように、IMOはより多くの情報をトークンに格納することをお勧めします。

0
Anshul Sahni