ストーリー
高橋アーキテクトが権限マトリクスを広げました。
RBAC(ロールベースアクセス制御)
ユーザーにロール(役割)を割り当て、ロールに対して権限を定義するモデルです。
// RBAC の設計
type Role = "viewer" | "editor" | "admin" | "super_admin";
type Permission = string; // "resource:action" 形式
// ロールと権限のマッピング
const rolePermissions: Record<Role, Permission[]> = {
viewer: [
"articles:read",
"comments:read",
],
editor: [
"articles:read",
"articles:write",
"articles:update",
"comments:read",
"comments:write",
],
admin: [
"articles:read",
"articles:write",
"articles:update",
"articles:delete",
"comments:read",
"comments:write",
"comments:delete",
"users:read",
"users:update",
],
super_admin: [
"**:*", // 全権限
],
};
// ロール階層(上位ロールは下位の権限を継承)
const roleHierarchy: Record<Role, Role[]> = {
viewer: [],
editor: ["viewer"],
admin: ["editor", "viewer"],
super_admin: ["admin", "editor", "viewer"],
};
// 権限チェック
const hasPermission = (userRole: Role, permission: Permission): boolean => {
const allRoles = [userRole, ...roleHierarchy[userRole]];
return allRoles.some(role => {
const permissions = rolePermissions[role];
return permissions.includes(permission) || permissions.includes("**:*");
});
};
// ミドルウェアとして使用
const requirePermission = (permission: Permission) => {
return (req: Request, res: Response, next: NextFunction) => {
if (!hasPermission(req.user.role, permission)) {
return res.status(403).json({ error: "Insufficient permissions" });
}
next();
};
};
app.delete("/api/articles/:id",
authenticate,
requirePermission("articles:delete"),
deleteArticleHandler
);
ABAC(属性ベースアクセス制御)
ユーザー、リソース、環境の属性に基づいて動的にアクセスを制御するモデルです。
// ABAC の設計
interface AccessRequest {
subject: {
userId: string;
role: string;
department: string;
clearanceLevel: number;
};
resource: {
type: string;
id: string;
ownerId: string;
classification: "public" | "internal" | "confidential" | "secret";
};
action: "read" | "write" | "delete";
environment: {
time: Date;
ipAddress: string;
deviceType: "managed" | "unmanaged";
};
}
// ポリシーの定義
interface AbacPolicy {
name: string;
description: string;
condition: (req: AccessRequest) => boolean;
effect: "allow" | "deny";
}
const policies: AbacPolicy[] = [
{
name: "所有者は自分のリソースを編集可能",
description: "リソースの所有者はread/write可能",
condition: (req) =>
req.subject.userId === req.resource.ownerId &&
["read", "write"].includes(req.action),
effect: "allow",
},
{
name: "機密データは営業時間内のみアクセス可能",
description: "confidential以上は9-18時のみ",
condition: (req) => {
const hour = req.environment.time.getHours();
return (
["confidential", "secret"].includes(req.resource.classification) &&
(hour < 9 || hour >= 18)
);
},
effect: "deny",
},
{
name: "秘密情報はクリアランスレベル3以上のみ",
description: "secret分類はclearanceLevel >= 3が必要",
condition: (req) =>
req.resource.classification === "secret" &&
req.subject.clearanceLevel < 3,
effect: "deny",
},
{
name: "非管理端末からの書き込み禁止",
description: "unmanaged端末はreadのみ許可",
condition: (req) =>
req.environment.deviceType === "unmanaged" &&
req.action !== "read",
effect: "deny",
},
];
// ポリシー評価エンジン
const evaluateAccess = (request: AccessRequest): boolean => {
// deny-first: 1つでもdenyがあれば拒否
for (const policy of policies) {
if (policy.condition(request) && policy.effect === "deny") {
console.log(`Access denied by policy: ${policy.name}`);
return false;
}
}
// 明示的なallowがあるか確認
const allowed = policies.some(
p => p.condition(request) && p.effect === "allow"
);
return allowed;
};
RBAC vs ABAC の比較
| 項目 | RBAC | ABAC |
|---|---|---|
| 複雑さ | シンプル | 複雑 |
| 柔軟性 | 低い(ロール単位) | 高い(属性の組み合わせ) |
| 管理コスト | ロール数に比例 | ポリシー数に比例 |
| 適用場面 | ロールが明確なシステム | 動的・細粒度の制御が必要なシステム |
| デバッグ | 容易 | 困難(ポリシーの競合) |
| パフォーマンス | 高速 | ポリシー評価にコストがかかる |
ハイブリッドアプローチ
// RBAC + ABAC のハイブリッド
const checkAccess = async (req: Request, permission: string): Promise<boolean> => {
// Step 1: RBAC チェック(粗い粒度)
if (!hasPermission(req.user.role, permission)) {
return false;
}
// Step 2: ABAC チェック(細かい粒度)
const accessRequest: AccessRequest = {
subject: {
userId: req.user.id,
role: req.user.role,
department: req.user.department,
clearanceLevel: req.user.clearanceLevel,
},
resource: await getResourceAttributes(req.params.id),
action: methodToAction(req.method),
environment: {
time: new Date(),
ipAddress: req.ip,
deviceType: req.headers["x-device-trust"] as "managed" | "unmanaged",
},
};
return evaluateAccess(accessRequest);
};
選択の指針
| ユースケース | 推奨モデル |
|---|---|
| シンプルなWeb管理画面 | RBAC |
| 企業の基幹業務システム | RBAC + ABAC ハイブリッド |
| 医療・金融の機密データ管理 | ABAC |
| マルチテナントSaaS | RBAC + テナント属性 |
まとめ
| ポイント | 内容 |
|---|---|
| RBAC | ロールに権限を付与、シンプルで管理しやすい |
| ABAC | 属性の組み合わせで動的に制御、柔軟だが複雑 |
| ハイブリッド | RBACで粗い制御 + ABACで細かい制御 |
| 選択基準 | システムの複雑さと要件に応じて選択 |
チェックリスト
- RBACのロール階層と権限マッピングを理解した
- ABACのポリシー定義と評価エンジンを理解した
- RBAC vs ABACの使い分けを把握した
- ハイブリッドアプローチの設計方法を理解した
次のステップへ
次は「マルチテナントセキュリティ」を学びます。複数の組織が同じシステムを共有する場合の、データ分離と認可の設計を身につけましょう。
推定読了時間: 30分