- Published on
Wireshark와 네트워크 보안 완전 가이드: 패킷 스니핑부터 침투 탐지까지 개발자를 위한 실전 보안
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 1. 왜 개발자에게 네트워크 보안이 중요한가
- 2. Wireshark 완전 정복
- 3. tcpdump 실전 (서버 환경)
- 4. 패킷으로 읽는 프로토콜 분석
- 5. 공격 탐지 실전
- 6. 보안 도구 생태계
- 7. 제로 트러스트 아키텍처
- 8. 개발자를 위한 보안 체크리스트
- 9. 보안 자격증 로드맵
- 10. 퀴즈
- 11. 참고 자료
1. 왜 개발자에게 네트워크 보안이 중요한가
보안은 선택이 아니라 필수다
2025년 IBM의 Cost of a Data Breach Report에 따르면, 데이터 유출 사고의 평균 비용은 488만 달러에 달합니다. 이는 전년 대비 10% 상승한 수치이며, 사상 최고치입니다. 더 충격적인 것은 보안 사고의 60% 이상이 애플리케이션 레이어에서 발생한다는 점입니다.
OWASP Top 10 2025를 보면 개발자가 직접 관여해야 하는 취약점이 대부분입니다:
| 순위 | 취약점 | 개발자 관련도 |
|---|---|---|
| 1 | Broken Access Control | 매우 높음 |
| 2 | Cryptographic Failures | 높음 |
| 3 | Injection (SQL, XSS, LDAP) | 매우 높음 |
| 4 | Insecure Design | 매우 높음 |
| 5 | Security Misconfiguration | 높음 |
| 6 | Vulnerable Components | 높음 |
| 7 | Authentication Failures | 매우 높음 |
| 8 | Data Integrity Failures | 높음 |
| 9 | Logging/Monitoring Failures | 보통 |
| 10 | SSRF (Server-Side Request Forgery) | 매우 높음 |
2026년 사이버 위협 환경
2026년은 사이버 보안 역사에서 가장 위험한 해로 기록되고 있습니다. 미국-이란 갈등이 고조되면서 국가 지원 해킹 그룹의 활동이 700% 이상 증가했습니다. 특히:
- APT 그룹 활동 급증: 국가 지원 해킹 그룹이 금융, 에너지, 의료 인프라를 집중 공격
- 공급망 공격 확대: npm, PyPI 등 패키지 저장소를 통한 악성코드 배포가 3배 증가
- AI 기반 공격: LLM을 활용한 피싱 메일, 자동화된 취약점 스캐닝이 일상화
- 랜섬웨어 진화: 이중 갈취(데이터 유출 + 암호화) 전략이 표준화
DevSecOps와 Shift-Left 보안
전통적인 보안은 개발이 끝난 후 테스트 단계에서 수행했습니다. 하지만 Shift-Left 접근법은 개발 초기 단계부터 보안을 통합합니다.
전통적 접근:
설계 → 개발 → 테스트 → [보안 테스트] → 배포
← 문제 발견 시 비용이 기하급수적으로 증가 →
Shift-Left:
[보안 설계] → [보안 코딩] → [보안 테스트] → [보안 모니터링]
← 조기 발견으로 비용 절감 →
개발자가 네트워크 패킷을 읽을 수 있다면:
- API 호출에서 민감한 데이터가 평문으로 전송되는지 직접 확인 가능
- TLS 설정 오류를 배포 전에 발견 가능
- 서드파티 라이브러리의 수상한 네트워크 활동 탐지 가능
- 성능 병목과 보안 취약점을 동시에 진단 가능
2. Wireshark 완전 정복
2-1. 설치 및 초기 설정
macOS:
brew install --cask wireshark
Ubuntu/Debian:
sudo apt update
sudo apt install wireshark
sudo usermod -aG wireshark $USER
# 로그아웃 후 재로그인 필요
Windows:
공식 사이트(wireshark.org)에서 설치 파일을 다운로드합니다. 설치 시 Npcap을 함께 설치해야 합니다.
2-2. 인터페이스 구성
Wireshark의 메인 화면은 3개 패널로 구성됩니다:
- 패킷 리스트 패널 (상단): 캡처된 모든 패킷의 요약 정보
- 패킷 디테일 패널 (중단): 선택한 패킷의 프로토콜 계층별 상세 정보
- 패킷 바이트 패널 (하단): 원시 바이너리 데이터 (Hex + ASCII)
2-3. Capture Filters vs Display Filters
이 두 필터는 Wireshark를 효과적으로 사용하기 위한 핵심입니다. 가장 중요한 차이는 적용 시점입니다.
| 구분 | Capture Filter | Display Filter |
|---|---|---|
| 적용 시점 | 캡처 시작 전 | 캡처 후 (실시간 변경 가능) |
| 문법 | BPF (Berkeley Packet Filter) | Wireshark 자체 문법 |
| 성능 | 높음 (커널 레벨 필터링) | 낮음 (모든 패킷 캡처 후 필터링) |
| 유연성 | 제한적 | 매우 유연 |
| 예시 | host 192.168.1.1 | ip.addr == 192.168.1.1 |
| 예시 | port 80 | tcp.port == 80 |
| 예시 | tcp and port 443 | tcp.port == 443 |
Capture Filter 예시:
host 10.0.0.1
port 443
net 192.168.0.0/24
tcp and port 80
not arp
Display Filter 예시:
ip.addr == 10.0.0.1
tcp.port == 443
http.request.method == "GET"
dns.qry.name contains "google"
2-4. 필수 Display Filters 30선
| 번호 | 필터 | 용도 |
|---|---|---|
| 1 | ip.addr == 10.0.0.1 | 특정 IP 필터링 |
| 2 | tcp.port == 443 | HTTPS 트래픽 |
| 3 | tcp.port == 80 | HTTP 트래픽 |
| 4 | http.request.method == "POST" | POST 요청만 |
| 5 | http.request.method == "GET" | GET 요청만 |
| 6 | dns.qry.name contains "example" | 특정 도메인 DNS 쿼리 |
| 7 | tcp.flags.syn == 1 and tcp.flags.ack == 0 | SYN 스캔 탐지 |
| 8 | tcp.analysis.retransmission | TCP 재전송 탐지 |
| 9 | tcp.analysis.duplicate-ack | 중복 ACK 탐지 |
| 10 | tcp.analysis.zero-window | Zero Window 탐지 |
| 11 | http.response.code == 500 | 서버 에러 응답 |
| 12 | http.response.code >= 400 | 모든 에러 응답 |
| 13 | tls.handshake.type == 1 | TLS Client Hello |
| 14 | tls.handshake.type == 2 | TLS Server Hello |
| 15 | arp | ARP 패킷만 |
| 16 | icmp | ICMP (ping) 패킷만 |
| 17 | dns | DNS 패킷만 |
| 18 | tcp.analysis.flags | TCP 문제가 있는 패킷 |
| 19 | http.cookie contains "session" | 세션 쿠키 포함 요청 |
| 20 | frame.time_delta > 1 | 1초 이상 지연된 패킷 |
| 21 | tcp.len > 0 | 데이터가 있는 TCP 패킷 |
| 22 | ip.src == 10.0.0.0/8 | 내부 네트워크 소스 |
| 23 | not arp and not dns | ARP, DNS 제외 |
| 24 | http.host contains "api" | API 호출 필터링 |
| 25 | tcp.stream eq 5 | 특정 TCP 스트림 |
| 26 | frame.len > 1500 | MTU 초과 패킷 |
| 27 | http.content_type contains "json" | JSON 응답 |
| 28 | tcp.analysis.lost_segment | 손실 세그먼트 |
| 29 | ip.ttl < 10 | 낮은 TTL (라우팅 문제) |
| 30 | tls.handshake.extensions_server_name | SNI 기반 필터링 |
2-5. Follow Stream 기능
TCP Stream 추적은 Wireshark의 가장 강력한 기능 중 하나입니다:
- 패킷을 우클릭합니다
- Follow > TCP Stream 선택
- 해당 연결의 전체 데이터 흐름을 볼 수 있습니다
# TCP Stream 예시 (HTTP 요청/응답)
GET /api/users HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
Set-Cookie: session=abc123; HttpOnly; Secure
[Response Body]
HTTP Stream은 HTTP/2 멀티플렉싱 환경에서 개별 HTTP 트랜잭션을 추적할 때 유용합니다.
2-6. Statistics 메뉴 활용
Conversations: 어떤 IP 간에 가장 많은 트래픽이 오가는지 확인
Source Destination Packets Bytes
10.0.0.5 52.85.132.99 1,234 890KB
10.0.0.5 142.250.80.46 567 234KB
10.0.0.5 104.18.32.68 345 156KB
Protocol Hierarchy: 프로토콜별 트래픽 비율 확인
Ethernet (100%)
├── IPv4 (98.5%)
│ ├── TCP (85.2%)
│ │ ├── TLS (62.1%)
│ │ ├── HTTP (15.3%)
│ │ └── Other (7.8%)
│ ├── UDP (12.8%)
│ │ ├── DNS (8.2%)
│ │ └── QUIC (4.6%)
│ └── ICMP (0.5%)
└── ARP (1.5%)
I/O Graphs: 시간대별 트래픽 패턴을 시각화합니다. DDoS 공격이나 트래픽 스파이크를 탐지하는 데 필수적입니다.
2-7. 프로파일 설정과 컬럼 커스텀
프로파일을 만들면 용도별로 최적화된 환경을 전환할 수 있습니다:
프로파일 예시:
- Default: 일반 분석
- Security: 보안 분석 (색상 규칙, 보안 관련 컬럼)
- Performance: 성능 분석 (지연시간, 재전송 컬럼)
- DNS: DNS 분석 전용
유용한 커스텀 컬럼:
- Delta Time: 이전 패킷과의 시간 차이
- TCP Stream Index: 스트림 번호
- HTTP Host: HTTP 요청의 호스트 헤더
- TLS SNI: TLS Server Name Indication
3. tcpdump 실전 (서버 환경)
3-1. 기본 문법과 BPF 필터
tcpdump는 서버에서 GUI 없이 패킷을 캡처하는 도구입니다. Wireshark와 동일한 BPF(Berkeley Packet Filter) 문법을 사용합니다.
기본 구조:
tcpdump [옵션] [BPF 필터 표현식]
주요 옵션:
-i eth0 # 인터페이스 지정
-w output.pcap # 파일로 저장
-r input.pcap # 파일 읽기
-c 100 # 패킷 100개만 캡처
-n # DNS 역조회 비활성화 (속도 향상)
-nn # 포트 번호도 이름 변환 안 함
-v / -vv / -vvv # 상세도 증가
-X # Hex + ASCII 출력
-A # ASCII만 출력
-s 0 # 전체 패킷 캡처 (기본: 262144 bytes)
-tttt # 타임스탬프를 읽기 쉬운 형식으로
3-2. 실전 명령어 20개
| 번호 | 명령어 | 용도 |
|---|---|---|
| 1 | sudo tcpdump -i eth0 | 기본 캡처 |
| 2 | sudo tcpdump -i eth0 -w capture.pcap | 파일로 저장 |
| 3 | sudo tcpdump -i eth0 host 10.0.0.1 | 특정 호스트 |
| 4 | sudo tcpdump -i eth0 port 443 | 특정 포트 |
| 5 | sudo tcpdump -i eth0 src 10.0.0.1 | 소스 IP 필터 |
| 6 | sudo tcpdump -i eth0 dst port 80 | 목적지 포트 필터 |
| 7 | sudo tcpdump -i eth0 net 192.168.0.0/24 | 서브넷 필터 |
| 8 | sudo tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0' | SYN 패킷만 |
| 9 | sudo tcpdump -i eth0 -c 1000 -w sample.pcap | 1000개만 캡처 |
| 10 | sudo tcpdump -i eth0 -nn port 53 | DNS 트래픽 |
| 11 | sudo tcpdump -i eth0 icmp | ICMP(ping)만 |
| 12 | sudo tcpdump -i eth0 'port 80 and host 10.0.0.1' | 복합 필터 |
| 13 | sudo tcpdump -i eth0 -A port 80 | HTTP 내용 보기 |
| 14 | sudo tcpdump -i eth0 'tcp[32:4] = 0x47455420' | GET 요청만 |
| 15 | sudo tcpdump -i eth0 greater 1000 | 1000바이트 초과 |
| 16 | sudo tcpdump -i eth0 arp | ARP 패킷만 |
| 17 | sudo tcpdump -i eth0 -tttt -c 50 port 443 | 타임스탬프 포함 |
| 18 | sudo tcpdump -i any port 8080 | 모든 인터페이스 |
| 19 | sudo tcpdump -i eth0 not port 22 | SSH 제외 |
| 20 | sudo tcpdump -i eth0 -G 3600 -w 'capture_%Y%m%d_%H.pcap' | 시간별 로테이션 |
3-3. 원격 서버 캡처 후 Wireshark 분석
서버에서 캡처한 pcap 파일을 로컬로 가져와 Wireshark에서 분석하는 워크플로우:
# 1. 서버에서 캡처
ssh user@server 'sudo tcpdump -i eth0 -c 5000 -w /tmp/capture.pcap port 443'
# 2. 로컬로 복사
scp user@server:/tmp/capture.pcap ./analysis/
# 3. Wireshark로 열기
wireshark ./analysis/capture.pcap
3-4. tcpdump + ssh 파이프라인 (실시간)
원격 서버의 패킷을 실시간으로 로컬 Wireshark에서 분석:
# 방법 1: ssh 파이프
ssh user@server 'sudo tcpdump -i eth0 -w - port 443' | wireshark -k -i -
# 방법 2: named pipe 사용
mkfifo /tmp/remote_capture
wireshark -k -i /tmp/remote_capture &
ssh user@server 'sudo tcpdump -i eth0 -w - port 443' > /tmp/remote_capture
# 방법 3: 특정 기간만 캡처
ssh user@server 'sudo timeout 60 tcpdump -i eth0 -w - port 80' | wireshark -k -i -
이 기법은 프로덕션 서버에서 발생하는 네트워크 이슈를 실시간으로 분석할 때 매우 유용합니다.
4. 패킷으로 읽는 프로토콜 분석
4-1. TCP 3-Way Handshake 패킷 분석
TCP 연결 설정의 3-Way Handshake를 패킷 레벨에서 분석합니다:
Step 1: SYN (클라이언트 → 서버)
Source: 10.0.0.5:54321
Dest: 93.184.216.34:443
Flags: [SYN]
Seq: 0 (ISN: Initial Sequence Number)
Win: 65535
Options: MSS=1460, SACK Permitted, Window Scale=6
Step 2: SYN-ACK (서버 → 클라이언트)
Source: 93.184.216.34:443
Dest: 10.0.0.5:54321
Flags: [SYN, ACK]
Seq: 0 (서버의 ISN)
Ack: 1 (클라이언트 ISN + 1)
Win: 65535
Options: MSS=1400, SACK Permitted, Window Scale=7
Step 3: ACK (클라이언트 → 서버)
Source: 10.0.0.5:54321
Dest: 93.184.216.34:443
Flags: [ACK]
Seq: 1
Ack: 1
Win: 65535
Wireshark 필터: tcp.flags.syn == 1 으로 SYN 패킷을 찾고, Follow TCP Stream으로 전체 핸드셰이크를 확인합니다.
문제 진단 포인트:
- SYN만 있고 SYN-ACK가 없으면: 서버가 응답하지 않음 (방화벽 차단 가능)
- SYN-ACK는 오지만 최종 ACK가 없으면: 클라이언트 측 문제
- RST 패킷이 즉시 오면: 포트가 닫혀 있거나 방화벽이 연결 거부
4-2. HTTP 요청/응답 분석
# HTTP 요청 패킷 상세
Frame 45: 342 bytes on wire
Ethernet II: Src=aa:bb:cc:dd:ee:ff, Dst=11:22:33:44:55:66
Internet Protocol Version 4: Src=10.0.0.5, Dst=93.184.216.34
Transmission Control Protocol: Src Port=54321, Dst Port=80
Hypertext Transfer Protocol:
GET /api/v1/users?page=1 HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0 ...
Accept: application/json
Authorization: Bearer eyJhbGci...
Cookie: session_id=abc123
Connection: keep-alive
# HTTP 응답 패킷 상세
Frame 47: 1280 bytes on wire
Hypertext Transfer Protocol:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 956
Set-Cookie: session_id=xyz789; HttpOnly; Secure; SameSite=Strict
X-Request-Id: req_abc123
Strict-Transport-Security: max-age=31536000
X-Content-Type-Options: nosniff
보안 관점에서 확인할 사항:
- Authorization 헤더가 평문 HTTP로 전송되고 있지 않은지
- Set-Cookie에 HttpOnly, Secure 플래그가 있는지
- 보안 헤더(HSTS, X-Content-Type-Options 등)가 설정되어 있는지
- 응답에 민감한 정보가 포함되어 있지 않은지
4-3. DNS 질의/응답 패킷 구조
# DNS 질의 (Query)
Domain Name System (query)
Transaction ID: 0x1a2b
Flags: 0x0100 Standard query
Questions: 1
Queries:
api.example.com: type A, class IN
# DNS 응답 (Response)
Domain Name System (response)
Transaction ID: 0x1a2b
Flags: 0x8180 Standard query response, No error
Questions: 1
Answer RRs: 2
Answers:
api.example.com: type A, class IN, addr 93.184.216.34
TTL: 300 (5 minutes)
api.example.com: type A, class IN, addr 93.184.216.35
TTL: 300 (5 minutes)
DNS 보안 점검:
- DNS 쿼리가 암호화되지 않은 채로 전송되고 있는지 (DoH/DoT 사용 여부)
- 비정상적으로 긴 도메인 이름 (DNS tunneling 의심)
- 알 수 없는 DNS 서버로의 쿼리 (DNS hijacking 가능성)
4-4. TLS Handshake 패킷 분석
TLS 1.3 핸드셰이크는 1-RTT(Round Trip Time)로 이전 버전보다 빠릅니다:
Step 1: Client Hello
TLS Record Layer:
Content Type: Handshake (22)
Version: TLS 1.0 (호환성)
Handshake Protocol: Client Hello
Version: TLS 1.2 (호환성을 위해)
Random: [32 bytes]
Session ID: [32 bytes]
Cipher Suites (17 suites):
TLS_AES_256_GCM_SHA384
TLS_AES_128_GCM_SHA256
TLS_CHACHA20_POLY1305_SHA256
Extensions:
server_name: api.example.com (SNI)
supported_versions: TLS 1.3
key_share: x25519 [public key]
signature_algorithms: ecdsa_secp256r1_sha256, rsa_pss_rsae_sha256
Step 2: Server Hello + Certificate + Finished
Handshake Protocol: Server Hello
Cipher Suite: TLS_AES_256_GCM_SHA384
Key Share: x25519 [server public key]
[Encrypted Extensions]
[Certificate]
[Certificate Verify]
[Finished]
Step 3: Client Finished (Encrypted)
[Change Cipher Spec]
[Finished]
4-5. TLS 복호화: SSLKEYLOGFILE 환경변수 설정
개발/디버깅 환경에서 TLS 트래픽을 복호화할 수 있습니다:
# 1. 환경변수 설정 (bash)
export SSLKEYLOGFILE="$HOME/.ssl-keys.log"
# 2. Chrome 또는 Firefox 실행 (이 환경변수를 자동으로 인식)
# 브라우저가 TLS 세션 키를 파일에 기록합니다
# 3. curl에서도 사용 가능
SSLKEYLOGFILE=$HOME/.ssl-keys.log curl https://api.example.com/health
Wireshark 설정:
- Edit > Preferences > Protocols > TLS
- (Pre)-Master-Secret log filename에 키 파일 경로 입력
- 이후 캡처된 TLS 트래픽이 복호화되어 표시됩니다
주의: 프로덕션 환경에서는 절대 사용하지 마세요. 개발/스테이징 환경에서만 사용하세요.
5. 공격 탐지 실전
5-1. ARP Spoofing 탐지
ARP Spoofing은 로컬 네트워크에서 MAC 주소를 위조하여 트래픽을 가로채는 공격입니다.
정상 ARP 동작:
Who has 10.0.0.1? Tell 10.0.0.5
→ 10.0.0.1 is at aa:bb:cc:dd:ee:ff
ARP Spoofing 탐지 시나리오:
# 공격자가 게이트웨이(10.0.0.1)를 사칭
10.0.0.1 is at [공격자 MAC: 11:22:33:44:55:66] ← 비정상!
10.0.0.1 is at [실제 MAC: aa:bb:cc:dd:ee:ff] ← 정상
# 같은 IP에 대해 두 개의 다른 MAC 주소 → ARP Spoofing!
Wireshark 필터:
arp.duplicate-address-detected
arp.duplicate-address-frame
탐지 스크립트:
#!/bin/bash
# ARP 테이블 모니터링
while true; do
arp -a | sort > /tmp/arp_current.txt
if [ -f /tmp/arp_previous.txt ]; then
diff /tmp/arp_previous.txt /tmp/arp_current.txt
if [ $? -ne 0 ]; then
echo "[ALERT] ARP table changed at $(date)"
diff /tmp/arp_previous.txt /tmp/arp_current.txt
fi
fi
cp /tmp/arp_current.txt /tmp/arp_previous.txt
sleep 10
done
5-2. Port Scanning 탐지
SYN Scan (Half-Open Scan) 패턴:
Nmap의 기본 스캔 방식입니다. SYN 패킷만 보내고 연결을 완료하지 않습니다.
공격자 → 대상:22 [SYN]
대상:22 → 공격자 [SYN,ACK] ← 포트 열림
공격자 → 대상:22 [RST] ← 연결 완료 안 함
공격자 → 대상:23 [SYN]
대상:23 → 공격자 [RST,ACK] ← 포트 닫힘
Wireshark 필터:
# SYN 스캔 탐지 (ACK 없는 SYN만)
tcp.flags.syn == 1 and tcp.flags.ack == 0
# 짧은 시간에 다수의 포트로 SYN
tcp.flags.syn == 1 and tcp.flags.ack == 0 and ip.src == 10.0.0.100
Nmap 스캔 유형별 패킷 특징:
| 스캔 유형 | 명령어 | TCP 플래그 | 특징 |
|---|---|---|---|
| SYN Scan | nmap -sS | SYN | 가장 흔함, Half-open |
| Connect Scan | nmap -sT | Full handshake | 완전한 TCP 연결 |
| FIN Scan | nmap -sF | FIN | 스텔스 스캔 |
| XMAS Scan | nmap -sX | FIN,PSH,URG | Christmas Tree |
| NULL Scan | nmap -sN | (없음) | 플래그 없는 패킷 |
| ACK Scan | nmap -sA | ACK | 방화벽 규칙 탐지 |
| UDP Scan | nmap -sU | UDP | ICMP 응답으로 판단 |
5-3. DDoS 패턴 분석
SYN Flood:
# 대량의 SYN 패킷이 다양한 소스 IP에서 동시에 유입
# Wireshark 필터
tcp.flags.syn == 1 and tcp.flags.ack == 0
# tcpdump로 SYN 패킷 카운트
sudo tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0' -c 10000 2>&1 | tail -1
# 결과: "10000 packets captured" 가 수 초 만에 나오면 SYN Flood 의심
HTTP Flood:
# 동일 URL에 대한 대량 요청
# Wireshark 필터
http.request.uri == "/api/login"
# 초당 요청 수 확인
# Statistics > I/O Graph에서 http.request를 Y축으로 설정
DNS Amplification:
# DNS 응답이 쿼리보다 훨씬 큰 경우 (증폭 비율)
# 작은 쿼리 (약 60 bytes) → 큰 응답 (약 3000+ bytes)
# Wireshark 필터
dns.response and frame.len > 1000
# 증폭 비율 = 응답 크기 / 요청 크기
# ANY 쿼리의 증폭 비율: 약 28x ~ 54x
DDoS 탐지 체크리스트:
- I/O Graph에서 트래픽 급증 확인
- Conversations에서 상위 소스 IP 확인
- Protocol Hierarchy에서 특정 프로토콜 비율 급증 확인
- 동일 패턴의 반복적 요청 확인
- 비정상 소스 IP 대역 확인 (GeoIP 활용)
5-4. SQL Injection 탐지
HTTP POST Body에서 SQL 키워드를 검색합니다:
# Wireshark Display Filter
http.request.method == "POST" and (
http contains "UNION" or
http contains "SELECT" or
http contains "DROP" or
http contains "DELETE" or
http contains "1=1" or
http contains "OR 1" or
http contains "--" or
http contains "/*"
)
실제 SQL Injection 패킷 예시:
POST /api/login HTTP/1.1
Host: vulnerable-app.com
Content-Type: application/x-www-form-urlencoded
username=admin' OR '1'='1&password=anything
비정상 응답 크기 패턴:
- 정상 로그인 실패 응답: 약 200 bytes
- SQL Injection 성공 시: 수천 bytes (데이터 유출)
# 비정상 응답 크기 탐지
http.response and http.content_length > 10000
5-5. 데이터 유출 탐지
DNS Tunneling 탐지:
DNS Tunneling은 DNS 쿼리에 데이터를 인코딩하여 방화벽을 우회하는 기법입니다.
# 정상 DNS 쿼리
api.example.com (15자)
# DNS Tunneling 쿼리 (비정상적으로 긴 도메인)
dGhpcyBpcyBhIHNlY3JldCBtZXNzYWdl.tunnel.evil.com (48자)
Wireshark 필터:
# 비정상 길이 DNS 쿼리 탐지
dns.qry.name.len > 50
# TXT 레코드 쿼리 (tunneling에 자주 사용)
dns.qry.type == 16
# 특정 도메인에 대한 비정상 많은 쿼리
dns.qry.name contains "tunnel.evil.com"
비정상 아웃바운드 트래픽 탐지:
# tcpdump로 대용량 아웃바운드 모니터링
sudo tcpdump -i eth0 'src net 10.0.0.0/8 and dst net not 10.0.0.0/8 and greater 10000' -c 100
# 비정상 포트로의 아웃바운드
sudo tcpdump -i eth0 'src net 10.0.0.0/8 and not (dst port 80 or dst port 443 or dst port 53 or dst port 22)'
6. 보안 도구 생태계
6-1. Nmap: 네트워크 스캐너
Nmap은 네트워크 탐색과 보안 감사를 위한 오픈소스 도구입니다.
# 기본 포트 스캔
nmap 192.168.1.1
# 서비스 버전 탐지
nmap -sV 192.168.1.1
# OS 탐지
nmap -O 192.168.1.1
# 공격적 스캔 (서비스, OS, 스크립트, traceroute)
nmap -A 192.168.1.1
# 전체 포트 스캔
nmap -p- 192.168.1.1
# NSE 스크립트 활용
nmap --script vuln 192.168.1.1
nmap --script ssl-heartbleed 192.168.1.1
nmap --script http-sql-injection 192.168.1.1
6-2. Burp Suite: 웹 앱 보안 테스트
Burp Suite는 웹 애플리케이션 보안 테스트의 표준 도구입니다.
주요 기능:
- Proxy: 브라우저와 서버 사이에서 요청/응답 가로채기 및 수정
- Scanner: 자동 취약점 스캔 (SQL Injection, XSS, CSRF 등)
- Intruder: 자동화된 공격 (Brute Force, Fuzzing)
- Repeater: 개별 요청을 수정하여 반복 전송
- Decoder: 인코딩/디코딩 도구
6-3. OWASP ZAP: 무료 웹 보안 스캐너
# Docker로 빠른 스캔
docker run -t zaproxy/zap-stable zap-baseline.py -t https://target.com
# API 스캔
docker run -t zaproxy/zap-stable zap-api-scan.py -t https://target.com/openapi.json -f openapi
# 전체 스캔
docker run -t zaproxy/zap-stable zap-full-scan.py -t https://target.com
6-4. Snort / Suricata: IDS/IPS
# Suricata 설치 (Ubuntu)
sudo apt install suricata
# 규칙 업데이트
sudo suricata-update
# IDS 모드로 실행
sudo suricata -c /etc/suricata/suricata.yaml -i eth0
# 커스텀 규칙 예시
# /etc/suricata/rules/local.rules
# alert http any any -> any any (msg:"SQL Injection Attempt"; content:"UNION SELECT"; nocase; sid:1000001; rev:1;)
# alert dns any any -> any any (msg:"DNS Tunneling Suspected"; dns.query; content:".tunnel."; nocase; sid:1000002; rev:1;)
6-5. Metasploit: 침투 테스트 프레임워크
# Metasploit 시작
msfconsole
# 취약점 검색
# msf6> search type:exploit platform:linux apache
# 모듈 사용 예시
# msf6> use exploit/multi/http/apache_log4j
# msf6> set RHOSTS target.com
# msf6> set RPORT 8080
# msf6> run
6-6. 도구 비교표
| 도구 | 용도 | 라이선스 | 난이도 | 주요 사용 대상 |
|---|---|---|---|---|
| Wireshark | 패킷 분석 | 무료/OSS | 중급 | 네트워크 엔지니어, 개발자 |
| tcpdump | CLI 패킷 캡처 | 무료/OSS | 중급 | 시스템 관리자 |
| Nmap | 포트 스캐닝 | 무료/OSS | 초급 | 보안 엔지니어 |
| Burp Suite | 웹 보안 테스트 | 유료/무료판 | 고급 | 펜테스터 |
| OWASP ZAP | 웹 보안 스캔 | 무료/OSS | 중급 | 개발자, QA |
| Snort | IDS/IPS | 무료/OSS | 고급 | 보안 운영 |
| Suricata | IDS/IPS | 무료/OSS | 고급 | 보안 운영 |
| Metasploit | 침투 테스트 | 유료/무료판 | 고급 | 펜테스터 |
7. 제로 트러스트 아키텍처
7-1. 기본 원칙: Never Trust, Always Verify
전통적인 보안 모델은 "성(Castle)과 해자(Moat)" 접근법이었습니다. 내부 네트워크는 신뢰하고, 외부만 차단합니다. 하지만 이 모델은 내부 위협이나 VPN 침투 시 무력합니다.
제로 트러스트는 근본적으로 다릅니다:
전통적 모델:
[인터넷] --방화벽-- [내부 네트워크: 모두 신뢰]
→ 내부 침투 시 횡적 이동(Lateral Movement) 자유
제로 트러스트:
[모든 요청] --인증/인가-- [리소스]
→ 모든 접근마다 검증 (위치 무관)
핵심 원칙:
- 명시적 검증: 모든 요청을 인증, 인가, 암호화
- 최소 권한: 필요한 최소한의 권한만 부여 (Just-In-Time, Just-Enough-Access)
- 침해 가정: 이미 침해당했다고 가정하고 설계
7-2. 마이크로 세그먼테이션
네트워크를 작은 영역으로 분리하여 횡적 이동을 차단합니다:
전통적 네트워크:
[Web Server] ←→ [App Server] ←→ [DB Server]
모두 같은 VLAN, 자유로운 통신
마이크로 세그먼테이션:
[Web Server] --정책-→ [App Server] --정책-→ [DB Server]
각 통신에 별도 보안 정책 적용
- Web → App: 포트 8080만 허용
- App → DB: 포트 5432만 허용
- Web → DB: 차단
7-3. mTLS (상호 인증) 구현
일반 TLS는 서버만 인증하지만, mTLS는 클라이언트도 인증합니다:
일반 TLS:
Client → Server: "당신이 진짜 서버인지 인증서로 증명해주세요"
Server → Client: [서버 인증서 제시]
→ 클라이언트는 검증하지 않음
mTLS:
Client → Server: "당신이 진짜 서버인지 증명해주세요"
Server → Client: [서버 인증서] + "당신도 증명해주세요"
Client → Server: [클라이언트 인증서]
→ 양방향 인증 완료
Node.js mTLS 서버 예시:
const https = require('https')
const fs = require('fs')
const options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem'),
ca: fs.readFileSync('ca-cert.pem'), // CA 인증서
requestCert: true, // 클라이언트 인증서 요구
rejectUnauthorized: true, // 유효하지 않은 인증서 거부
}
const server = https.createServer(options, (req, res) => {
const clientCert = req.socket.getPeerCertificate()
console.log('Client CN:', clientCert.subject.CN)
res.writeHead(200)
res.end('Mutual TLS authenticated!\n')
})
server.listen(443)
7-4. Service Mesh (Istio)에서의 보안
Istio는 마이크로서비스 간 통신에 자동으로 mTLS를 적용합니다:
# Istio PeerAuthentication - mTLS 강제
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: production
spec:
mtls:
mode: STRICT # mTLS 필수
---
# Istio AuthorizationPolicy - 세밀한 접근 제어
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: api-access
namespace: production
spec:
selector:
matchLabels:
app: api-server
rules:
- from:
- source:
principals: ['cluster.local/ns/production/sa/frontend']
to:
- operation:
methods: ['GET', 'POST']
paths: ['/api/v1/*']
7-5. 제로 트러스트와 Kubernetes
# Kubernetes NetworkPolicy - Pod 간 통신 제어
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-network-policy
namespace: production
spec:
podSelector:
matchLabels:
app: api-server
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
- to: # DNS 허용
- namespaceSelector: {}
ports:
- protocol: UDP
port: 53
---
# Pod Security Standards
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:latest
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
resources:
limits:
memory: '256Mi'
cpu: '500m'
8. 개발자를 위한 보안 체크리스트
8-1. HTTPS 강제와 HSTS
# Nginx 설정
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# HSTS 헤더 (2년, 서브도메인 포함, preload)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# TLS 설정
ssl_protocols TLSv1.3 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
}
8-2. CORS 설정
// Express.js - 안전한 CORS 설정
const cors = require('cors')
const corsOptions = {
origin: ['https://app.example.com', 'https://admin.example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400, // Preflight 캐시 24시간
}
app.use(cors(corsOptions))
// 절대 이렇게 하지 마세요:
// app.use(cors()); // 모든 오리진 허용 = 보안 취약
8-3. Rate Limiting
// Express.js - Rate Limiting
const rateLimit = require('express-rate-limit')
// 일반 API
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15분
max: 100, // 최대 100 요청
message: 'Too many requests, please try again later.',
standardHeaders: true,
legacyHeaders: false,
})
// 로그인 엔드포인트 (더 엄격)
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
message: 'Too many login attempts, please try again after 15 minutes.',
})
app.use('/api/', apiLimiter)
app.use('/api/login', loginLimiter)
8-4. Input Validation
// Joi를 사용한 입력 검증
const Joi = require('joi')
const userSchema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
email: Joi.string().email().required(),
password: Joi.string()
.min(12)
.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])/)
.required(),
age: Joi.number().integer().min(13).max(120),
})
app.post('/api/register', (req, res) => {
const result = userSchema.validate(req.body)
if (result.error) {
return res.status(400).json({
error: result.error.details[0].message,
})
}
// 검증 통과 후 처리
})
8-5. SQL Parameterized Queries
// Node.js + PostgreSQL - 안전한 쿼리
const { Pool } = require('pg')
const pool = new Pool()
// 절대 하지 마세요 (SQL Injection 취약):
// const query = `SELECT * FROM users WHERE id = ${userId}`;
// 올바른 방법: Parameterized Query
async function getUser(userId) {
const result = await pool.query('SELECT id, username, email FROM users WHERE id = $1', [userId])
return result.rows[0]
}
// ORM 사용 (Prisma)
async function getUserPrisma(userId) {
return await prisma.user.findUnique({
where: { id: userId },
select: { id: true, username: true, email: true },
})
}
8-6. JWT/OAuth2 보안
// JWT 보안 모범 사례
const jwt = require('jsonwebtoken')
// Access Token: 짧은 만료 시간
const accessToken = jwt.sign({ userId: user.id, role: user.role }, process.env.JWT_SECRET, {
expiresIn: '15m', // 15분
algorithm: 'RS256', // RSA 비대칭 키 사용
issuer: 'api.example.com',
audience: 'app.example.com',
})
// Refresh Token: DB에 저장, 회전(Rotation) 적용
const refreshToken = jwt.sign(
{ userId: user.id, tokenVersion: user.tokenVersion },
process.env.REFRESH_SECRET,
{ expiresIn: '7d' }
)
// 검증 미들웨어
function verifyToken(req, res, next) {
const token = req.headers.authorization?.split(' ')[1]
if (!token) return res.status(401).json({ error: 'No token' })
try {
const decoded = jwt.verify(token, process.env.JWT_PUBLIC_KEY, {
algorithms: ['RS256'],
issuer: 'api.example.com',
audience: 'app.example.com',
})
req.user = decoded
next()
} catch (err) {
return res.status(401).json({ error: 'Invalid token' })
}
}
8-7. 시크릿 관리
# HashiCorp Vault 사용 예시
vault kv put secret/myapp/db \
username="dbuser" \
password="supersecret" \
host="db.internal.example.com"
# 애플리케이션에서 읽기
vault kv get -field=password secret/myapp/db
// AWS Secrets Manager
const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager')
async function getSecret(secretName) {
const client = new SecretsManagerClient({ region: 'us-east-1' })
const response = await client.send(new GetSecretValueCommand({ SecretId: secretName }))
return JSON.parse(response.SecretString)
}
// 사용
const dbCreds = await getSecret('production/database')
8-8. 보안 헤더
// Helmet.js로 보안 헤더 설정
const helmet = require('helmet')
app.use(
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:', 'https:'],
connectSrc: ["'self'", 'https://api.example.com'],
fontSrc: ["'self'", 'https://fonts.gstatic.com'],
objectSrc: ["'none'"],
mediaSrc: ["'none'"],
frameSrc: ["'none'"],
},
},
crossOriginEmbedderPolicy: true,
crossOriginOpenerPolicy: true,
crossOriginResourcePolicy: { policy: 'same-site' },
hsts: { maxAge: 63072000, includeSubDomains: true, preload: true },
noSniff: true, // X-Content-Type-Options: nosniff
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
xssFilter: true, // X-XSS-Protection
})
)
8-9. 의존성 취약점 스캔
# npm audit
npm audit
npm audit fix
# Snyk
npx snyk test
npx snyk monitor
# GitHub Dependabot (자동 PR 생성)
# .github/dependabot.yml
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: 'npm'
directory: '/'
schedule:
interval: 'weekly'
open-pull-requests-limit: 10
reviewers:
- 'security-team'
labels:
- 'dependencies'
- 'security'
종합 보안 체크리스트:
| 항목 | 중요도 | 확인 |
|---|---|---|
| HTTPS 강제 + HSTS | 필수 | - |
| TLS 1.2+ 만 허용 | 필수 | - |
| CORS 화이트리스트 | 필수 | - |
| Rate Limiting | 필수 | - |
| Input Validation | 필수 | - |
| Parameterized Queries | 필수 | - |
| JWT RS256 + 짧은 만료 | 높음 | - |
| Refresh Token Rotation | 높음 | - |
| 시크릿 매니저 사용 | 필수 | - |
| CSP 헤더 | 높음 | - |
| X-Content-Type-Options | 높음 | - |
| Referrer-Policy | 중간 | - |
| 의존성 취약점 스캔 | 필수 | - |
| Dependabot 활성화 | 높음 | - |
| 보안 로깅/모니터링 | 필수 | - |
9. 보안 자격증 로드맵
입문 → 중급 → 고급 경로
입문 (0~1년차):
CompTIA Security+
├── 난이도: ★★☆☆☆
├── 비용: 약 $404 (시험비)
├── 준비 기간: 2~3개월
├── 내용: 보안 기초, 위협, 암호화, 네트워크 보안
└── 커리어 임팩트: IT 보안 입문, 정부/군 분야 필수
중급 (1~3년차):
CEH (Certified Ethical Hacker)
├── 난이도: ★★★☆☆
├── 비용: 약 $1,199 (시험비)
├── 준비 기간: 3~4개월
├── 내용: 해킹 기법, 취약점 분석, 침투 테스트
└── 커리어 임팩트: 보안 분석가, 펜테스터
CISSP (보안 관리자 경로)
├── 난이도: ★★★★☆
├── 비용: 약 $749 (시험비)
├── 준비 기간: 4~6개월 (경력 5년 필요)
├── 내용: 보안 관리, 거버넌스, 리스크 관리
└── 커리어 임팩트: CISO, 보안 아키텍트
고급 (3년차+):
OSCP (Offensive Security Certified Professional)
├── 난이도: ★★★★★
├── 비용: 약 $1,649 (교육 + 시험)
├── 준비 기간: 6~12개월
├── 내용: 24시간 실전 침투 테스트 시험
└── 커리어 임팩트: 시니어 펜테스터, 레드팀
자격증별 상세 비교
| 자격증 | 대상 | 실기 비율 | 갱신 주기 | 평균 연봉 상승 |
|---|---|---|---|---|
| CompTIA Security+ | 입문자 | 20% | 3년 | +15% |
| CEH | 중급자 | 40% | 3년 | +20% |
| CISSP | 관리자 | 0% (이론) | 3년 | +25% |
| OSCP | 전문가 | 100% (실기) | 없음 | +35% |
| GPEN | 펜테스터 | 60% | 4년 | +25% |
| CISM | 매니저 | 0% (이론) | 3년 | +20% |
개발자 추천 경로
웹 개발자:
Security+ → CEH → OSCP → Bug Bounty
클라우드 개발자:
Security+ → AWS Security Specialty → CCSP
DevOps 엔지니어:
Security+ → CKS (Kubernetes Security) → OSCP
10. 퀴즈
지금까지 배운 내용을 확인해 보겠습니다.
Q1. Wireshark에서 Capture Filter와 Display Filter의 가장 큰 차이점은?
정답: 적용 시점이 다릅니다.
- Capture Filter: 캡처 시작 전에 설정하며, BPF 문법을 사용합니다. 커널 레벨에서 필터링하므로 성능이 좋지만 캡처 중 변경할 수 없습니다.
- Display Filter: 캡처 후에 적용하며, Wireshark 자체 문법을 사용합니다. 이미 캡처된 데이터에서 필터링하므로 실시간으로 변경 가능합니다.
대용량 트래픽 환경에서는 Capture Filter로 필요한 트래픽만 캡처하고, Display Filter로 세밀하게 분석하는 것이 모범 사례입니다.
Q2. TLS 트래픽을 Wireshark에서 복호화하려면 어떤 환경변수를 설정해야 하나요?
정답: SSLKEYLOGFILE
SSLKEYLOGFILE 환경변수를 설정하면 Chrome, Firefox 등의 브라우저가 TLS 세션 키를 지정된 파일에 기록합니다. Wireshark에서 이 파일을 지정하면 (Edit - Preferences - Protocols - TLS) TLS로 암호화된 트래픽의 내용을 볼 수 있습니다.
주의: 이 기능은 개발/디버깅 환경에서만 사용해야 하며, 프로덕션 환경에서는 절대 사용하면 안 됩니다.
Q3. SYN Flood DDoS 공격을 Wireshark에서 탐지하려면 어떤 필터를 사용하나요?
정답: tcp.flags.syn == 1 and tcp.flags.ack == 0
이 필터는 ACK 플래그 없이 SYN 플래그만 설정된 패킷을 표시합니다. SYN Flood 공격은 대량의 SYN 패킷을 보내 서버의 TCP 연결 테이블을 가득 채우는 공격입니다.
추가로 Statistics - I/O Graphs에서 이 필터를 적용하면 시간대별 SYN 패킷 급증 패턴을 시각적으로 확인할 수 있습니다.
Q4. 제로 트러스트 보안 모델의 3가지 핵심 원칙은?
정답:
- 명시적 검증 (Verify Explicitly): 모든 요청을 항상 인증하고 인가합니다. 위치, 네트워크에 관계없이 모든 접근을 검증합니다.
- 최소 권한 (Least Privilege): Just-In-Time, Just-Enough-Access 원칙으로 필요한 최소한의 권한만 부여합니다.
- 침해 가정 (Assume Breach): 이미 침해당했다고 가정하고, 횡적 이동을 최소화하며, 엔드투엔드 암호화를 적용합니다.
전통적인 "성과 해자" 모델과 달리, 내부 네트워크도 신뢰하지 않는 것이 핵심입니다.
Q5. DNS Tunneling을 탐지하기 위해 확인해야 할 주요 지표는?
정답:
- 비정상적으로 긴 도메인 이름: 정상 DNS 쿼리는 보통 20-30자인 반면, DNS Tunneling은 50자 이상의 인코딩된 데이터를 포함합니다.
- TXT 레코드 쿼리 급증: DNS Tunneling은 더 많은 데이터를 전송하기 위해 TXT 레코드를 자주 사용합니다.
- 특정 도메인에 대한 비정상적 쿼리 빈도: 같은 도메인에 대해 초당 수십~수백 건의 쿼리가 발생합니다.
Wireshark 필터: dns.qry.name.len > 50 또는 dns.qry.type == 16 (TXT 레코드)으로 탐지할 수 있습니다.
11. 참고 자료
- Wireshark 공식 문서 - wireshark.org/docs
- tcpdump 매뉴얼 - tcpdump.org/manpages
- OWASP Top 10 (2025) - owasp.org/Top10
- NIST Zero Trust Architecture (SP 800-207) - csrc.nist.gov
- IBM Cost of a Data Breach Report 2025 - ibm.com/security
- Nmap Reference Guide - nmap.org/book/man.html
- Metasploit Unleashed - offensive-security.com/metasploit-unleashed
- SANS Network Forensics - sans.org/cyber-security-courses/network-forensics
- Practical Packet Analysis, 3rd Edition - Chris Sanders (No Starch Press)
- The Web Application Hacker's Handbook - Dafydd Stuttard, Marcus Pinto
- CompTIA Security+ Study Guide - comptia.org/certifications/security
- OSCP Certification Guide - offensive-security.com/pwk-oscp
- Suricata IDS Documentation - suricata.readthedocs.io
- Istio Security Documentation - istio.io/latest/docs/concepts/security
- Kubernetes Network Policies - kubernetes.io/docs/concepts/services-networking/network-policies
- OWASP ZAP Documentation - zaproxy.org/docs
- Burp Suite Documentation - portswigger.net/burp/documentation