
DX Security — Cookie Attribute 보안 v1.0.0 안내서
DesignOneX CMS Cookie 보안 기업 납품 수준 강화 플러그인
PHP 5.6+ · IIS / Apache / Nginx · 공유호스팅 완전 호환
코어(Auth.php / Secure.php / session_extend.php) 수정 없음 — 훅으로만 동작
기업 납품 요구사항 COOKIE-001~008 완전 준수
OWASP Session Management Cheat Sheet 준수
목차
- Cookie 보안이 왜 중요한가
- DXCMS 코어의 현재 Cookie 보안 수준
- 코어의 실제 버그와 미비점
- 설치 방법
- 방어층 상세 설명
- CA1. Cookie Policy Registry — 중앙 정책 관리
- CA2. dx_remember SameSite 통일
- CA3. 삭제 시 Secure=false 버그 수정
- CA4. csrf_refresh 세션 쿠키 속성 통일
- CA5. __Host- Cookie Prefix 지원
- CA6. 로그아웃 시 모든 인증 Cookie 완전 제거
- CA7. Cookie Injection 방어 — 입력값 검증
- CA8. 보안 감사 로그
- 훅 실행 타이밍 전체 흐름
- 보안 로그 읽는 법
- Cookie Inventory — 기업 보안감사 대응
- 보안 강점 요약 — 기업 납품 요구사항 준수 현황
- 방어하지 못하는 영역
- __Host- Prefix 활성화 방법
- 개발자 연동 가이드 — 표준 함수 사용법
- 자주 묻는 질문
1. Cookie 보안이 왜 중요한가
Session ID, Remember Me Token 등 인증 정보는 Cookie를 통해 브라우저와 서버 사이를 오갑니다. Cookie 속성 하나의 누락이 치명적인 보안 취약점으로 이어집니다.
Cookie 속성별 방어 대상
Secure — HTTPS에서만 전송
→ 네트워크 도청(패킷 스니핑)으로 Cookie 탈취 방어
→ HTTP 평문 구간에서 Cookie 노출 방지
HttpOnly — JavaScript 접근 차단
→ XSS 공격으로 document.cookie 읽기 방어
→ 악성 스크립트가 Session ID 탈취 시도 방어
SameSite — Cross-Site 요청에서 Cookie 전송 제한
→ CSRF 공격의 보조 방어층
→ Lax: 최상위 GET 탐색은 허용, Cross-Site POST는 차단
→ Strict: 모든 Cross-Site 요청 차단
Path — Cookie 전송 경로 제한
→ /admin Path Cookie가 /board에서 전송되지 않도록
Domain — Cookie 전송 도메인 제한
→ 미지정 시 현재 Host만 (Subdomain 제외)
→ 지정 시 Subdomain 포함 → Cookie Shadowing 위험
__Host- — 브라우저 레벨 Secure+Domain없음+Path=/ 강제
→ Subdomain 공격으로 Cookie 덮어쓰기 방어
Cookie 속성 하나 누락의 실제 위험
Secure 없음:
HTTP 요청에서도 Cookie 전송
→ 공개 Wi-Fi에서 세션 탈취 가능
HttpOnly 없음:
<script>document.cookie</script>로 Session ID 탈취
→ XSS 1개로 계정 장악
SameSite 없음:
Cross-Site POST 요청에 Cookie 자동 첨부
→ CSRF Token 없이도 CSRF 가능 (구버전 브라우저)
삭제 시 Secure 불일치:
set: Secure Cookie
delete: Non-Secure Cookie
→ 브라우저가 다른 Cookie로 인식 → 실제 삭제 안 됨
2. DXCMS 코어의 현재 Cookie 보안 수준
코어 Secure.php initSession()을 분석한 결과입니다.
세션 Cookie (코어 initSession())
// Secure.php 실제 코드
@ini_set('session.use_only_cookies', 1); // URL 세션 금지
@ini_set('session.cookie_httponly', 1); // HttpOnly
@ini_set('session.cookie_path', '/'); // Path=/
@ini_set('session.cookie_lifetime', 86400); // 24시간
@ini_set('session.cookie_secure', 1); // Secure (HTTPS)
@ini_set('session.cookie_samesite', 'Lax'); // PHP 7.3+
@ini_set('session.use_strict_mode', 1); // PHP 7.1+
| 속성 | 상태 | 평가 |
|---|---|---|
| HttpOnly | ✅ | 완벽 |
| Secure | ✅ HTTPS 자동 | 완벽 |
| SameSite=Lax | ✅ PHP 7.3+ | 완벽 |
| use_only_cookies | ✅ | URL 세션 차단 |
| use_strict_mode | ✅ PHP 7.1+ | 미발급 세션 거부 |
| Path=/ | ✅ | 완벽 |
| Domain | ✅ 미지정 | 현재 Host만 |
| __Host- Prefix | ❌ | 이 플러그인 선택 적용 |
세션 Cookie 자체 평가: 9/10 — 매우 우수
Remember Me Cookie (코어 Auth.php)
코어에서 dx_remember 쿠키를 발급하는 코드:
// Auth.php — setcookie 실제 코드
setcookie('dx_remember', $userId.':'.$newToken,
time() + 2592000, '/', '', $isHttps, true);
// ↑Path ↑Domain ↑Secure ↑HttpOnly
// SameSite 속성 없음 ←──────────────────────────────────────
| 속성 | 상태 | 평가 |
|---|---|---|
| HttpOnly | ✅ | 완벽 |
| Secure | ✅ HTTPS | 완벽 |
| SameSite | ❌ 없음 | 이 플러그인 추가 |
| Path=/ | ✅ | 완벽 |
| Domain | ✅ 미지정 | 완벽 |
삭제 코드의 치명적 버그
// Auth.php — 쿠키 삭제 실제 코드 (여러 곳)
setcookie('dx_remember', '', time() - 3600, '/', '', false, true);
// ↑
// Secure=false 하드코딩!
// HTTPS 환경에서도 false
HTTPS 환경에서 발생하는 실제 문제:
발급: Set-Cookie: dx_remember=42:abc...; Secure; HttpOnly
삭제: Set-Cookie: dx_remember=; Secure 없음; HttpOnly
브라우저 판단:
Secure Cookie와 Non-Secure Cookie는 다른 Cookie
→ 삭제 요청이 발급된 쿠키에 적용되지 않음
→ 로그아웃해도 dx_remember 쿠키가 브라우저에 남음
→ 자동 로그인 쿠키가 만료(30일)까지 브라우저에 유지
3. 코어의 실제 버그와 미비점
코어 소스를 직접 분석한 결과입니다.
버그 1: dx_remember 삭제 시 Secure=false 하드코딩
// Auth.php의 tryRememberMe(), login(), loginById(), logout()
// 모든 삭제 경로에서 동일하게 발생
setcookie('dx_remember', '', time() - 3600, '/', '', false, true);
// ↑ HTTPS에서도 false
영향: HTTPS 사이트에서 로그아웃해도 Remember Me 쿠키가 30일간 브라우저에 남음
버그 2: session_extend.php의 삭제도 동일 버그
// session_extend.php — 5곳 모두 동일
setcookie($cookieName, '', time() - 3600, '/', '', false, true);
미비점 1: dx_remember에 SameSite 없음
발급 경로 4곳 모두:
Auth::login() → setcookie(..., $isHttps, true) // SameSite 없음
Auth::loginById() → setcookie(..., $isHttps, true) // SameSite 없음
Auth::tryRememberMe() → setcookie(..., $isHttps, true) // SameSite 없음
session_extend.php → setcookie(..., $isHttps, true) // SameSite 없음
미비점 2: csrf_refresh.php 세션 쿠키 직접 재발급
// csrf_refresh.php
@setcookie(session_name(), session_id(), time() + 86400, '/', '', $isHttps, true);
// SameSite 없음 — Secure.php initSession()의 설정과 별개로 동작
Heartbeat(30초마다 호출)가 SameSite 없는 세션 쿠키를 재발급하여 브라우저의 세션 쿠키 속성을 덮어씁니다.
미비점 3: Cookie 정책 중앙 관리 없음
setcookie() 호출 위치:
Auth.php — 9곳
session_extend.php — 5곳
csrf_refresh.php — 1곳
합계: 15곳에 분산
각각 다른 속성으로 Cookie를 발급하면 정책 불일치가 발생하고, 코드 리뷰에서 누락을 확인하기 어렵습니다.
4. 설치 방법
4-1. 파일 배치
(DXCMS 루트)/
└── plugins/
└── dx-security-cookie/
├── manifest.php
└── plugin.php
4-2. 관리자 페이지에서 활성화
관리자 → 플러그인 → dx-security-cookie → 활성화
활성화 즉시 모든 이후 로그인·로그아웃·Remember Me 요청에 올바른 Cookie 속성이 적용됩니다.
4-3. 권장 설치 순서
1. dx-security-guard (Session Fixation, Timeout)
2. dx-security-hijacking (Session Hijacking, Reuse Detection)
3. dx-security-csrf (CSRF 방어)
4. dx-security-cookie (이 플러그인 — Cookie 속성 통합)
단독 설치도 가능합니다.
5. 방어층 상세 설명
CA1. Cookie Policy Registry — 중앙 정책 관리
목적
분산된 15곳의 setcookie() 호출을 단일 정책으로 통합합니다.
Cookie 보안 등급 (기획서 §4.2)
| 등급 | 용도 | 예 | 적용 |
|---|---|---|---|
| C0 | 인증 핵심 | Session Cookie | 최고 보안 필수 |
| C1 | 장기 인증 | Remember Me | 최고 보안 필수 |
| C2 | 보안 토큰 | CSRF 토큰 (세션 저장) | 해당 없음 |
| C3 | 기능 | UI 설정 | 중간 보안 |
| C4 | 분석 | 통계 | 최소 보안 |
Registry 구조
$GLOBALS['_dx_cookie_registry'] = array(
'session' => array(
'grade' => 0, // C0 — 인증 핵심
'purpose' => '세션 식별',
'httponly' => true,
'secure' => 'auto', // HTTPS 자동
'samesite' => 'Lax',
'path' => '/',
'domain' => '', // 미지정 = 현재 Host만
'max_age' => 86400, // 24시간
'sensitive' => true, // 값을 로그에 기록하지 않음
),
'remember' => array(
'grade' => 1, // C1 — 장기 인증
'purpose' => '자동 로그인 토큰',
'name' => 'dx_remember',
'httponly' => true,
'secure' => 'auto',
'samesite' => 'Lax', // 코어 미적용 → 이 플러그인이 적용
'path' => '/',
'domain' => '',
'max_age' => 2592000, // 30일
'sensitive' => true,
),
);
기업 보안감사 대응
감사관: "시스템에서 사용하는 모든 Cookie를 목록화할 수 있습니까?"
↓
dx_cookie_inventory() 함수 호출
↓
Cookie 이름·목적·등급·보안 속성 전체 즉시 출력
CA2. dx_remember SameSite 통일
문제
코어의 모든 Remember Me 쿠키 발급 경로에 SameSite 속성이 없습니다. Session Cookie에는 SameSite=Lax가 있지만 Remember Me Cookie에는 없어 정책이 불일치합니다.
동작
dx_after_login 훅(priority 8)에서 코어가 발급한 쿠키를 SameSite=Lax 속성으로 즉시 덮어씁니다.
코어 Auth::login()
→ setcookie('dx_remember', value, ...) // SameSite 없음
→ dx_after_login 훅 실행
→ 이 플러그인 (priority 8)
→ _dx_ca_set_remember() 호출
→ setcookie('dx_remember', 동일값, ... SameSite=Lax)
→ 브라우저: SameSite=Lax 쿠키로 최종 적용
PHP 버전별 처리
// PHP 7.3+ — samesite 파라미터 직접 지원
setcookie('dx_remember', $value, [
'expires' => time() + 2592000,
'samesite' => 'Lax', // 네이티브 지원
'secure' => $isHttps,
'httponly' => true,
]);
// PHP 5.6~7.2 — path에 직접 주입하는 호환 방식
setcookie('dx_remember', $value,
time() + 2592000,
'/; SameSite=Lax', // path에 주입
'', // domain 없음
$isHttps,
true
);
CA3. 삭제 시 Secure=false 버그 수정
문제 (코드 레벨)
// 코어 Auth.php — 발급
setcookie('dx_remember', $userId.':'.$token, time()+2592000, '/', '', $isHttps, true);
// ↑Secure=HTTPS
// 코어 Auth.php — 삭제 (버그)
setcookie('dx_remember', '', time()-3600, '/', '', false, true);
// ↑Secure=false 항상!
HTTPS 환경에서 실제로 발생하는 현상
브라우저 Cookie 저장소:
dx_remember (Secure) = "42:abc..." ← 로그인 시 발급된 쿠키
dx_remember (Non-Secure)= "" ← 삭제 요청 (다른 쿠키!)
결과:
삭제 요청이 적용되지 않음
브라우저에는 여전히 Secure Cookie 유지
→ 로그아웃해도 자동 로그인 쿠키 30일간 유지
이 플러그인의 수정
dx_after_logout 훅(priority 8)에서 올바른 속성으로 덮어씁니다.
// 이 플러그인 — 발급과 동일한 Secure 속성으로 삭제
_dx_ca_set_cookie('dx_remember', '', time() - 86400);
// → Secure=$isHttps (현재 HTTPS 상태 자동 반영)
// → SameSite=Lax
// → HttpOnly=true
// → Path=/
CA4. csrf_refresh.php 세션 쿠키 속성 통일
문제
csrf_refresh.php는 글쓰기 중 Heartbeat(30초마다)로 세션 수명을 연장할 때 세션 쿠키를 직접 재발급합니다.
// csrf_refresh.php
@setcookie(session_name(), session_id(), time() + 86400, '/', '', $isHttps, true);
// SameSite 없음 — 30초마다 이 쿠키로 덮어쓰여짐
30초마다 SameSite 없는 세션 쿠키가 재발급되어 브라우저의 세션 쿠키 속성이 덮어씌워집니다.
이 플러그인의 수정
dx_after_csrf_refresh 훅(코어에 추가 필요)에서 올바른 속성으로 재설정합니다.
// _dx_ca_refresh_session_cookie()
setcookie(session_name(), session_id(), time()+86400, array(
'samesite' => 'Lax',
'secure' => $isHttps,
'httponly' => true,
'path' => '/',
'domain' => '', // Domain 미지정 강제
));
코어 연동 필요:
csrf_refresh.php에dx_run_hook('dx_after_csrf_refresh', [])추가가 필요합니다.
CA5. __Host- Cookie Prefix 지원
__Host- Prefix란
브라우저가 다음 3가지 조건을 강제합니다:
__Host-로 시작하는 Cookie는:
① Secure 속성 필수 (HTTP에서 발급 자체 거부)
② Domain 속성 금지 (있으면 브라우저가 저장 거부)
③ Path=/ 필수
결과:
Subdomain에서 절대 Cookie 덮어쓰기 불가
→ Cookie Shadowing 공격 완전 방어
Subdomain Cookie Shadowing 공격이란
공격자가 attacker.example.com 을 탈취
↓
Set-Cookie: DXSESSID=악성값; Domain=example.com; Path=/
↓
example.com의 세션 쿠키를 공격자 값으로 덮어씀
↓
Session Fixation 공격 성공
__Host-DXSESSID 사용 시:
Domain 속성 있으면 브라우저가 저장 자체를 거부
→ 덮어쓰기 불가능
활성화 방법
plugin.php에서:
define('DX_COOKIE_HOST_PREFIX', true); // false → true로 변경
중요 주의사항:
- HTTPS 환경에서만 동작 (HTTP에서는 브라우저가 거부)
- 변경 시 기존 로그인 세션 전부 무효화 → 유지보수 시간에 변경
- 기존 설치: Migration 후 변경 권장
CA6. 로그아웃 시 모든 인증 Cookie 완전 제거
기업 납품 원칙 (기획서 §4.19)
로그아웃 시 삭제해야 할 Cookie:
① 세션 Cookie (코어 Auth::logout() → Security Guard 플러그인)
② Remember Me Cookie (이 플러그인)
삭제 원칙:
발급 시와 동일한 Path / Domain / Secure / SameSite / HttpOnly 속성 사용
이 플러그인의 보장
dx_after_logout 훅 (priority 8)
↓
_dx_ca_clear_remember($userId)
↓
_dx_ca_set_cookie('dx_remember', '', time()-86400)
↓
Secure=$isHttps (HTTPS 자동 감지)
SameSite=Lax
HttpOnly=true
Path=/
Domain='' (미지정)
↓
CA:REMEMBER_CLEARED_LOGOUT 로그 기록
CA7. Cookie Injection 방어 — 입력값 검증
공격 원리
공격자가 직접 조작한 Cookie 값을 전송:
Cookie: dx_remember=1:INVALID\r\nSet-Cookie: admin=1
↑ 헤더 인젝션 시도
검증 항목 (매 요청 dx_extend_top priority 1)
① 길이: 최대 128자 (userId 10자 + ':' + token 64자 + 여유)
② 제어문자: \x00~\x1f, \r, \n, ;, ,, \\ 없음
③ 형식: "양의정수:64자리소문자16진수"
④ userId: 양의 정수이며 변환 손실 없음
⑤ token: 정확히 [0-9a-f]{64} 패턴
비정상 쿠키 발견 시
1. 즉시 삭제 (_dx_ca_clear_remember())
2. $_COOKIE['dx_remember'] 제거 (현재 요청에서 차단)
3. CA:COOKIE_INJECTION_BLOCKED 로그 기록
4. Auth.php에 도달하기 전에 차단 완료
정상 token 패턴 예시
[0-9a-f]{64}
예: a8f9c2d1e0b3f456789abcdef0123456789abcdef0123456789abcdef012345
CA8. 보안 감사 로그
기업 보안감사에서 Cookie 관련 이벤트 추적이 가능합니다.
| 이벤트 타입 | 발생 시점 |
|---|---|
CA:REMEMBER_ISSUED |
Remember Me 쿠키 발급 (SameSite 포함) |
CA:REMEMBER_CLEARED |
Remember Me 쿠키 삭제 |
CA:REMEMBER_CLEARED_LOGOUT |
로그아웃 시 올바른 속성으로 쿠키 삭제 |
CA:REMEMBER_SAMESITE_APPLIED |
로그인 후 SameSite 속성 적용 |
CA:SESSION_COOKIE_REFRESHED |
csrf_refresh 후 세션 쿠키 속성 통일 |
CA:COOKIE_INJECTION_BLOCKED |
비정상 쿠키 형식 감지 및 차단 |
CA:COOKIE_AUDIT_WARN |
HTTP 전송 인증 쿠키 감지 |
CA:HOST_PREFIX_SET |
__Host- Prefix 세션 이름 변경 완료 |
6. 훅 실행 타이밍 전체 흐름
[모든 요청]
↓
index.php DxExtend::runTop()
└── dx_extend_top 훅
├── priority 1: dx-security-cookie [CA7] Cookie Injection 검증 ← 이 플러그인
│ (Auth보다 먼저 — 비정상 쿠키 차단)
├── priority 1: dx-security-guard (session_version 검증)
├── priority 2: dx-security-hijacking (Session Guard)
├── priority 3: dx-security-hijacking (관리자 재인증)
├── priority 5: dx-security-csrf (CSRF Pipeline)
└── priority 10: dx-security-cookie [CA8] Cookie 감사 ← 이 플러그인
[로그인 성공]
Auth::login() → dx_after_login
├── priority 1,2: dx-security-guard (세션 ID 재생성)
├── priority 5: dx-security-hijacking (Risk 초기화)
├── priority 8: dx-security-cookie [CA2] SameSite 적용 ← 이 플러그인
└── priority 10: dx-security-csrf (CSRF 토큰 Rotation)
[로그아웃]
Auth::logout() → dx_after_logout
├── priority 1: dx-security-guard (세션 파기)
├── priority 5: dx-security-hijacking (Remember 쿠키 삭제)
├── priority 8: dx-security-cookie [CA3][CA6] 올바른 속성으로 삭제 ← 이 플러그인
└── priority 10: dx-security-csrf (CSRF 토큰 폐기)
[csrf_refresh (Heartbeat)]
→ dx_after_csrf_refresh (코어 추가 필요)
└── priority 1: dx-security-cookie [CA4] 세션 쿠키 속성 통일 ← 이 플러그인
7. 보안 로그 읽는 법
data/security.log에 기록됩니다.
로그 형식
[날짜 시각][CA:이벤트타입][IP:주소][UA:유저에이전트] 메시지
로그 예시 및 해석
# 정상 로그인 — SameSite 적용 완료
[2026-09-01 09:00:01][CA:REMEMBER_SAMESITE_APPLIED][IP:121.131.x.x][UA:Mozilla/5.0...]
user_id=42 login — SameSite=Lax applied
# 로그아웃 — 올바른 속성으로 삭제 (Secure=yes)
[2026-09-01 18:30:22][CA:REMEMBER_CLEARED_LOGOUT][IP:121.131.x.x][UA:Mozilla/5.0...]
user_id=42 proper-secure-delete applied
# Cookie Injection 시도 차단
[2026-09-01 14:22:10][CA:COOKIE_INJECTION_BLOCKED][IP:45.33.x.x][UA:python-requests/2.28]
reason=INVALID_TOKEN_FORMAT val_len=200
# Cookie 형식 오류 (공격 도구가 비정상 형식 사용)
[2026-09-01 16:45:03][CA:COOKIE_INJECTION_BLOCKED][IP:58.29.x.x][UA:curl/7.68.0]
reason=INVALID_CHARS val_len=85
# HTTP 환경에서 인증 쿠키 감지 경고
[2026-09-01 20:11:44][CA:COOKIE_AUDIT_WARN][IP:121.131.x.x][UA:Mozilla/5.0...]
issues=SESSION_ON_HTTP https=no
⚠️ Cookie 값 원문은 절대 로그에 기록하지 않습니다. (COOKIE-008 준수)
8. Cookie Inventory — 기업 보안감사 대응
기업 보안감사 질문
"시스템에서 사용하는 모든 Cookie를 목록화할 수 있습니까?"
"각 Cookie의 보안 속성이 정책을 준수합니까?"
"민감 정보를 Cookie에 저장하지 않습니까?"
Cookie Inventory 출력
// 관리자 코드 또는 보안감사 전용 페이지에서
$inventory = dx_cookie_inventory();
foreach ($inventory as $cookie) {
echo $cookie['name'] . ': ';
echo 'Secure=' . $cookie['secure'] . ' ';
echo 'HttpOnly=' . $cookie['httponly'] . ' ';
echo 'SameSite=' . $cookie['samesite'];
echo PHP_EOL;
}
출력 예시 (DXCMS v9.0.0 + 이 플러그인)
| Cookie | 목적 | 등급 | Secure | HttpOnly | SameSite | Domain | Path | 만료 |
|---|---|---|---|---|---|---|---|---|
| DXSESSID | 세션 식별 | C0 | ✅ HTTPS | ✅ | Lax | 없음 | / | 24시간 |
| dx_remember | 자동 로그인 | C1 | ✅ HTTPS | ✅ | Lax (이 플러그인 추가) | 없음 | / | 30일 |
9. 보안 강점 요약 — 기업 납품 요구사항 준수 현황
기업 납품 MUST NOT 요구사항 (COOKIE-001~008)
| 요구사항 | 내용 | 코어 | 이 플러그인 | 상태 |
|---|---|---|---|---|
| COOKIE-001 | 인증 Cookie HttpOnly 제거 금지 | ✅ | ✅ 유지 | ✅ PASS |
| COOKIE-002 | HTTPS 운영환경 Secure 제거 금지 | ✅ | ✅ 삭제버그 수정 | ✅ PASS |
| COOKIE-003 | SameSite=None 임의 사용 금지 | ✅ | ✅ | ✅ PASS |
| COOKIE-004 | Domain=상위도메인 임의 설정 금지 | ✅ | ✅ Domain='' 강제 | ✅ PASS |
| COOKIE-005 | Session ID를 localStorage로 이동 금지 | ✅ | ✅ | ✅ PASS |
| COOKIE-006 | Session ID를 URL에 포함 금지 | ✅ use_only_cookies | ✅ | ✅ PASS |
| COOKIE-007 | 민감정보 Cookie 평문 저장 금지 | ✅ Opaque Token | ✅ | ✅ PASS |
| COOKIE-008 | Cookie Token 로그 평문 기록 금지 | ✅ | ✅ 준수 | ✅ PASS |
기업 납품 평가 배점 (기획서 §4.31)
| 평가 영역 | 배점 | 코어 | 이 플러그인 | 합계 |
|---|---|---|---|---|
| Secure | 10 | 10 | — | 10 |
| HttpOnly | 10 | 10 | — | 10 |
| SameSite | 10 | 7 (세션만) | +3 (remember 추가) | 10 |
| Domain 제한 | 10 | 10 | — | 10 |
| Path 제한 | 5 | 5 | — | 5 |
| Cookie Lifetime | 10 | 8 | — | 8 |
| Cookie Prefix | 10 | 0 | 10 (opt-in) | 10 |
| Session Cookie 보호 | 10 | 10 | — | 10 |
| Remember Me 보호 | 10 | 7 (버그있음) | +3 (버그수정) | 10 |
| Cookie 중앙관리 | 5 | 0 | 5 | 5 |
| Cookie Inventory | 5 | 0 | 5 | 5 |
| 자동 보안 테스트 | 5 | 0 | 3 | 3 |
| 총점 | 100 | 67 | +29 | 96 |
10. 방어하지 못하는 영역
| 취약점 | 이유 | 추가 대책 |
|---|---|---|
| XSS로 Cookie 탈취 | HttpOnly가 있어도 간접 방법 존재 | XSS 플러그인(8번), CSP |
| HTTP 환경의 Secure Cookie 미동작 | 환경적 한계 | HTTPS 강제 적용 |
| __Host- 미지원 구버전 브라우저 | IE 미지원 | CA5를 사용하지 않으면 영향 없음 |
| Cookie 도용 후 즉각 감지 | 수동적 방어 | Session Guard(플러그인 2)와 병행 |
11. __Host- Prefix 활성화 방법
활성화 조건 확인
필수: HTTPS 운영 환경
권장: 신규 설치 또는 유지보수 시간대
주의: 기존 로그인 세션 전부 무효화됨
활성화 절차
// 1. plugin.php에서 상수 변경
define('DX_COOKIE_HOST_PREFIX', true); // false → true
// 2. 저장 후 관리자 페이지에서 플러그인 재활성화
// 3. 기존 사용자에게 공지:
// "보안 업데이트로 자동 로그아웃됩니다. 다시 로그인하세요."
활성화 후 세션 쿠키 이름
변경 전: PHPSESSID (또는 DXSESSID)
변경 후: __Host-DXSESSID
브라우저 지원 현황
| 브라우저 | __Host- 지원 |
|---|---|
| Chrome 49+ | ✅ |
| Firefox 75+ | ✅ |
| Safari 12+ | ✅ |
| Edge 79+ | ✅ |
| IE 모든 버전 | ❌ |
IE 사용자가 있는 환경에서는 DX_COOKIE_HOST_PREFIX=false 유지를 권장합니다.
12. 개발자 연동 가이드 — 표준 함수 사용법
이 플러그인은 Cookie 발급·삭제를 위한 표준 함수를 제공합니다. 새로 개발하는 플러그인에서는 코어의 setcookie()를 직접 호출하는 대신 이 함수들을 사용하세요.
Remember Me 쿠키 발급
// 기존 방식 (코어) — SameSite 없음
setcookie('dx_remember', $userId.':'.$token, time()+2592000, '/', '', $isHttps, true);
// 권장 방식 (이 플러그인) — Policy Registry 자동 적용
dx_set_remember_cookie($userId, $token); // 기본 30일
dx_set_remember_cookie($userId, $token, 604800); // 7일로 단축
Remember Me 쿠키 삭제
// 기존 방식 (코어) — Secure=false 버그
setcookie('dx_remember', '', time()-3600, '/', '', false, true);
// 권장 방식 (이 플러그인) — 올바른 속성으로 삭제
dx_clear_remember_cookie($userId);
Cookie Inventory 출력
// 관리자 보안 페이지에서
$inventory = dx_cookie_inventory();
echo '<table>';
echo '<tr><th>Cookie</th><th>목적</th><th>Secure</th><th>HttpOnly</th><th>SameSite</th></tr>';
foreach ($inventory as $c) {
echo '<tr>';
echo '<td>' . $c['name'] . '</td>';
echo '<td>' . $c['purpose'] . '</td>';
echo '<td>' . $c['secure'] . '</td>';
echo '<td>' . $c['httponly'] . '</td>';
echo '<td>' . $c['samesite'] . '</td>';
echo '</tr>';
}
echo '</table>';
Registry에 새 Cookie 등록
플러그인에서 새 Cookie를 발급할 때 Registry에 등록하면 보안감사 자동 포함됩니다.
// 다른 플러그인에서 Registry 확장
add_action('dx_extend_top', function() {
if (!isset($GLOBALS['_dx_cookie_registry'])) return;
$GLOBALS['_dx_cookie_registry']['my_plugin_pref'] = array(
'grade' => 3, // C3 — 기능 Cookie
'purpose' => '내 플러그인 UI 설정',
'name' => 'dx_myplugin_pref',
'httponly' => false, // JS가 읽어야 함
'secure' => 'auto',
'samesite' => 'Strict',
'path' => '/',
'domain' => '',
'max_age' => 31536000, // 1년
'sensitive'=> false,
);
});
13. 자주 묻는 질문
Q. 코어에 이미 HttpOnly, Secure, SameSite가 있는데 이 플러그인이 필요한가요?
세션 Cookie 속성은 코어가 잘 처리하고 있습니다. 이 플러그인은 3가지 핵심 문제를 해결합니다: ① dx_remember 쿠키에 SameSite 없음, ② 삭제 시 Secure=false 하드코딩 버그, ③ csrf_refresh.php가 SameSite 없이 세션 쿠키 재발급. 기업 납품 보안감사에서 이 3가지는 반드시 지적됩니다.
Q. dx_remember 삭제 버그가 실제로 문제가 되나요?
HTTPS 사이트에서 로그아웃해도 Remember Me 쿠키가 브라우저에 30일간 남습니다. 공용 컴퓨터에서 로그아웃 후 자동 로그인이 여전히 가능한 상태가 됩니다. 이 플러그인 설치 후 즉시 수정됩니다.
Q. __Host- Prefix를 활성화하면 Remember Me도 변경되나요?
현재 구현에서 __Host- Prefix는 세션 Cookie(session_name())에만 적용됩니다. dx_remember는 별도 마이그레이션이 필요하므로 현재 버전에서는 적용하지 않습니다. 향후 버전에서 __Host-DXREMEMBER 지원이 추가될 예정입니다.
Q. Cookie Injection 검증이 정상 사용자를 차단하지 않나요?
정상 쿠키는 코어 Auth.php가 Secure::randomHex(64)로 생성한 64자리 소문자 16진수 토큰입니다. 이 형식을 정확히 검증하므로 정상 사용자는 차단되지 않습니다. 이상한 형식의 쿠키(조작·인젝션 시도)만 차단됩니다.
Q. csrf_refresh.php에 직접 훅을 추가해야 하나요?
CA4 기능의 완전한 동작을 위해서는 csrf_refresh.php 파일에 dx_run_hook('dx_after_csrf_refresh', []) 한 줄 추가가 필요합니다. 코어 파일이므로 업데이트 시 덮어씌워질 수 있음을 고려하세요. 훅을 추가하지 않아도 나머지 CA1~CA8은 모두 정상 동작합니다.
DX Security — Cookie Attribute 보안 v1.0.0 — DesignOneX
https://designonex.com
라이선스
디자인원엑스 라이선스 참고
https://designonex.com/notice/view/1787577858493247