RBAC 인가
규칙
- 모든 라우트에 미들웨어를 사용하여 권한 확인 — 인가를 선택 사항으로 만들지 마세요
- 기본적으로 거부: 명시적 권한이 부여되지 않은 경우 접근이 거부됩니다
- 서버 측 강제: UI 요소 숨기기는 보안이 아닙니다. 서버에서 확인하세요
- 역할 계층 정의: admin > editor > viewer — 상위 역할은 하위 권한을 상속받습니다
- 리소스 수준 확인:
can(user, "edit", resource) 확인 — 역할 기반 접근만으로는 충분하지 않습니다
- 모든 권한 확인 감사 — 누가, 언제, 무엇에 접근했는지 기록하고, 특히 관리자 작업에 대해 기록하세요
- 인증(당신은 누구인가?)과 인가(무엇을 할 수 있는가?)를 분리하세요
type Action = "read" | "create" | "update" | "delete";
type Resource = { ownerId: string; [key: string]: any };
function can(user: { id: string; role: string }, action: Action, resource?: Resource): boolean {
const permissions: Record<string, Action[]> = {
admin: ["read", "create", "update", "delete"],
editor: ["read", "create", "update"],
viewer: ["read"],
};
const allowed = permissions[user.role];
if (!allowed?.includes(action)) return false;
if (resource && action !== "read" && user.role !== "admin") {
return resource.ownerId === user.id;
}
return true;
}
function requirePermission(action: Action) {
return (req, res, next) => {
if (!req.user) return res.status(401).json({ error: "인증되지 않음" });
if (!can(req.user, action, req.resource)) {
return res.status(403).json({ error: "금지됨" });
}
next();
};
}
app.put("/api/posts/:id", loadResource, requirePermission("update"), updatePost);
피해야 할 사항
- UI에서만 권한 확인 — 공격자가 API를 직접 호출합니다
- 명시적으로 거부되지 않으면 접근 허용 — 항상 기본적으로 거부하세요
- 단일
isAdmin 부울 값 사용 — 역할은 세분화되고 확장 가능해야 합니다
- 리소스 수준 확인 누락 — "편집자"가 "모든 사람의 콘텐츠를 편집할 수 있음"을 의미해서는 안 됩니다
- "내부" 엔드포인트에서 인가 생략 — 결국 공개됩니다