認証と認可
認証(AuthN)と認可(AuthZ)は、それらに関連する脆弱性が非常に頻繁に発生するため、極めて重要なテーマです。これほど頻繁に問題となる背景には、それぞれの定義や、何が原因で脆弱性が生じるのかについて、理解が曖昧な部分があることが考えられます。
念のため、各用語の定義を以下にまとめます。
- 認証:ユーザーは誰か?
- 認可:ユーザーは何にアクセスできるべきか?
以下、それぞれについて詳しく見ていきましょう。
認証(識別および認証の不備)
不適切な認証は、以下のような多岐にわたる脆弱性の原因となります。
- 特定のページやエンドポイントにおける認証の欠如
- ブルートフォース攻撃(クレデンシャルスタッフィング)に対する保護の欠如
- 安全でないアカウント/パスワード復旧プロセス
- 認証トークンの生成、検証、有効期限、送信、または保存における不備
- 2要素認証(2FA)の検証が不適切、または欠落している(該当する場合)
リストの最初にある「認証の欠如」は、実際の現場で最も頻繁に見られる問題です。多くの場合、開発者はページやエンドポイントごとに必要な認証レベルを明示的に設定(アノテーション)する必要がありますが、この手順は非常に見落とされやすいものです。
システムは「フェイルオープン」ではなく「フェイルクローズ」(失敗した際にアクセスを拒否する)設計にすることが推奨されます。つまり、各エンドポイントに認証済みセッションが必要であることを個別に指定するのではなく、デフォルトですべてのルートにおいて認証済みセッションを必須とし、例外的に許可する場合のみ設定を上書きするようにします。これにより、人為的ミスの可能性を大幅に減らすことができます。
認可(不適切なアクセス制御)
認可の問題は、非常によくある以下の形で現れることが一般的です。
- 安全でない直接オブジェクト参照(IDOR)
- 機能レベルのアクセス制御の欠如(認可の欠如)
- 権限昇格(水平方向または垂直方向)
安全でない直接オブジェクト参照
オブジェクトには、参照するためのキーとして一意の識別子(ID)が割り当てられるのが一般的です。ユーザーが注文履歴やアカウント情報などを閲覧するリクエストを送る際、通常はこのIDが含まれます。「安全でない直接オブジェクト参照」は、アプリケーションがそのユーザー(または未認証ユーザー)に対して、特定のオブジェクトへのアクセス権があるかどうかを検証し損ねた場合に発生します。
機能レベルのアクセス制御の欠如
もう一つ非常によくある脆弱性が、オブジェクトではなく、ページやエンドポイントに対する認可チェックの欠如です。
使用するフレームワークにもよりますが、開発者がハンドラー内で認可チェックを行うか、エンドポイントにアノテーションを付与して呼び出しに必要な要件を指定しなければならないケースが一般的です。
残念ながら、こうした追加の手順は忘れられやすく、それが認可に関する脆弱性を引き起こす主な原因となっています。
推奨事項
デフォルトで「閉じる」設定にする
認証と認可のいずれにおいても、デフォルトを「開く」ではなく「閉じる」設定にすることが重要です。
使用する言語やフレームワークにもよりますが、アプリケーションへのすべてのルートに対し、デフォルトで認証済みセッションと最高レベルの権限を要求するように設定するのがベストプラクティスです。こうすることで、開発者はルートごとの要件を明示的に上書きしなければならなくなります。
cs
// デフォルトの動作としてリクエストを認証し、管理者権限があるかを確認する
[Authenticate]
[Authorize("Admin")]
public class SecureController : Controller
{
}
public class MyController : SecureController
{
// 継承されたAuthorize属性を上書きし、すべてのユーザーがページにアクセスできるようにする
[Authorize("User")]
public Page ShowUserProfile() {
}
// 管理者ユーザーのみがアクセス可能
public Page ShowAdminPage() {
}
// 認証および認可属性を上書きし、自分のみアクセスを許可する
[AllowAnonymous]
public Page ShowLoginPage() {
}
}
サービス内で認可チェックを強制する
データにアクセスする際は、すべてのデータアクセスにおいて、関連するアクセス権限や認可チェックが統一された方法で強制されるようにすることが極めて重要です。これは一般的に、ドメインサービスを使用することで実現されます。
その他の例
以下に、安全な認証・認可と安全でない認証・認可の違いを理解するための短い例をいくつか紹介します。
C# - セキュリティ上の脆弱性あり
認証の欠如
public class AdminController : Controller
{
// 安全でない例: 管理者ページを表示する前にユーザーがログインしているかを確認していない
public Page ShowAdminPage() {
}
}
認可の欠如
[Authenticate]
public class AdminController : Controller
{
// 安全でない例: 管理者ページを表示する前にユーザーの認可を確認していない public Page ShowAdminPage() {
}
}
C# - セキュリティ対策済み
[Authenticate]
[Authorize("Admin")]
public class AdminController : Controller
{
// 安全な例: ユーザーがログインしていることと、管理者ロールを持っていることの両方を確認している
public Page ShowAdminPage() {
}
}
実践的なセキュアコーディング
OWASP Top 10のようなソフトウェアの脆弱性を理解し軽減するためのセキュアコーディングガイドラインを確認しましょう。また、実践的なアプリシミュレーションを通じて学べるトレーニングミッションにもぜひ挑戦してください。