
DX Security — IP Spoofing / Proxy Trust 방어 v1.0.0 안내서
PHP 5.6+ · IIS / Apache / Nginx 완전 호환 코어 Secure::clientIp() / isTrustedProxy() 재사용 코어 수정 없음 — 훅으로만 동작
목차
1. IP Spoofing이란 무엇인가
공격자가 HTTP 헤더를 조작하여 자신의 실제 IP를 숨기거나, 신뢰된 IP로 위장하는 공격입니다.
공격 시나리오:
① Rate Limit 우회:
X-Forwarded-For: 1.1.1.1 (매 요청마다 다른 IP)
→ IP 기반 Rate Limit 우회
② 관리자 IP 제한 우회:
X-Forwarded-For: 관리자IP
→ 관리자 전용 기능 접근
③ localhost 위장:
X-Forwarded-For: 127.0.0.1
→ 내부 전용 API 접근
④ 감사 로그 위조:
X-Real-IP: 피해자IP
→ 공격 기록이 피해자 IP로 남음
핵심 원칙
클라이언트가 임의로 조작할 수 있는 IP 헤더를 신뢰하지 않는다.
올바른 구조:
REMOTE_ADDR (직접 연결 IP)
↓
Trusted Proxy인가?
↓ YES
X-Forwarded-For 검증
↓
Canonical Client IP
잘못된 구조:
$ip = $_SERVER['HTTP_X_FORWARDED_FOR']; ← 검증 없이 신뢰
2. DXCMS 코어의 현재 방어 수준
코어 clientIp() 구현
public function clientIp()
{
$remote = $_SERVER['REMOTE_ADDR'];
if ($this->isTrustedProxy($remote)) {
if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
}
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$parts = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
return trim($parts[0]); // 첫 번째 값 사용
}
}
return $remote; // Trusted Proxy가 아니면 REMOTE_ADDR 반환
}
Trusted Proxy 기본 목록
코어는 다음 IP를 기본으로 신뢰합니다:
| 대역 | 용도 |
|---|---|
127.0.0.1/32 |
Loopback |
10.0.0.0/8 |
사설 네트워크 |
172.16.0.0/12 |
사설 네트워크 |
192.168.0.0/16 |
사설 네트워크 |
173.245.48.0/20 ~ |
Cloudflare IP 목록 |
코어 평가
| 항목 | 방어 | 평가 |
|---|---|---|
| REMOTE_ADDR 우선 | Trusted Proxy일 때만 XFF 사용 | ✅ |
| CIDR 지원 | IPv4/IPv6 CIDR 모두 지원 | ✅ |
| Cloudflare 지원 | CF IP 목록 기본 내장 | ✅ |
3. 코어가 하지 않는 것
❌ XFF 첫 번째 값만 사용 — 체인 우측 검증 없음
공격 시나리오:
X-Forwarded-For: 1.1.1.1, 신뢰Proxy
코어 처리:
parts[0] = 1.1.1.1 → 반환
공격자가 첫 번째 값에 임의 IP를 넣어도
Trusted Peer에서 온 요청이면 신뢰함
❌ CF_CONNECTING_IP 조작 탐지 없음
Cloudflare가 아닌 서버에서 온 요청인데
CF-Connecting-IP 헤더를 포함하면 탐지 없음
❌ Header Injection 탐지 로그 없음
Trusted Proxy가 아닌 직접 클라이언트가
X-Forwarded-For, X-Real-IP 등을 보내도 로그 없음
❌ Proxy Chain 길이 제한 없음
X-Forwarded-For: IP1, IP2, IP3, ... IP1000
→ 무제한 허용
❌ Private IP 위장 탐지 없음
X-Forwarded-For: 127.0.0.1
→ Trusted Peer에서 온 경우 탐지 없음
4. 설치 방법
(DXCMS 루트)/
└── plugins/
└── dx-security-ipspoofing/
├── manifest.php
└── plugin.php
관리자 → 플러그인 → dx-security-ipspoofing → 활성화
5. 방어층 상세 설명
IP1. XFF 체인 우측 검증 보완
코어는 XFF 첫 번째 값만 사용합니다. 이 플러그인은 XFF 체인을 분석하여 이상 패턴을 탐지합니다.
정상 XFF 체인:
X-Forwarded-For: 203.0.113.25, 10.0.0.1, 10.0.0.2
실제 클라이언트: 203.0.113.25 (첫 번째)
Proxy 체인: 10.0.0.1, 10.0.0.2 (오른쪽으로 갈수록 가까운 Proxy)
IP2. CF-Connecting-IP 조작 탐지
CF-Connecting-IP 헤더가 Cloudflare IP 대역이 아닌 곳에서 전송되면 탐지합니다.
정상:
REMOTE_ADDR = 173.245.48.x (Cloudflare IP)
CF-Connecting-IP = 203.0.113.25 (실제 클라이언트)
→ 신뢰
의심:
REMOTE_ADDR = 45.33.x.x (일반 IP)
CF-Connecting-IP = 203.0.113.25 (위조 시도)
→ CF_HEADER_NOT_FROM_CF 로그
IP3. Header Injection 탐지
Trusted Proxy가 아닌 직접 클라이언트가 IP 관련 헤더를 전송하는 경우를 탐지합니다.
탐지 대상 헤더:
X-Forwarded-For X-Real-IP
X-Forwarded Forwarded-For
CF-Connecting-IP X-Cluster-Client-IP
X-Original-Forwarded-For
조건: REMOTE_ADDR가 사설 IP 대역이 아닌 경우 (Trusted Peer가 아님)
결과: HEADER_INJECTION_SUSPECT 로그
IP4. Proxy Chain 길이 제한
과도하게 긴 XFF 체인을 탐지합니다.
기본 제한: 10개 (DX_IP_MAX_CHAIN_DEPTH)
X-Forwarded-For: IP1, IP2, ..., IP11
→ XFF_CHAIN_TOO_LONG 로그
정상적인 환경에서 Proxy를 10단계 이상 거치는 경우는 거의 없음
IP5. Private IP 위장 탐지
XFF에 사설/루프백 IP가 클라이언트 IP로 주장되는 경우를 탐지합니다.
탐지 시나리오:
Trusted Proxy에서 온 요청인데
X-Forwarded-For: 127.0.0.1 또는
X-Forwarded-For: 192.168.1.1 → XFF_PRIVATE_IP_CLAIMED 로그
정상 클라이언트 IP는 공개 IP여야 함
사설 IP를 클라이언트 IP로 주장하는 것은 내부망 위장 시도
IP6. 관리자/인증 경로 IP 감사 로그
관리자 페이지와 로그인 경로에서 IP 정보를 자동으로 기록합니다.
기록 정보:
PEER: REMOTE_ADDR (TCP 직접 연결 IP)
resolved: 코어 clientIp() 반환값
xff: X-Forwarded-For 헤더 값
감시 경로:
/admin/, /auth/login, /api/admin
6. 보안 로그 읽는 법
로그 형식
[날짜 시각][IPSF:타입][PEER:직접연결IP][UA:에이전트][UID:사용자ID][METHOD URI] 메시지
이벤트 타입
| 타입 | 의미 |
|---|---|
IPSF:HEADER_INJECTION_SUSPECT |
Trusted Proxy가 아닌 곳에서 IP 헤더 전송 |
IPSF:XFF_CHAIN_TOO_LONG |
XFF 체인 길이 초과 |
IPSF:XFF_PRIVATE_IP_CLAIMED |
XFF에 사설 IP 위장 탐지 |
IPSF:CF_HEADER_NOT_FROM_CF |
Cloudflare가 아닌 곳에서 CF 헤더 전송 |
IPSF:IP_MISMATCH_SENSITIVE |
민감 경로에서 IP 불일치 탐지 |
IPSF:IP_AUDIT |
관리자/인증 경로 IP 감사 기록 |
로그 예시
# Trusted Proxy가 아닌 곳에서 XFF 헤더 전송
[2026-09-03 09:00:01][IPSF:HEADER_INJECTION_SUSPECT][PEER:45.33.x.x][...][UID:0][POST /auth/login]
header=X-Forwarded-For value=127.0.0.1 peer=45.33.x.x (non-trusted peer)
# CF 헤더 위조 시도
[2026-09-03 14:22:10][IPSF:CF_HEADER_NOT_FROM_CF][PEER:58.29.x.x][...][UID:0][GET /admin/]
cf_ip=192.168.1.1 peer=58.29.x.x (CF-Connecting-IP from non-CF peer)
# XFF에 127.0.0.1 주입 시도
[2026-09-03 16:45:03][IPSF:XFF_PRIVATE_IP_CLAIMED][PEER:10.0.0.20][...][UID:0][POST /api/admin]
claimed_client_ip=127.0.0.1 peer=10.0.0.20
# 관리자 접근 IP 감사
[2026-09-03 20:11:44][IPSF:IP_AUDIT][PEER:10.0.0.20][...][UID:1][GET /admin/members/]
peer=10.0.0.20 resolved=203.0.113.25 uri=/admin/members/
7. 보안 강점 요약
| 공격 | 결과 | 방어 레이어 |
|---|---|---|
| 직접 XFF 헤더 조작 | HEADER_INJECTION_SUSPECT 로그 | IP3 |
| CF-Connecting-IP 위조 | CF_HEADER_NOT_FROM_CF 로그 | IP2 |
| XFF에 127.0.0.1 주입 | XFF_PRIVATE_IP_CLAIMED 로그 | IP5 |
| XFF 과도한 체인 | XFF_CHAIN_TOO_LONG 로그 | IP4 |
| Rate Limit IP 우회 | 코어 clientIp() 사용 → 일관된 IP | 코어 |
| 정상 Cloudflare 요청 | 정상 처리 | 코어 ✅ |
| 정상 Reverse Proxy | 정상 처리 | 코어 ✅ |
| 직접 접근 | REMOTE_ADDR 사용 | 코어 ✅ |
8. 개발자 API 가이드
플러그인에서 IP 가져오기
// 올바른 방법 — Proxy Trust 검증 포함
$clientIp = dx_ip_client(); // 코어 Secure::clientIp() 래핑
// TCP 직접 연결 IP (Proxy 체인 무시)
$peerIp = dx_ip_peer(); // REMOTE_ADDR
// 절대 사용 금지
// $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; ← 검증 없는 직접 사용 금지
// $ip = $_SERVER['HTTP_X_REAL_IP']; ← 금지
IP 대역 확인
// 특정 CIDR 범위 내 IP 확인
$ip = dx_ip_client();
if (dx_ip_in_range($ip, '10.0.0.0/8')) {
// 내부 네트워크 요청
}
// 사설 IP 여부 확인
if (dx_ip_is_private($ip)) {
// 사설 IP — 내부 요청
}
IP 감사 로그 기록
$ip = dx_ip_client();
dx_ip_log('MY_PLUGIN_ACCESS', 'ip=' . $ip . ' action=download');
Rate Limit에 올바른 IP 사용
// 올바른 방법
$ip = dx_ip_client();
$allowed = Secure::getInstance()->rateLimit('my_action_' . md5($ip), 10, 60);
// 잘못된 방법 (검증 없는 헤더 직접 사용)
// $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; ← 금지
// $allowed = rateLimit('my_action_' . $ip, 10, 60); ← 우회 가능
DX Security — IP Spoofing / Proxy Trust 방어 v1.0.0 — DesignOneX https://designonex.com
라이선스
디자인원엑스 라이선스 참고
https://designonex.com/notice/view/1787577858493247