ЁЯНк рдзрдбрд╛ 03 тАФ Cookies, CSRF, CORS рдЖрдгрд┐ SSRF: browser рдЖрдгрд┐ server рдордзрд▓рд╛ рдЬрдкреВрди рдареЗрд╡рд▓реЗрд▓рд╛ рд╡рд┐рд╢реНрд╡рд╛рд╕
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 16 рдкреИрдХреА рдзрдбрд╛ 03 ┬╖ рдорд╛рдЧреЗ: lesson-02-xss-csp ┬╖ рдкреБрдвреЗ: lesson-04-authentication
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбреЗ 01тАУ02, рдЖрдгрд┐ рдЕрд░реНрдЬ рдЦрд┐рдбрдХреАрдЪрд╛ рд╢реЗрд╡рдЯрдЪрд╛ рдзрдбрд╛. рдЪрд╛рд░ рдирд┐рдпрдВрддреНрд░рдгреЗ, рдПрдХ рдХрд▓реНрдкрдирд╛ тАФ рдХреЛрдг рдХрд╛рдп рдкрд╛рдард╡реВ рд╢рдХрддреЛ, рдЖрдгрд┐ рдХреБрдареВрди:
- session cookie рд▓рд╛ scripts рдЖрдгрд┐ рдЗрддрд░ sites рдкрд╛рд╕реВрди рджреВрд░ рдареЗрд╡рдгрд╛рд░реЗ cookie flags,
- form рд╢рд╛рд│реЗрдЪреНрдпрд╛рдЪ page рд╡рд░реВрди рдЖрд▓рд╛ рд╣реЗ рд╕рд┐рджреНрдз рдХрд░рдгрд╛рд░рд╛ CSRF token,
- рдЗрддрд░ рдХреЛрдгрддреНрдпрд╛ sites рддреБрдордЪреНрдпрд╛ API рдЪреА рдЙрддреНрддрд░реЗ рд╡рд╛рдЪреВ рд╢рдХрддрд╛рдд рддреЗ browsers рдирд╛ рд╕рд╛рдВрдЧрдгрд╛рд░реА CORS allow-list,
- server URL fetch рдХрд░рдгреНрдпрд╛рдЖрдзреА рддреЛ рддрдкрд╛рд╕рдгрд╛рд░рд╛ SSRF рдкрд╣рд╛рд░реЗрдХрд░реА.
sec/demo.py рдордзрд▓реЗ browser() рдЪрд╛рд░рд╣реА рджрд╛рдЦрд╡рддреЗ.
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
рдХрддрд░рд┐рдирд╛рдХрдбреЗ рд╢рд╛рд│реЗрдЪрд╛ рдПрдХ visitor badge рдЖрд╣реЗ (session cookie). Office рддреНрдпрд╛ badge рд▓рд╛ рддреАрди рдирд┐рдпрдо рджреЗрддреЗ:
- HttpOnly тАФ badge рддрд┐рдЪреНрдпрд╛ рдЦрд┐рд╢рд╛рддрдЪ рд░рд╛рд╣рддреЛ; рдХреЛрдгрддреНрдпрд╛рд╣реА рдЕрдиреЛрд│рдЦреА рд╡реНрдпрдХреНрддреАрдЪреА рдЪрд┐рдареНрдареА рддреЛ рд╡рд╛рдЪреВ рд╢рдХрдд рдирд╛рд╣реА.
- Secure тАФ badge рдлрдХреНрдд рд╢рд╛рд│реЗрдЪреНрдпрд╛ рдмрдВрдж corridors рдордзреНрдпреЗрдЪ рджрд╛рдЦрд╡рд▓рд╛ рдЬрд╛рддреЛ (HTTPS).
- SameSite тАФ badge рдлрдХреНрдд рдХрддрд░рд┐рдирд╛ рд╕реНрд╡рддрдГ office рдордзреНрдпреЗ рдпреЗрддреЗ рддреЗрд╡реНрд╣рд╛рдЪ рджрд╛рдЦрд╡рд▓рд╛ рдЬрд╛рддреЛ, рджреБрд╕рд▒реНрдпрд╛ рд╢рд╛рд│реЗрдЪреЗ рдкрддреНрд░ "рддрд┐рдЪреНрдпрд╛ рд╡рддреАрдиреЗ" рдХрд╛рд╣реА рдорд╛рдЧрддреЗ рддреЗрд╡реНрд╣рд╛ рдирд╛рд╣реА.
рдкреНрд░рддреНрдпреЗрдХ рдЕрдзрд┐рдХреГрдд form рд╡рд░ рдХрддрд░рд┐рдирд╛рдЪреНрдпрд╛ badge рд╢реА рдЬреБрд│рдгрд╛рд░рд╛ рдПрдХ рдЧреБрдкреНрдд рд╢рд┐рдХреНрдХрд╛ рдЕрд╕рддреЛ (CSRF token). рджреБрд╕рд▒реНрдпрд╛ рд╢рд╛рд│реЗрдиреЗ рдмрдирд╡рд▓реЗрд▓реНрдпрд╛ рдЦреЛрдЯреНрдпрд╛ form рд╡рд░ рдЬреБрд│рдгрд╛рд░рд╛ рд╢рд┐рдХреНрдХрд╛ рдирд╕рддреЛ, рдореНрд╣рдгреВрди office рддреЛ рдирд╛рдХрд╛рд░рддреЗ.
Office рдЖрдкрд▓реА рдЙрддреНрддрд░реЗ рд╡рд╛рдЪреВ рд╢рдХрдгрд╛рд▒реНрдпрд╛ рдореИрддреНрд░реАрдкреВрд░реНрдг рд╢рд╛рд│рд╛рдВрдЪреА рдпрд╛рджреА рдкрдг рдареЗрд╡рддреЗ (CORS). рд▓рдХреНрд╖рд╛рдд рдареЗрд╡рд╛: рддреА рдпрд╛рджреА рд╡рд╛рдЪрдХрд╛рдВрдЪреЗ browsers рддрдкрд╛рд╕рддрд╛рдд тАФ office рдирд╛рд╣реА. рддреНрдпрд╛рдореБрд│реЗ рдХреЛрдгреА рдереЗрдЯ office рдкрд░реНрдпрдВрдд рдЪрд╛рд▓рдд рдпреЗрдгреНрдпрд╛рдкрд╛рд╕реВрди рдерд╛рдВрдмрдд рдирд╛рд╣реА.
рд╢реЗрд╡рдЯреА, office рдХрдзреА рдХрдзреА visitors рд╕рд╛рдареА рд╡рд╕реНрддреВ рдЖрдгрддреЗ: "рдХреГрдкрдпрд╛ рдпрд╛ рдкрддреНрддреНрдпрд╛рд╡рд░реВрди рдорд╛рдЭрд╛ photo рдЖрдгрд╛." рдЕрдзрд┐рдХрд╛рд░реА рдЖрдзреА рдкрддреНрддрд╛ рддрдкрд╛рд╕рддреЛ. рдлрдХреНрдд рд╢рд╛рд│реЗрдЪреЗ photo рджреБрдХрд╛рди рдЪрд╛рд▓рддреЗ. рдЗрдорд╛рд░рддреАрдЪреНрдпрд╛ рдЖрддрд▓реЗ рдкрддреНрддреЗ тАФ server room, staff рдЪреА рдХрд┐рд▓реНрд▓реНрдпрд╛рдВрдЪреА рдкреЗрдЯреА тАФ рдиреЗрд╣рдореА рдирд╛рдХрд╛рд░рд▓реЗ рдЬрд╛рддрд╛рдд. рд╣реА рддрдкрд╛рд╕рдгреА SSRF рдерд╛рдВрдмрд╡рддреЗ.
ЁЯЧ║я╕П рдЖрдХреГрддреА
flowchart LR
b["ЁЯМР Katrina's browser<br/>cookie: HttpOnly ┬╖ Secure ┬╖ SameSite=Lax"]
ev["ЁЯП┤ other site<br/>fake form, no token"]
subgraph srv["ЁЯПл school server"]
csrf["ЁЯФП CSRF check<br/>token matches session?"]
cors["ЁЯУЬ CORS header<br/>allow-list of origins"]
ssrf["ЁЯзн SSRF guard<br/>https ┬╖ allow-listed host ┬╖ no private IPs"]
end
b -->|"form + token"| csrf
ev -->|"no token"| csrf
cors -.->|"browser enforces"| b
ssrf -->|"allowed"| shop["ЁЯЦ╝я╕П images.school.example"]
ssrf -.->|"refused"| meta["ЁЯФТ 169.254.169.254<br/>10.0.0.5"]
ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/security/lesson-diagrams.html#l03
тЭУ рдХрд╛рдп
- Session cookie flags (
cookie_ok()рддрд┐рдиреНрд╣реА рддрдкрд╛рд╕рддреЗ):HttpOnlyтАФ JavaScript cookie рд╡рд╛рдЪреВ рд╢рдХрдд рдирд╛рд╣реА, рддреНрдпрд╛рдореБрд│реЗ XSS bug рддреА copy рдХрд░реВ рд╢рдХрдд рдирд╛рд╣реА.SecureтАФ рдлрдХреНрдд HTTPS рд╡рд░реВрдирдЪ рдкрд╛рдард╡рд▓реА рдЬрд╛рддреЗ.SameSiteтАФ рджреБрд╕рд▒реНрдпрд╛ site рд╡рд░ рд╕реБрд░реВ рдЭрд╛рд▓реЗрд▓реНрдпрд╛ requests рд╕реЛрдмрдд browser cookie рдХрдзреА рдкрд╛рдард╡рддреЛ:Strict(рдХрдзреАрдЪ рдирд╛рд╣реА),Lax(рдлрдХреНрдд link click рд╕рд╛рд░рдЦреНрдпрд╛ safe methods рдЕрд╕рд▓реЗрд▓реНрдпрд╛ top-level navigations рд╡рд░ тАФ Chrome рдЖрдгрд┐ Edge SameSite рдирд╕рд▓реЗрд▓реНрдпрд╛ cookie рд▓рд╛ Lax рдорд╛рдирддрд╛рдд),None(рдиреЗрд╣рдореА тАФ рдЖрдгрд┐ рдордЧSecureрдЖрд╡рд╢реНрдпрдХ).
- CSRF (cross-site request forgery) тАФ рджреБрд╕рд░реА site рдкреАрдбрд┐рддрд╛рдЪреНрдпрд╛ browser рдХрдбреВрди рддреБрдордЪреНрдпрд╛ site рд▓рд╛
request рдкрд╛рдард╡рд╛рдпрд▓рд╛ рд▓рд╛рд╡рддреЗ, рдЖрдгрд┐ browser рдкреАрдбрд┐рддрд╛рдЪреА cookie рдЬреЛрдбрддреЛ. рдЙрдкрд╛рдп: session рд╢реА рдЬреЛрдбрд▓реЗрд▓рд╛
CSRF token, state рдмрджрд▓рдгрд╛рд▒реНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ request рд╡рд░ рддрдкрд╛рд╕рд▓реЗрд▓рд╛.
csrf_token()рд╣рд╛ session id рдЪрд╛ HMAC рдЖрд╣реЗ;csrf_ok()constant time рдордзреНрдпреЗ рддреБрд▓рдирд╛ рдХрд░рддреЗ.SameSite=LaxрдХрд┐рдВрд╡рд╛Strictрд╣рд╛ рдордЬрдмреВрдд рджреБрд╕рд░рд╛ рдерд░ рдЖрд╣реЗ. OWASP Top 10 (2021) CSRF рд▓рд╛ A01:2021 тАУ Broken Access Control рд╢реА рдЬреЛрдбрддреЗ. - CORS (cross-origin resource sharing) тАФ default рдиреЗ
evil.exampleрд╡рд░рдЪреЗ page рддреБрдордЪреНрдпрд╛ API рдЪреА рдЙрддреНрддрд░реЗ рд╡рд╛рдЪреВ рд╢рдХрдд рдирд╛рд╣реА (same-origin policy). CORS headers origins рдЪреНрдпрд╛ allow-list рд╕рд╛рдареА рд╣реА рдмрдВрджреА рд╕реИрд▓ рдХрд░рддрд╛рдд. CORS browsers enforce рдХрд░рддрд╛рдд, рддреБрдордЪрд╛ server рдирд╛рд╣реА. Script,curlрдХрд┐рдВрд╡рд╛ рджреБрд╕рд░рд╛ server рддреНрдпрд╛рдХрдбреЗ рджреБрд░реНрд▓рдХреНрд╖ рдХрд░рддреЛ. рд╣реЗ authentication рдХрд┐рдВрд╡рд╛ authorization рдирд┐рдпрдВрддреНрд░рдг рдирд╛рд╣реА тАФ рдкреНрд░рддреНрдпреЗрдХ endpoint рдЕрдЬреВрдирд╣реА session рдЖрдгрд┐ permissions рддрдкрд╛рд╕рддреЛ. - SSRF (server-side request forgery) тАФ user рдиреЗ рджрд┐рд▓реЗрд▓рд╛ URL server fetch рдХрд░рддреЛ
(photo, webhook, PDF preview). Crafted URL рддреНрдпрд╛рд▓рд╛ рддреБрдордЪреНрдпрд╛ network рдЪреНрдпрд╛ рдЖрдд рд╡рд│рд╡рддреЛ:
http://10.0.0.5/admin, рдХрд┐рдВрд╡рд╛169.254.169.254рд╡рд░рдЪреА cloud instance metadata service, рдЬреА credentials рджреЗрдК рд╢рдХрддреЗ. A10:2021 тАУ SSRF.ssrf_ok()рдлрдХреНрддhttps, рдлрдХреНрдд allow-list рдордзрд▓реЗ hosts рдЪрд╛рд▓реВ рджреЗрддреЗ, рдЖрдгрд┐ private, loopback рдХрд┐рдВрд╡рд╛ link-local addresses рдХрдзреАрдЪ рдирд╛рд╣реАрдд. - IMDSv2 тАФ AWS EC2 рд╡рд░ metadata service рд╕рд╛рдареА session tokens рдЖрд╡рд╢реНрдпрдХ рдХрд░рд╛
(
HttpTokens=required). Token рд╕рд╛рдареА рдЖрдзреАPUTrequest рд▓рд╛рдЧрддреЗ, рдЬреА рдмрд╣реБрддреЗрдХ SSRF bugs рдкрд╛рдард╡реВ рд╢рдХрдд рдирд╛рд╣реАрдд. 1 рдЪрд╛ hop limit token рдЪреЗ рдЙрддреНрддрд░ instance рдЪреНрдпрд╛ рдкрд▓реАрдХрдбреЗ рдЬрд╛рдгреНрдпрд╛рдкрд╛рд╕реВрди рдерд╛рдВрдмрд╡рддреЛ.
ЁЯдФ рдХрд╛
рдХрд╛рд░рдг browser рдорджрдд рдХрд░рдгрд╛рд░рд╛ рдЖрд╣реЗ: рддреЛ рддреБрдордЪреНрдпрд╛ site рд╡рд░рдЪреНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ request рд▓рд╛ cookies рдЬреЛрдбрддреЛ, рдЕрдЧрджреА рджреБрд╕рд▒реНрдпрд╛ site рдиреЗ рд╕реБрд░реВ рдХреЗрд▓реЗрд▓реНрдпрд╛ requests рд▓рд╛ рд╕реБрджреНрдзрд╛. рдЖрдгрд┐ servers рдорджрдд рдХрд░рдгрд╛рд░реЗ рдЖрд╣реЗрдд: рддреНрдпрд╛рдВрдирд╛ рдЬреЗ рд╕рд╛рдВрдЧрд┐рддрд▓реЗ рддреЗ рддреЗ fetch рдХрд░рддрд╛рдд. рдЗрдерд▓реЗ рдкреНрд░рддреНрдпреЗрдХ рдирд┐рдпрдВрддреНрд░рдг рддреА рдорджрдд рдЕрдЯреАрд╡рд░ рджреЗрддреЗ тАФ рдлрдХреНрдд рд╣реА site, рдлрдХреНрдд рд╣рд╛ form, рдлрдХреНрдд рд╣рд╛ host. рддреА рдирд╕рддреАрд▓ рддрд░, login рдХреЗрд▓реЗрд▓реЗ рдкрд╛рд▓рдХ рдЪреБрдХреАрдЪреЗ page рдЙрдШрдбреВрди рдирдХрд│рдд рд╕реНрд╡рддрдГрдЪрд╛ password рдмрджрд▓реВ рд╢рдХрддрд╛рдд, рдЖрдгрд┐ photo preview рддреБрдордЪреА cloud credentials рд╡рд╛рдЪреВ рд╢рдХрддреЛ.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
sec/web.py рдордзреНрдпреЗ: cookie_ok(header) Set-Cookie header рд╡рд╛рдЪрддреЗ рдЖрдгрд┐
рдирд╕рд▓реЗрд▓реЗ flags рдпрд╛рджреАрдд рджреЗрддреЗ. csrf_token(session_id) рдореНрд╣рдгрдЬреЗ HMAC-SHA256(SECRET, session_id), 16
hex рдЕрдХреНрд╖рд░рд╛рдВрдкрд░реНрдпрдВрдд рдХрд╛рдкрд▓реЗрд▓реЗ; csrf_ok() hmac.compare_digest рдиреЗ рддреБрд▓рдирд╛ рдХрд░рддреЗ. cors_ok(origin) рдЕрдЪреВрдХ
allow-list (https://school.example) рддрдкрд╛рд╕рддреЗ. ssrf_ok(url) URL parse рдХрд░рддреЗ, https рдирд╕рд▓реЗрд▓реЗ
рд╕рдЧрд│реЗ рдирд╛рдХрд╛рд░рддреЗ, private, loopback рдХрд┐рдВрд╡рд╛ link-local IP addresses рдирд╛рдХрд╛рд░рддреЗ, рдордЧ
host allow-list (images.school.example) рдордзреНрдпреЗ рдЕрд╕рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдХрд░рддреЗ.
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
python3 sec/demo.py browser
python3 - <<'EOF'
import sys; sys.path.insert(0, "sec"); from web import cookie_ok, csrf_token, csrf_ok, cors_ok, ssrf_ok
for h in ("sid=1; Secure", "sid=1; HttpOnly; Secure; SameSite=Strict"):
print(f"{h:<42} тЖТ {cookie_ok(h)}")
print("Katrina's token works for Dipika's session тЖТ", csrf_ok("sess-dipika", csrf_token("sess-katrina")))
print("http://school.example (not https) тЖТ", cors_ok("http://school.example"))
for u in ("https://127.0.0.1/", "https://192.168.1.1/router", "file:///etc/passwd", "https://images.school.example.evil.example/x"):
print(f"{u:<45} тЖТ {ssrf_ok(u)}")
EOF
тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ
browser рд╣реЗ print рдХрд░рддреЗ:
тФАтФА Set-Cookie: session=abc123 тЖТ missing httponly, secure, samesite
тФАтФА Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax тЖТ ok
тФАтФА CSRF: the form carries a token tied to the session тЖТ valid True ┬╖ forged False ┬╖ missing False
тФАтФА CORS allow-list: https://school.example тЖТ True ┬╖ https://evil.example тЖТ False
тФАтФА SSRF: the server fetches a photo URL a user typed тАФ check it first
https://images.school.example/p/7.jpg тЖТ (True, 'allowed')
http://images.school.example/p/7.jpg тЖТ (False, 'only https')
https://169.254.169.254/latest/ тЖТ (False, 'private / internal address')
https://10.0.0.5/admin тЖТ (False, 'private / internal address')
https://evil.example/x тЖТ (False, 'host not on the allow-list')
рддреБрдордЪрд╛ snippet рд╣реЗ print рдХрд░рддреЛ:
sid=1; Secure тЖТ (False, ['httponly', 'samesite'])
sid=1; HttpOnly; Secure; SameSite=Strict тЖТ (True, [])
Katrina's token works for Dipika's session тЖТ False
http://school.example (not https) тЖТ False
https://127.0.0.1/ тЖТ (False, 'private / internal address')
https://192.168.1.1/router тЖТ (False, 'private / internal address')
file:///etc/passwd тЖТ (False, 'only https')
https://images.school.example.evil.example/x тЖТ (False, 'host not on the allow-list')
ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ
рдПрдХрд╛ session рд╕рд╛рдареА рдмрдирд╡рд▓реЗрд▓рд╛ token рджреБрд╕рд▒реНрдпрд╛ session рд╕рд╛рдареА рдирд┐рд░реБрдкрдпреЛрдЧреА рдЕрд╕рддреЛ. CORS рдпрд╛рджреА рдЕрдЪреВрдХ
рдЬреБрд│рдгреА рдорд╛рдЧрддреЗ тАФ https:// рдРрд╡рдЬреА http:// рд╕реБрджреНрдзрд╛ рдирд╛рдХрд╛рд░рд▓реЗ рдЬрд╛рддреЗ. рдЖрдгрд┐ SSRF рдкрд╣рд╛рд░реЗрдХрд░реА рдЕрд╕рд╛ host
рдирд╛рдХрд╛рд░рддреЛ рдЬреЛ рдкрд░рд╡рд╛рдирдЧреА рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдирд╛рд╡рд╛рдиреЗ рдлрдХреНрдд рд╕реБрд░реВ рд╣реЛрддреЛ: рддрдкрд╛рд╕рдгреА рдкреВрд░реНрдг host рдЪреА рддреБрд▓рдирд╛ рдХрд░рддреЗ,
prefix рдЪреА рдирд╛рд╣реА. рдореБрдЦреНрдп рдХрд╛рдо allow-list рдХрд░рддреЗ; private-address рдирд┐рдпрдо рд╣реЗ рджреБрд╕рд░реЗ рдХреБрд▓реВрдк рдЖрд╣реЗ.
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
HttpOnlyрдирд╕рд▓реЗрд▓реНрдпрд╛ session cookies (рдордЧ XSS рддреНрдпрд╛ copy рдХрд░рддреЗ) рдХрд┐рдВрд╡рд╛Secureрдирд╕рд▓реЗрд▓реНрдпрд╛- рдлрдХреНрдд SameSite рд╡рд░ рдЕрд╡рд▓рдВрдмреВрди рд░рд╛рд╣рдгреЗ тАФ subdomains рдПрдХрдЪ site рдорд╛рдирд▓реЗ рдЬрд╛рддрд╛рдд, рдЖрдгрд┐ рдЬреБрдиреЗ browsers рд╡реЗрдЧрд│реЗ рд╡рд╛рдЧрддрд╛рдд
Access-Control-Allow-Credentials: trueрд╕рд╣ рдХреЛрдгрддрд╛рд╣реАOriginрдкрд░рдд рджрд╛рдЦрд╡рдгрд╛рд░рд╛ CORS- CORS рд▓рд╛ API рдЪреА security рдорд╛рдирдгреЗ ("рдлрдХреНрдд рдЖрдордЪреАрдЪ site call рдХрд░реВ рд╢рдХрддреЗ") тАФ рддреЗ рддрд╕реЗ рдирд╛рд╣реА
- рдлрдХреНрдд block-list рдЕрд╕рд▓реЗрд▓реА SSRF рддрдкрд╛рд╕рдгреА, рдХрд┐рдВрд╡рд╛ DNS resolution рдЖрдгрд┐ redirects рдирдВрддрд░ рдкреБрдиреНрд╣рд╛ рди рдХреЗрд▓реЗрд▓реА рдирд╛рд╡рд╛рдЪреА рддрдкрд╛рд╕рдгреА
- IMDSv1 рдЪрд╛рд▓реВ рдареЗрд╡рдгреЗ, рддреНрдпрд╛рдореБрд│реЗ рдлрдХреНрдд GET рдХрд░рдгрд╛рд░рд╛ SSRF bug instance credentials рд╡рд╛рдЪреВ рд╢рдХрддреЛ
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд
рдЦрд▒реНрдпрд╛ account рд╡рд░ тАФ Flask рдЖрдгрд┐ Express рдордзрд▓реЗ cookie flags:
app.config.update(SESSION_COOKIE_HTTPONLY=True, SESSION_COOKIE_SECURE=True, SESSION_COOKIE_SAMESITE="Lax")
app.use(session({ secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false,
cookie: { httpOnly: true, secure: true, sameSite: 'lax', maxAge: 30 * 60 * 1000 } }));
CSRF middleware тАФ Django рдордзреНрдпреЗ рддреЗ default рдиреЗ рдЪрд╛рд▓реВ рдЕрд╕рддреЗ (MIDDLEWARE рдордзреНрдпреЗ CsrfViewMiddleware рдЖрдгрд┐
рдкреНрд░рддреНрдпреЗрдХ form рдордзреНрдпреЗ {% csrf_token %} рдареЗрд╡рд╛); Flask Flask-WTF рд╡рд╛рдкрд░рддреЗ:
from flask_wtf.csrf import CSRFProtect
CSRFProtect(app) # every POST/PUT/PATCH/DELETE must carry the token
рдЕрдЪреВрдХ allow-list рд╕рд╣ CORS (Express cors package). рд▓рдХреНрд╖рд╛рдд рдареЗрд╡рд╛: рд╣реЗ рдлрдХреНрдд browsers рдирд╛
рддреЗ рдХрд╛рдп рд╡рд╛рдЪреВ рд╢рдХрддрд╛рдд рддреЗ рд╕рд╛рдВрдЧрддреЗ; route рдЕрдЬреВрдирд╣реА session рдЖрдгрд┐ permissions рддрдкрд╛рд╕рддреЛ:
import cors from 'cors';
app.use('/api', cors({ origin: ['https://school.example'], credentials: true }));
Resolved addresses рд╕реБрджреНрдзрд╛ рддрдкрд╛рд╕рдгрд╛рд░рд╛ SSRF рдкрд╣рд╛рд░реЗрдХрд░реА (Python), рдЬреЛ redirects рдирд╛рдХрд╛рд░рддреЛ:
import ipaddress, socket
from urllib.parse import urlparse
ALLOWED = {"images.school.example"}
def safe_to_fetch(url):
u = urlparse(url)
if u.scheme != "https" or u.hostname not in ALLOWED: return False
for info in socket.getaddrinfo(u.hostname, 443):
ip = ipaddress.ip_address(info[4][0])
if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved: return False
return True
# then fetch with redirects off, e.g. requests.get(url, allow_redirects=False, timeout=5)
EC2 instance рд╡рд░ 1 рдЪреНрдпрд╛ hop limit рд╕рд╣ IMDSv2 рдЖрд╡рд╢реНрдпрдХ рдХрд░рд╛:
aws ec2 modify-instance-metadata-options --instance-id i-0123456789abcdef0 \
--http-tokens required --http-put-response-hop-limit 1 --http-endpoint enabled
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: cookie flags рдПрдХрджрд╛рдЪ, framework config рдордзреНрдпреЗ set рдХрд░рд╛. рд╕реНрд╡рддрдГрдЪреЗ рд▓рд┐рд╣рд┐рдгреНрдпрд╛рдРрд╡рдЬреА framework рдЪреЗ CSRF рд╕рдВрд░рдХреНрд╖рдг рдЪрд╛рд▓реВ рдХрд░рд╛. рдмрд╛рд╣реЗрд░ рдЬрд╛рдгрд╛рд░реЗ fetches allow-list рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдПрдХрд╛рдЪ function рдорд╛рдЧреЗ рдареЗрд╡рд╛ тАФ рдХрд┐рдВрд╡рд╛ рдлрдХреНрдд internet рдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪреВ рд╢рдХрдгрд╛рд▒реНрдпрд╛ proxy рдорд╛рдЧреЗ тАФ рдЖрдгрд┐ рддреБрдордЪреНрдпрд╛ launch templates рдордзреНрдпреЗ IMDSv2 default рдХрд░рд╛.
тПня╕П рдкреБрдвреЗ
рдЕрд░реНрдЬ рдЦрд┐рдбрдХреА рдкреВрд░реНрдг рдЭрд╛рд▓реА. рдЖрддрд╛ рдУрд│рдЦрдкрддреНрд░ рдЦрд┐рдбрдХреА: рд╢рд╛рд│реЗрд▓рд╛ рдХрд╕реЗ рдХрд│рддреЗ рдХреА рд╣реА рдЦрд░реЛрдЦрд░ рдХрддрд░рд┐рдирд╛рдЪ рдЖрд╣реЗ тАФ рдЪреЛрд░рд╛рд▓рд╛ рд╡рд╛рдкрд░рддрд╛ рдпреЗрдгрд╛рд░ рдирд╛рд╣реАрдд рдЕрд╕реЗ рд╕рд╛рдард╡рд▓реЗрд▓реЗ passwords, MFA рдЖрдгрд┐ passkeys, sessions рдЖрдгрд┐ tokens.
git checkout lesson-04-authentication