
DX Security — Authorization Bypass 방어 v1.0.0 안내서
PHP 5.6+ · IIS / Apache / Nginx 완전 호환 코어 수정 없음 — 훅으로만 동작
목차
- Authentication vs Authorization
- DXCMS 코어의 현재 방어 수준
- 이 플러그인의 역할
- 설치 방법
- 방어층 상세 설명
- 보안 로그 읽는 법
- 보안 강점 요약
- 개발자 API 가이드
1. Authentication vs Authorization
| 구분 | 질문 | 예 |
|---|---|---|
| Authentication (11번) | 누구인가? | 김철수 |
| Authorization (12번) | 무엇을 할 수 있는가? | 본인 게시글만 삭제 가능 |
로그인에 성공했다고 모든 기능을 사용할 수 있어서는 안 됩니다.
공격 예시:
수평적 권한 상승 (IDOR):
/api/post/100/edit (내 게시글)
/api/post/101/edit (타인 게시글) ← 수정 가능하면 취약
수직적 권한 상승:
/admin/member/delete (일반 회원이 직접 접근)
Mass Assignment:
POST /mypage/update
{ "name": "홍길동", "role": "admin" } ← role 주입
2. DXCMS 코어의 현재 방어 수준
코어가 이미 잘 하는 것
| 항목 | 방어 방식 | 평가 |
|---|---|---|
| 게시글 소유권 | isAdmin() || user_id === member_id |
✅ |
| 댓글 소유권 | isAdmin() || user_id === member_id |
✅ |
| role Allowlist | in_array(['member','manager','admin']) |
✅ |
| mypage 허용 필드 | $upd 배열 명시적 구성 |
✅ |
| 관리자 URL 인증 | Dispatcher::isAdmin() |
✅ |
코어가 하지 않는 것
| 항목 | 위험도 |
|---|---|
| 권한 거부 감사 로그 | 높음 |
| IDOR/수평 권한 상승 탐지 | 높음 |
| 수직 권한 상승 탐지 | 높음 |
| Mass Assignment 위험 필드 차단 | 높음 |
| 일괄 작업 권한 검증 API | 중간 |
| Fail Closed 정책 | 높음 |
| 플러그인 표준 권한 API | 중간 |
3. 이 플러그인의 역할
코어의 권한 로직을 수정하거나 교체하지 않습니다. 코어가 하지 않는 탐지, 로깅, 표준 권한 API를 훅으로 보강합니다.
4. 설치 방법
(DXCMS 루트)/
└── plugins/
└── dx-security-authz/
├── manifest.php
└── plugin.php
관리자 → 플러그인 → dx-security-authz → 활성화
5. 방어층 상세 설명
AZ1. 권한 거부 감사 로그
모든 권한 관련 이벤트를 data/security.log에 기록합니다.
AZ2. IDOR / 수평적 권한 상승 탐지
같은 등급의 다른 사용자 리소스에 접근하는 시도를 탐지합니다.
탐지 방식:
URL/POST의 member_id, user_id, uid 파라미터가
현재 로그인 사용자 ID와 다를 때 → IDOR_SUSPECT 로그
예:
로그인 사용자 ID: 42
GET /api/member?member_id=43
→ IDOR_SUSPECT 로그 기록
WAF: 탐지·로그만 수행
실제 차단: 코어 소유권 검사 + dx_authz_owns() 담당
AZ3. 수직적 권한 상승 탐지
일반 회원이 관리자 기능 접근을 시도하는 패턴을 탐지합니다.
탐지 패턴:
/admin/ 경로 직접 접근
/api/admin/ 직접 접근
?action=admin 파라미터
?role=admin 파라미터
?is_admin=1 파라미터
→ VERTICAL_ESCALATION_ATTEMPT 로그
WAF: 탐지·로그만 수행
실제 차단: 코어 Dispatcher::isAdmin() 담당
AZ4. Mass Assignment 위험 필드 차단
사용자 요청에 보호 필드가 포함되면 해당 필드를 $_POST에서 제거합니다.
보호 필드 목록:
role, is_admin, level, permission, status,
verified, account_type, group_id, point, exp
처리 방식:
권한 상승 패턴 (role=admin, is_admin=1 등) → 차단 + 로그
단순 보호 필드 포함 → 필드 제거 + 로그
적용 대상:
/auth/, /api/member, /mypage, /member/ 경로
POST 요청, 일반 회원만 (관리자 제외)
예:
POST /mypage/update
{ "name": "홍길동", "role": "admin" }
→ role 필드 제거 후 처리
→ MASS_ASSIGNMENT_FIELD 로그
AZ5. 일괄 작업 권한 검증
대량 처리 시 각 항목을 개별적으로 권한 검증합니다.
위험한 방식:
첫 번째 ID 권한 확인 → 나머지 모두 처리
안전한 방식 (dx_authz_check_batch):
ID 1 → 소유자 확인
ID 2 → 소유자 확인
ID 3 → 소유자 확인
→ 하나라도 실패 시 전체 거부
AZ6. Fail Closed
권한 시스템 오류 시 허용이 아닌 거부로 처리합니다.
잘못된 방식 (Fail Open):
권한 확인 실패 → 일단 허용
이 플러그인 (Fail Closed):
dx_authz_exception 훅 발동
→ HTTP 403
→ AUTHZ_EXCEPTION_FAIL_CLOSED 로그
→ exit
AZ7. 플러그인 표준 권한 API
플러그인 개발자가 일관된 방식으로 권한을 검사할 수 있는 표준 함수를 제공합니다.
6. 보안 로그 읽는 법
이벤트 타입
| 타입 | 의미 |
|---|---|
AUTHZ:PRIVILEGE_ESCALATION_BLOCKED |
권한 상승 시도 차단 |
AUTHZ:MASS_ASSIGNMENT_FIELD |
보호 필드 제거 |
AUTHZ:VERTICAL_ESCALATION_ATTEMPT |
수직 권한 상승 시도 탐지 |
AUTHZ:IDOR_SUSPECT |
IDOR 의심 탐지 |
AUTHZ:ACCESS_DENIED_403 |
403 거부 |
AUTHZ:AUTHZ_DENIED_NOT_LOGIN |
미로그인 접근 거부 |
AUTHZ:AUTHZ_DENIED_NOT_ADMIN |
비관리자 접근 거부 |
AUTHZ:AUTHZ_DENIED_NOT_OWNER |
소유권 없는 접근 거부 |
AUTHZ:BATCH_OWNERSHIP_DENIED |
일괄 작업 소유권 거부 |
AUTHZ:FILTER_BLOCKED_FIELD |
필드 필터링으로 차단 |
AUTHZ:AUTHZ_EXCEPTION_FAIL_CLOSED |
권한 예외 — Fail Closed |
로그 예시
# Mass Assignment 차단
[2026-09-03 09:00:01][AUTHZ:PRIVILEGE_ESCALATION_BLOCKED][IP:45.33.x.x][...][UID:42][POST /mypage/update]
field=role value=admin uri=/mypage/update
# IDOR 탐지
[2026-09-03 14:22:10][AUTHZ:IDOR_SUSPECT][IP:58.29.x.x][...][UID:42][GET /api/member]
user_id=42 param=member_id target_id=43 uri=/api/member
# 일괄 작업 소유권 거부
[2026-09-03 16:45:03][AUTHZ:BATCH_OWNERSHIP_DENIED][IP:121.131.x.x][...][UID:42][POST /api/post/bulk]
user_id=42 denied_ids=100,101 context=bulk_delete
7. 보안 강점 요약
| 공격 | 결과 | 방어 레이어 |
|---|---|---|
role=admin POST 주입 |
필드 제거 + 차단 | AZ4 |
is_admin=1 POST 주입 |
필드 제거 + 차단 | AZ4 |
| IDOR (타인 게시글 수정) | 코어 소유권 검사 + IDOR 탐지 로그 | 코어 + AZ2 |
| 수직 권한 상승 시도 | 코어 Dispatcher 차단 + 탐지 로그 | 코어 + AZ3 |
| 일괄 작업 권한 우회 | dx_authz_check_batch()로 개별 검증 |
AZ5 |
| 권한 시스템 오류 | Fail Closed (403) | AZ6 |
| 정상 mypage 수정 | 통과 (보호 필드 없으면 영향 없음) | ✅ |
8. 개발자 API 가이드
로그인 여부 확인
if (!dx_authz_check('login', 0, 0, 'my_plugin')) {
http_response_code(401);
exit(json_encode(array('success' => false, 'message' => '로그인이 필요합니다.')));
}
관리자 권한 확인
if (!dx_authz_check('admin', 0, 0, 'my_plugin_admin')) {
http_response_code(403);
exit(json_encode(array('success' => false, 'message' => '관리자만 사용할 수 있습니다.')));
}
소유권 확인 (게시글 수정 등)
// 게시글 조회
$post = $db->row("SELECT * FROM posts WHERE id=?", array($postId));
if (!$post) { dx_error('게시글을 찾을 수 없습니다.', 404); }
// 소유권 확인
if (!dx_authz_owns($post['member_id'], 'post_edit')) {
dx_error('수정 권한이 없습니다.', 403);
}
// 수정 처리
Mass Assignment 방어
// 허용 필드만 추출
$allowed = array('name', 'email', 'intro', 'phone');
$safe = dx_authz_filter_fields($_POST, $allowed, 'mypage_update');
// 안전한 데이터만 DB 업데이트
$db->updateRow('members', $safe, array('id' => $memberId));
일괄 작업 권한 검증
$ids = isset($_POST['ids']) ? (array)$_POST['ids'] : array();
// 각 게시글의 소유자 조회
$ownerships = array();
foreach ($ids as $id) {
$post = $db->row("SELECT id, member_id FROM posts WHERE id=?", array((int)$id));
if ($post) $ownerships[$post['id']] = (int)$post['member_id'];
}
// 일괄 소유권 검증
if (!dx_authz_check_batch($ids, $ownerships, 'bulk_delete')) {
dx_error('일부 게시글에 대한 권한이 없습니다.', 403);
}
// 삭제 처리
권한 감사 로그 직접 기록
dx_authz_log('MY_PLUGIN_DENIED', 'resource=invoice target_id=' . $invoiceId);
DX Security — Authorization Bypass 방어 v1.0.0 — DesignOneX https://designonex.com
라이선스
디자인원엑스 라이선스 참고
https://designonex.com/notice/view/1787577858493247