web-dev-qa-db-ja.com

「ユーザーは、それが管理者であるかどうかを決定するべきではありません。特権またはセキュリティシステムで決定する必要があります。」

質問で使用されている例 最小限のデータを関数に渡す は、ユーザーが管理者であるかどうかを判断する最良の方法に触れています。一般的な答えの1つは次のとおりです。

user.isAdmin()

これにより、コメントが何度も繰り返され、何度も賛成票が投じられました。

ユーザーは、それが管理者であるかどうかを決定すべきではありません。特権またはセキュリティシステムがすべきです。何かがクラスに密接に結合されているからといって、それをそのクラスの一部にすることは良い考えではありません。

私は答えた、

ユーザーは何も決めていません。 Userオブジェクト/テーブルは、各ユーザーに関するデータを格納します。実際のユーザーは自分自身についてすべてを変更することはできません。

しかし、これは生産的ではありませんでした。明らかに、コミュニケーションを困難にしている根本的な見方の違いがあります。誰かがuser.isAdmin()が悪い理由を私に説明して、「正しく」行われたように見えるものの簡単なスケッチを描くことができますか?

本当に、セキュリティとそれが保護しているシステムを分離することの利点を見逃しています。セキュリティテキストには、セキュリティは最初からシステムに組み込む必要があり、開発、展開、保守、さらにはサポート終了のあらゆる段階で検討する必要があると書かれています。側面にボルトで固定できるものではありません。しかし、このコメントに対するこれまでの17の賛成票には、重要な何かが欠けているとあります。

55
GlenPeterson

ユーザーをキー、権限を値として考えます。

コンテキストが異なると、ユーザーごとに異なる権限が付与される場合があります。権限はコンテキストに関連しているため、コンテキストごとにユーザーを変更する必要があります。そのため、そのロジックを別の場所(コンテキストクラスなど)に配置し、必要な場所で権限を検索することをお勧めします。

関連性のない場所の管理フラグをクリアすることを忘れることができないため、副作用としてシステムがより安全になる可能性があります。

12
Wilbert

そのコメントはひどい言葉で書かれた。

重要なのは、ユーザーオブジェクトが管理者、試用ユーザー、スーパーユーザー、またはanythingであるか通常ではないかをユーザーオブジェクトが「決定」するかどうかではありません。明らかに、これらのことはどれも決定しません。ソフトウェアシステム全体は、ユーザーが実装したとおりに決定します。重要なのは、「ユーザー」classrepresentであるか、そのオブジェクトが表すプリンシパルの役割であるか、またはこれが別のクラスの責任であるかどうかです。

40
Kilian Foth

私は元のコメントをその言葉通りに言葉で表現することを選択しなかったでしょうが、それは潜在的に正当な問題を識別します。

具体的には、分離を保証する懸念は、認証承認です。

認証は、ログインしてアイデンティティを取得するプロセスを指します。これは、システムがあなたが誰であるかを認識する方法であり、パーソナライゼーション、オブジェクトの所有権などに使用されます。

Authorization許可されていることを指し、これは(一般的に)ではないによって決定されますあなたは誰。代わりに、役割や権限などの名前やメールアドレスなどを気にしないセキュリティポリシーによって決定されます。

これら2つは互いに直交して変化する可能性があります。たとえば、OpenID/OpenAuthプロバイダーを追加して認証モデルを変更できます。また、新しいロールを追加したり、RBACからABACに変更したりして、セキュリティポリシーを変更することもできます。

これらすべてが1つのクラスまたは抽象化に当てはまる場合、リスク軽減のための最も重要なツールの1つであるセキュリティコードは、皮肉にもリスクが高くなります。

私は、認証と承認が緊密に結合されているシステムを使用してきました。 1つのシステムでは、2つの並列ユーザーデータベースがあり、それぞれが1つのタイプの「役割」に対応していました。それを設計した人物またはチームは、1人の物理ユーザーが両方の役割にある可能性があること、複数の役割に共通の特定のアクションがあること、またはユーザーIDの衝突に問題がある可能性があることを決して考えていません。これは確かに極端な例ですが、信じられないほど作業するのは大変でした。

MicrosoftおよびSun/Oracle(Java)は、認証および承認情報の集約Security Principal と呼びます。完璧ではありませんが、かなりうまく機能します。たとえば、.NETでは、IPrincipalがあり、カプセル化IIdentity-前者はポリシー(認証)オブジェクトであり、後者はアイデンティティ(認証)。もう一方の内部を置くという決定に合理的に疑問を投げかけることができますが、重要なことは、あなたが書くほとんどのコードは、抽象化の1つだけのためのものであることですこれは、テストとリファクタリングが簡単であることを意味します。

User.IsAdminフィールドに問題はありません...以外の場合User.Nameフィールドもありません。これは、「ユーザー」の概念が適切に定義されていないであることを示していますが、これは悲しいことに、セキュリティの面であまり耳にしていない開発者の間でよくある間違いです。通常、identitypolicyで共有する必要があるのはユーザーIDだけです。これは、偶然ではなく、Windowsと* nixの両方のセキュリティモデルに実装されている方法とまったく同じです。 。

IDとポリシーの両方をカプセル化するラッパーオブジェクトを作成することは完全に許容されます。たとえば、現在のユーザーがアクセスを許可されているさまざまなウィジェットやリンクに加えて「hello」メッセージを表示する必要があるダッシュボード画面の作成を容易にします。このラッパーがIDとポリシー情報をwrapsしているだけで、それを所有しているとは主張していません。つまり、集約ルートとして表示されていない限り。

シンプルなセキュリティモデルは常に、新しいアプリケーションを最初に設計するときの良いアイデアのように思われるです。YAGNIなどがあるためですが、ほとんどの場合、後で突然あなたに噛み付いて戻ってきます。 、新機能が追加されます!

したがって、自分にとって最適なものがわかっている場合は、認証情報と承認情報を別々に保持します。現在の「承認」が「IsAdmin」フラグと同じくらい簡単な場合でも、それが認証情報と同じクラスまたはテーブルの一部でない場合は、セキュリティポリシーで変更すれば、すでに正常に機能している認証システムで再建手術を行う必要はありません。

30
Aaronaught

まあ、少なくともそれは 単一責任の原則 に違反しています。変更(複数の管理者レベル、さらに異なる役割?)があると、この種の設計はかなり面倒で保守がひどくなります。

セキュリティシステムをユーザークラスから分離する利点を検討してください。これにより、両方が単一の明確に定義された役割を持つことができます。最終的には、デザインが全体的にすっきりとして維持しやすくなります。

28
Zavior

問題は、おそらく不適切な表現であると思いますが、Userクラスに重きを置きすぎているということです。いいえ、これらのコメントで示唆されているように、メソッドUser.isAdmin()を使用してもセキュリティの問題はありません。結局のところ、誰かがコアクラスの1つに悪意のあるコードを挿入できるほどコードの深さにいる場合、深刻な問題が発生します。一方、Userがすべてのコンテキストの管理者であるかどうかを判断しても意味がありません。フォーラムのモデレーター、フロントページに投稿を公開できる編集者、公開する編集者向けのコンテンツを作成できる作家など、さまざまな役割を追加するとします。 Userオブジェクトに配置するアプローチでは、クラスに直接関係のないUserに存在するメソッドとロジックが多すぎます。

代わりに、さまざまなタイプのアクセス許可の列挙型とPermissionsManagerクラスを使用できます。おそらく、PermissionsManager.userHasPermission(User, Permission)のようなメソッドを使用して、ブール値を返すことができます。実際には、次のようになります(Javaの場合)。

if (!PermissionsManager.userHasPermission(user, Permissions.EDITOR)) {
  Site.renderSecurityRejectionPage();
}

これは基本的にRails method before_filterメソッド)と同等であり、現実の世界でこのタイプの権限チェックの例を提供します。

10
asthasr

Object.isX()は、オブジェクトとXの間の明確な関係を表すために使用されます。これは、ブール結果として表現できます。次に例を示します。

Water.isBoiling()

Circuit.isOpen()

User.isAdmin()はシステムのすべてのコンテキスト内の「管理者」の単一の意味を想定しています。つまり、ユーザーはシステムのすべての部分で管理者です。

簡単に聞こえるかもしれませんが、実際のプログラムはこのモデルにほとんど適合しませんが、ユーザーが[X]リソースを管理するが[Y]は管理しない、またはより明確なタイプの管理者([プロジェクト]管理者vs [システム]管理者)。

この状況では、通常、要件の調査が必要です。まれに、クライアントがUser.isAdmin()が実際に表す関係を必要とする場合があるので、明確化せずにそのようなソリューションを実装することをためらいます。

9
FMJaguar

ポスターは単に異なる、より複雑なデザインを念頭に置いていました。 私の意見では、User.isAdmin()で結構です。後で複雑なアクセス許可モデルを導入しても、User.isAdmin()を移動する理由はほとんどありません。後で、Userクラスを、ログインしたユーザーを表すオブジェクトと、ユーザーに関するデータを表す静的オブジェクトに分割することができます。または、そうでない場合があります。明日のために明日を保存します。

6
kevin cline

実際には問題ありません...

User.isAdmin()に問題はありません。私は確かにPermissionManager.userHasPermission(user, permissions.ADMIN)のようなものよりも好んでいます。SRPの神聖な名前では、コードがわかりにくくなり、価値のあるものが追加されません。

SRPは文字通り、一部では文字通り解釈されていると思います。クラスがリッチなインターフェースを持つことは、より良いことだと思います。 SRPとは、オブジェクトが単一の責任に該当しないものを、オブジェクトが共同編集者に委任する必要があることを意味します。ユーザーの管理者ロールがブールフィールドよりも複雑な場合、ユーザーオブジェクトがPermissionsManagerに委任することは非常に理にかなっています。ただし、これは、ユーザーがisAdminメソッドを保持することも適切ではないという意味ではありません。実際、これは、アプリケーションが単純なものから複雑なものに変化したときに、Userオブジェクトを使用するコードを変更する必要があることを意味します。 IOW、クライアントは、ユーザーが管理者であるかどうかの質問に答えるために本当に必要なことを知る必要はありません。

...しかし、なぜあなたは本当に知りたいのですか?

そうは言っても、ユーザーがウィジェットの更新などの特定のアクションの実行を許可されているかどうかなど、他の質問に答えられる場合を除いて、ユーザーが管理者であるかどうかを知る必要はほとんどないようです。その場合は、ウィジェットにisUpdatableBy(User user)などのメソッドを用意することをお勧めします。

_boolean isUpdatableBy(User user) {
    return user.isAdmin();
}
_

このようにして、ウィジェットは、ユーザーがウィジェットを更新するために満たす必要がある基準を把握する責任があり、ユーザーはそれが管理者であるかどうかを把握する責任があります。この設計は、その意図について明確に伝えており、その時が来たら、より複雑なビジネスロジックへの移行を容易にします。

[編集]

User.isAdmin()を使用し、PermissionManager.userHasPermission(...)を使用するnotの問題

ユーザーが管理者である(または管理者の役割を持っている)かどうかを知りたい場合は、PermissionManagerオブジェクトのメソッドを呼び出すよりも、Userオブジェクトのメソッドを呼び出す方がはるかに好ましい理由を説明するために、回答に追加すると思いました。

質問をする必要がある場合は常にUserクラスに依存することになると想定するのは公平だと思いますこのユーザーは管理者ですか?これは取り除くことができない依存関係。しかし、ユーザーに別のオブジェクトを渡してそれについて質問する必要がある場合は、ユーザーに既に持っているオブジェクトに加えて、そのオブジェクトに新しい依存関係が作成されます。質問が頻繁に出される場合、それは追加の依存関係を作成する多くの場所であり、依存関係のいずれかがそれを要求する場合、変更しなければならない可能性がある多くの場所です。

これを、依存関係をUserクラスに移動する場合と比較してください。今、突然あなたはクライアントコード(質問をする必要があるコードはこのユーザー、管理者)がどのように実装に結合されていないシステムを持っていますこの質問に回答します。パーミッションシステムを完全に変更することは自由であり、1つのクラスの1つのメソッドを更新するだけで変更できます。すべてのクライアントコードは同じままです。

アクセス許可サブシステムのUserクラスに依存関係を作成するのを恐れて、UserにisAdminメソッドがないことを主張することは、1ドルを費やして10セントを獲得することに似ています。確かに、Userクラスで1つの依存関係を回避しますが、質問をする必要があるすべての場所で1つ作成することを犠牲にしてください。お買い得ではありません。

5
KaptajnKold

この議論は、Eric Lippertによるブログ投稿を思い出させました。これは、クラスの設計、セキュリティ、および正確さに関する同様の議論で私の注目を集めました。

なぜフレームワーククラスの多くがシールされているのですか?

特に、エリックはポイントを上げます:

4)安全。ポリモーフィズムの要点は、動物のように見えても実際にはキリンであるオブジェクトを渡すことができるということです。ここには潜在的なセキュリティ問題があります。

封印されていない型のインスタンスを受け取るメソッドを実装するたびに、その型の潜在的に悪意のあるインスタンスに直面しても、そのメソッドを堅牢にする必要があります。悪意のあるWebページが実装をサブクラス化し、仮想メソッドをオーバーライドしてロジックをめちゃくちゃにするものを実行し、それを渡すため、実装に当てはまることがわかっている不変条件に依存することはできません。クラスをシールするたびに、そのクラスの機能を知っているという確信を持って、そのクラスを使用するメソッドを記述できます。

これは正接のように思えるかもしれませんが、他のポスターが [〜#〜] solid [〜#〜]単一の責任の原則 について提起したポイントとうまく一致すると思います。 _User.IsAdmin_メソッドまたはプロパティを実装しない理由として、次の悪意のあるクラスを検討してください。

_public class MyUser : User
{

    public new boolean IsAdmin()
    {
        // You know it!
        return true;
    }

    // Anything else we can break today?

}
_

確かに、それは少しストレッチです:IsAmdminを仮想メソッドにすることはまだ誰も提案していませんが、ユーザー/ロールアーキテクチャが奇妙に複雑になった場合に発生する可能性があります。 (またはそうでない可能性があります。_public class Admin : User { ... }_を検討してください。このようなクラスには、上記のコードが含まれている可能性があります。)悪意のあるバイナリを配置することは一般的な攻撃方法ではなく、あいまいなユーザーライブラリよりも混乱の可能性がはるかに高くなります。 -繰り返しになりますが、これは 特権エスカレーション のバグであり、本当の悪意者への扉を開きます。最後に、悪意のあるバイナリまたはオブジェクトのインスタンスがランタイムに侵入した場合、「特権またはセキュリティシステム」が同様の方法で置き換えられることを想像するのはそれほど簡単ではありません。

しかし、エリックのポイントを心に留めて、ユーザーコードを特定の種類の脆弱なシステムに配置すると、おそらくゲームを失うだけでした。

ああ、そして記録を正確にするために、私は質問の仮定に同意します。「ユーザーは、それが管理者であるかどうかを決定すべきではありません。特権またはセキュリティシステムがすべきです。」 _User.IsAdmin_メソッドは、システム上でコードを実行している場合、コードの100%の制御に留まらないので悪い考えです。代わりにPermissions.IsAdmin(User user)を実行する必要があります。

1
Patrick M

ここでの問題は次のとおりです。

if (user.isAdmin()) {
   doPriviledgedOp();
} else {
   // maybe we can anyway!
   doPriviledgedOp();
}

セキュリティを適切に設定した場合、特権メソッドは呼び出し元に十分な権限があるかどうかをチェックする必要があります。この場合、チェックは不要です。または、特権を持たないクラスがセキュリティに関して独自の決定を行うことはない、または信頼している。

0
James Anderson