作成者
権限はどこでチェックすべきですか?
権限をチェックする場所を決定する場合、異なるレイヤーで複数のチェックを実装することで、徹底的な防御を適用します。ファインダーやサービスのような低レベルのレイヤーから始め、GraphQL、公開REST API、コントローラのような高レベルのレイヤーが続きます。
詳細については、抽象化を再利用するためのガイドラインを参照してください。
多くのポイントで同じリソースを保護するということは、防御のレイヤーの1つが侵害されたり欠落したりしても、顧客データは追加のレイヤーによって保護されるということです。
権限の詳細については、セキュアコーディングガイドラインの権限のセクションを参照してください。
考慮事項
サービスまたはファインダーが適切な場所である理由
- 複数のエンドポイントがサービスまたはファインダーを共有するため、ダウンストリーム・ロジックが再利用される可能性が高くなります。
- レコードをフィルタリングするために、DBクエリに作成者ロジックを組み込む必要がある場合があります。
- セキュリティチェックとしてではなく、より良いUXを提供する以外、表示レイヤーでの権限チェックは避けるべきです。例えば、ボタンのような非データ要素の表示と非表示です。
Defense-in-Depthの欠点は以下の通りです:
-
DeclarativePolicy
ルールは比較的高性能ですが、条件によってデータベースが呼び出される可能性があります。 - メンテナンスコストが高い
例外
開発者は、特定のケースにおけるリスクとデメリットを考慮した上で、単一の領域でのみオーソライゼーションを行うことを選択することができます。
ドメインロジック(サービスやファインダー)を、例外を発生させる際の真実のソースとして優先してください。
バックエンドのワーカーロジックのようなロジックは、現在のユーザーに基づく作成者を必要としないかもしれません。サービスやファインダーのコンストラクタがcurrent_user
を期待しない場合、通常権限をチェックしません。
ヒント
あるクラスがcurrent_user
を受け入れる場合、そのクラスは作成者の責任を負うことになります。
例新しい API エンドポイントの追加
デフォルトでは、エンドポイントで作成者を認証します。既存の能力をチェックすることは理にかなっているかもしれません。
余談ですが、ほとんどのエンドポイントはリソースのCRUD(作成、読み込み、更新、破棄)アクションとしてきれいに分類できます。サービスや能力もそれに倣い、Projects::CreateService
や:read_project
のような名前が付けられています。
例えば、エンドポイント全体をサービスに抽出するとします。can?
のチェックはサービスの中に入ります。サービスが既存のファインダーを再利用し、それを目的のために変更するとします。ファインダーチェックをアビリティにすべきでしょうか?
- もしファインダーが
current_user
、権限をチェックしないのであれば、おそらくノーでしょう。 - ファインダーが
current_user
を受け入れ、権限をチェックしない場合、ファインダーの他の使用方法を再チェックし、権限を追加することを検討すべきです。 - ファインダーが
current_user
を受け入れ、すでに権限をチェックしている場合、私たちのケースを追加する必要があるか、既存のチェックが適切であるかのどちらかです。