ЁЯФС рдзрдбрд╛ 04 тАФ Authentication: рддреБрдореНрд╣реА рдХреЛрдг рдЖрд╣рд╛рдд рд╣реЗ рд╕рд┐рджреНрдз рдХрд░рдгреЗ
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 16 рдкреИрдХреА рдзрдбрд╛ 04 ┬╖ рдорд╛рдЧреЗ: lesson-03-browser-server ┬╖ рдкреБрдвреЗ: lesson-05-oauth-oidc
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбреЗ 01тАУ03, рдЖрдгрд┐ рдУрд│рдЦрдкрддреНрд░ рдЦрд┐рдбрдХреАрдЪрд╛ рдкрд╣рд┐рд▓рд╛ рдзрдбрд╛. рд╢рд╛рд│реЗрдиреЗ рдЕрд╕реЗ рдХрд╛рд╣реАрддрд░реА рд╕рд╛рдард╡рд▓реЗ рдкрд╛рд╣рд┐рдЬреЗ
рдЬреНрдпрд╛рдиреЗ рддреА password рддрдкрд╛рд╕реВ рд╢рдХреЗрд▓ password рди рд╕рд╛рдард╡рддрд╛. рддреБрдореНрд╣реА password рд▓рд╛ salt рдЖрдгрд┐
рд╣рд│реВ key-derivation function (PBKDF2) рдиреЗ hash рдХрд░рддрд╛, рдмрд░реЛрдмрд░ рдЖрдгрд┐ рдЪреБрдХреАрдЪреЗ login рддрдкрд╛рд╕рддрд╛,
рдЖрдгрд┐ рдЪреЛрд░рд▓реЗрд▓рд╛ hash рдХрд┐рддреА рд╡реЗрдЧрд╛рдиреЗ рдУрд│рдЦрддрд╛ рдпреЗрддреЛ рддреНрдпрд╛рдЪреА рддреБрд▓рдирд╛ рдХрд░рддрд╛. рдордЧ MFA, passkeys, рдЖрдгрд┐
sessions рд╡рд┐рд░реБрджреНрдз tokens. sec/demo.py рдордзрд▓реЗ passwords() рд╣реЗ рджрд╛рдЦрд╡рддреЗ.
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
рдУрд│рдЦрдкрддреНрд░ рдЦрд┐рдбрдХреАрд▓рд╛ рджрд░рд░реЛрдЬ рд╕рдХрд╛рд│реА рдХрддрд░рд┐рдирд╛рдЪрд╛ рдЧреБрдкреНрдд рд╢рдмреНрдж рддрдкрд╛рд╕рд╛рд╡рд╛ рд▓рд╛рдЧрддреЛ. рдкрдг рддрд┐рдиреЗ рддреЛ рд╢рдмреНрдж рдХрдзреАрд╣реА рд▓рд┐рд╣реВрди рдареЗрд╡рддрд╛ рдХрд╛рдорд╛ рдирдпреЗ тАФ рдЪреЛрд░ рд╡рд╣реА рдЪреЛрд░реВ рд╢рдХрддреЛ.
рдореНрд╣рдгреВрди рдЦрд┐рдбрдХреА рдПрдХ рд╣реБрд╢рд╛рд░ рдЧреЛрд╖реНрдЯ рдХрд░рддреЗ. рддреА рдХрддрд░рд┐рдирд╛рдЪрд╛ рд╢рдмреНрдж рдШреЗрддреЗ, рдлрдХреНрдд рддрд┐рдЪреНрдпрд╛рд╕рд╛рдареАрдЪреЗ рдПрдХ random sticker рдЬреЛрдбрддреЗ (salt), рдЖрдгрд┐ рджреЛрдиреНрд╣реА рдПрдХрд╛ рдЦреВрдк рд╣рд│реВ рджрд│рдгрд╛рд▒реНрдпрд╛ рдпрдВрддреНрд░рд╛рдд рдШрд╛рд▓рддреЗ. рдпрдВрддреНрд░ handle 200,000 рд╡реЗрд│рд╛ рдлрд┐рд░рд╡рддреЗ рдЖрдгрд┐ рдПрдХ рдбрд╛рдЧ рджреЗрддреЗ. рдЦрд┐рдбрдХреА sticker рдЖрдгрд┐ рдбрд╛рдЧ рд▓рд┐рд╣реВрди рдареЗрд╡рддреЗ.
рдЙрджреНрдпрд╛ рдХрддрд░рд┐рдирд╛ рдкреБрдиреНрд╣рд╛ рддрд┐рдЪрд╛ рд╢рдмреНрдж рд╕рд╛рдВрдЧрддреЗ. рдЦрд┐рдбрдХреА рддрд┐рдЪреЗ sticker рдЬреЛрдбрддреЗ, рдкреБрдиреНрд╣рд╛ рджрд│рддреЗ, рдЖрдгрд┐ рдбрд╛рдЧрд╛рдВрдЪреА рддреБрд▓рдирд╛ рдХрд░рддреЗ. рддреЛрдЪ рдбрд╛рдЧ тЖТ рддреЛрдЪ рд╢рдмреНрдж.
рд╡рд╣реА рдЪреЛрд░рдгрд╛рд▒реНрдпрд╛ рдЪреЛрд░рд╛рд▓рд╛ рдлрдХреНрдд stickers рдЖрдгрд┐ рдбрд╛рдЧ рджрд┐рд╕рддрд╛рдд. рд╢рдмреНрдж рд╢реЛрдзрдгреНрдпрд╛рд╕рд╛рдареА рддреНрдпрд╛рд▓рд╛ рдЕрдВрджрд╛рдЬ рдХрд░рд╛рд╡рд╛ рд▓рд╛рдЧрддреЛ, 200,000 рд╡реЗрд│рд╛ рджрд│рд╛рд╡реЗ рд▓рд╛рдЧрддреЗ, рддреБрд▓рдирд╛ рдХрд░рд╛рд╡реА рд▓рд╛рдЧрддреЗ тАФ рдкреНрд░рддреНрдпреЗрдХ рдЕрдВрджрд╛рдЬрд╛рд╕рд╛рдареА, рдкреНрд░рддреНрдпреЗрдХ рд╡рд┐рджреНрдпрд╛рд░реНрдереНрдпрд╛рд╕рд╛рдареА, рдХрд╛рд░рдг рдкреНрд░рддреНрдпреЗрдХрд╛рдЪреЗ sticker рд╡реЗрдЧрд│реЗ рдЕрд╕рддреЗ. рд╣реЗ рдореБрджреНрджрд╛рдордЪ рд╣рд│реВ рдЖрд╣реЗ.
рдЖрдгрд┐ рдорд╣рддреНрддреНрд╡рд╛рдЪреНрдпрд╛ рджрд╛рд░рд╛рдВрд╕рд╛рдареА рдЦрд┐рдбрдХреА рджреБрд╕рд░реА рдЧреЛрд╖реНрдЯрд╣реА рдорд╛рдЧрддреЗ тАФ рдХрддрд░рд┐рдирд╛рдЪреНрдпрд╛ phone рд╡рд░рдЪрд╛ code, рдХрд┐рдВрд╡рд╛ рддрд┐рдЪреА passkey. рдлрдХреНрдд рдЪреЛрд░рд▓реЗрд▓рд╛ рд╢рдмреНрдж рдкреБрд░реЗрд╕рд╛ рдирд╕рддреЛ.
ЁЯЧ║я╕П рдЖрдХреГрддреА
flowchart LR
pw["ЁЯФд password"] --> kdf["ЁЯРв slow KDF<br/>PBKDF2-SHA256 ├Ч 200,000<br/>(Argon2id in production)"]
salt["ЁЯО▓ random salt<br/>16 bytes, per user"] --> kdf
kdf --> store["ЁЯУТ stored: salt + iterations + hash<br/>never the password"]
login["ЁЯФд login attempt"] --> kdf2["ЁЯРв same KDF, same salt"]
store --> kdf2
kdf2 --> cmp{"constant-time<br/>compare"}
cmp -->|"match"| mfa["ЁЯУ▒ second factor<br/>TOTP ┬╖ passkey"]
mfa --> sess["ЁЯНк new session id"]
ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/security/lesson-diagrams.html#l04
тЭУ рдХрд╛рдп
- Authentication тАФ рддреБрдореНрд╣реА рдХреЛрдг рдЖрд╣рд╛рдд рд╣реЗ рд╕рд┐рджреНрдз рдХрд░рдгреЗ. (Authorization тАФ рддреБрдореНрд╣реА рдХрд╛рдп рдХрд░реВ рд╢рдХрддрд╛ тАФ рд╣рд╛ рдзрдбрд╛ 06 рдЖрд╣реЗ.) рдЗрдерд▓реНрдпрд╛ рдЪреБрдХрд╛ рдореНрд╣рдгрдЬреЗ A07:2021 тАУ Identification and Authentication Failures.
- Passwords рдХрдзреАрд╣реА рд╕рд╛рдард╡реВ рдирдХрд╛ тАФ рд╕рд╛рдзреНрдпрд╛ text рдордзреНрдпреЗ рдирд╛рд╣реА, encrypted рдирд╛рд╣реА (key leak рд╣реЛрдК рд╢рдХрддреЗ), рдЬрд▓рдж hash (MD5, SHA-256) рдиреЗ рд╕реБрджреНрдзрд╛ рдирд╛рд╣реА.
- Salt тАФ random bytes, рдкреНрд░рддреНрдпреЗрдХ user рд╕рд╛рдареА рд╡реЗрдЧрд│реЗ, hash рдЪреНрдпрд╛ рд╢реЗрдЬрд╛рд░реА рд╕рд╛рдард╡рд▓реЗрд▓реЗ. рдПрдХрдЪ password рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рджреЛрди users рдирд╛ рд╡реЗрдЧрд╡реЗрдЧрд│реЗ hashes рдорд┐рд│рддрд╛рдд, рдЖрдгрд┐ рдЪреЛрд░ рд╕рдЧрд│реНрдпрд╛рдВрд╕рд╛рдареА рдЖрдзреАрдЪ рдЧрдгрди рдХреЗрд▓реЗрд▓реА рдПрдХ table рд╡рд╛рдкрд░реВ рд╢рдХрдд рдирд╛рд╣реА.
- рд╣рд│реВ key-derivation function (KDF) тАФ рдореБрджреНрджрд╛рдо рдорд╣рд╛рдЧ рдмрдирд╡рд▓реЗрд▓рд╛ hash, рдЬреНрдпрд╛рдЪрд╛
work factor рддреБрдореНрд╣реА рдХрд╛рд│рд╛рдиреБрд╕рд╛рд░ рд╡рд╛рдврд╡реВ рд╢рдХрддрд╛. OWASP рдЪреА Password Storage Cheat Sheet (2023) рд╢рд┐рдлрд╛рд░рд╕ рдХрд░рддреЗ:
- рд╕рд░реНрд╡рдкреНрд░рдердо Argon2id (рдХрд┐рдорд╛рди 19 MiB memory, 2 iterations, 1 degree of parallelism),
- Argon2id рдЙрдкрд▓рдмреНрдз рдирд╕реЗрд▓ рддрд░ scrypt,
- рдЬреБрдиреНрдпрд╛ systems рд╕рд╛рдареА bcrypt (work factor 10 рдХрд┐рдВрд╡рд╛ рдЬрд╛рд╕реНрдд; рддреЛ рдлрдХреНрдд рдкрд╣рд┐рд▓реЗ 72 bytes рд╡рд╛рдЪрддреЛ),
- FIPS-140 рдорд╛рдиреНрдпрддрд╛ рдЖрд╡рд╢реНрдпрдХ рдЕрд╕реЗрд▓ рддреЗрд╡реНрд╣рд╛ 600,000 рдХрд┐рдВрд╡рд╛ рдЬрд╛рд╕реНрдд iterations рд╕рд╣ PBKDF2-HMAC-SHA256. Lab 200,000 iterations рд╕рд╣ PBKDF2 рд╡рд╛рдкрд░рддреЗ рддреЗ рдлрдХреНрдд demo рдЬрд▓рдж рдЪрд╛рд▓рд╛рд╡рд╛ рдореНрд╣рдгреВрди тАФ рдЦрд▒реНрдпрд╛ systems рдордзреНрдпреЗ рд╡рд░рдЪреЗ рдЖрдХрдбреЗ рд╡рд╛рдкрд░рд╛.
- Constant-time compare тАФ рдкрд╣рд┐рд▓рд╛ byte рд╡реЗрдЧрд│рд╛ рдЕрд╕реЛ рдХреА рд╢реЗрд╡рдЯрдЪрд╛,
hmac.compare_digestрд▓рд╛ рддреЗрд╡рдврд╛рдЪ рд╡реЗрд│ рд▓рд╛рдЧрддреЛ, рддреНрдпрд╛рдореБрд│реЗ timing рдордзреВрди рдХрд╛рд╣реАрдЪ рдЙрдШрдб рд╣реЛрдд рдирд╛рд╣реА. - MFA (multi-factor authentication) тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдорд╛рд╣реАрдд рдЕрд╕рд▓реЗрд▓реА рдЧреЛрд╖реНрдЯ + рддреБрдордЪреНрдпрд╛рдХрдбреЗ рдЕрд╕рд▓реЗрд▓реА рдЧреЛрд╖реНрдЯ (TOTP app, security key) рдХрд┐рдВрд╡рд╛ рддреБрдореНрд╣реА рд╕реНрд╡рддрдГ. Passkeys (WebAuthn / FIDO2) password рдРрд╡рдЬреА device рд╡рд░рдЪреА key pair рд╡рд╛рдкрд░рддрд╛рдд; private key рдХрдзреАрдЪ device рд╕реЛрдбрдд рдирд╛рд╣реА, рдЖрдгрд┐ рддреА рдлрдХреНрдд рдЦрд▒реНрдпрд╛ site рд▓рд╛рдЪ рдЙрддреНрддрд░ рджреЗрддреЗ, рддреНрдпрд╛рдореБрд│реЗ рддрд┐рдЪреЗ phishing рд╣реЛрдК рд╢рдХрдд рдирд╛рд╣реА.
- Login рдЪреЗ рдЗрддрд░ рдмрдЪрд╛рд╡ тАФ back-off рд╕рд╣ rate limits рдЖрдгрд┐ lock-outs, рдирд╡реЗ passwords breached passwords рдЪреНрдпрд╛ рдпрд╛рджреАрд╢реА рддрдкрд╛рд╕рдгреЗ, рдЖрдгрд┐ рд╕рд░реНрд╡рд╕рд╛рдзрд╛рд░рдг error messages ("рдЪреБрдХреАрдЪрд╛ email рдХрд┐рдВрд╡рд╛ password").
- Sessions рд╡рд┐рд░реБрджреНрдз tokens тАФ login рдирдВрддрд░ server рдПрдХрддрд░
HttpOnlycookie рдордзреНрдпреЗ random session id рджреЗрддреЛ (server state рдареЗрд╡рддреЛ; revoke рдХрд░рдгреЗ рд╕реЛрдкреЗ) рдХрд┐рдВрд╡рд╛ JWT рд╕рд╛рд░рдЦрд╛ signed token (state рддреНрдпрд╛рдЪреНрдпрд╛ рдЖрддрдЪ рдЕрд╕рддреЗ; expire рд╣реЛрдгреНрдпрд╛рдЖрдзреА revoke рдХрд░рдгреЗ рдХрдареАрдг). Login рд╡реЗрд│реА рдирд╡рд╛ session id рджреНрдпрд╛, рдореНрд╣рдгрдЬреЗ login рдЖрдзреА рдШреБрд╕рд╡рд▓реЗрд▓рд╛ id рдирд┐рд░реБрдкрдпреЛрдЧреА рдард░рддреЛ.
ЁЯдФ рдХрд╛
рдХрд╛рд░рдг password databases leak рд╣реЛрддрд╛рдд тАФ SQL injection (рдзрдбрд╛ 01), рд╣рд░рд╡рд▓реЗрд▓рд╛ backup, рдХрд┐рдВрд╡рд╛ рдирд┐рд╖реНрдХрд╛рд│рдЬреА log рдордзреВрди. рддреЗ leak рдЭрд╛рд▓реЗ рдХреА рдЪреЛрд░ рдЖрдгрд┐ рддреБрдордЪреНрдпрд╛ users рдЪреА accounts тАФ рдЖрдгрд┐ рддреНрдпрд╛рдВрдиреА password рдкреБрдиреНрд╣рд╛ рд╡рд╛рдкрд░рд▓реЗрд▓реНрдпрд╛ рдЗрддрд░ рд╕рдЧрд│реНрдпрд╛ sites тАФ рдпрд╛рдВрдЪреНрдпрд╛рдордзреНрдпреЗ рдлрдХреНрдд hash рдХрд┐рддреА рд╣рд│реВ рдЖрд╣реЗ рдПрд╡рдвреЗрдЪ рдЙрд░рддреЗ. GPU рд╡рд░ рдЬрд▓рдж hash рдЪреЗ рд╕реЗрдХрдВрджрд╛рд▓рд╛ рдЕрдмреНрдЬрд╛рд╡рдзреА рдЕрдВрджрд╛рдЬ рд╣реЛрддрд╛рдд. рд╣рд│реВ, salted hash рдЪреЗ рдлрдХреНрдд рд╣рдЬрд╛рд░реЛ. MFA рдЖрдгрд┐ passkeys рдореБрд│реЗ рдУрд│рдЦрд▓реЗрд▓рд╛ password рдПрдХрдЯрд╛ рдкреБрд░реЗрд╕рд╛ рдард░рдд рдирд╛рд╣реА.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
sec/identity.py рдордзреНрдпреЗ: hash_password(password, salt=None, iterations=200_000) random 16-byte salt рдмрдирд╡рддреЗ рдЖрдгрд┐
(salt, iterations, pbkdf2_hmac("sha256", тАж)) рдкрд░рдд рджреЗрддреЗ. verify_password(password, stored) рд╕рд╛рдард╡рд▓реЗрд▓реНрдпрд╛
salt рдЖрдгрд┐ iterations рдиреЗ рдкреБрдиреНрд╣рд╛ рдЧрдгрди рдХрд░рддреЗ рдЖрдгрд┐ hmac.compare_digest рдиреЗ рддреБрд▓рдирд╛ рдХрд░рддреЗ.
guesses_per_second(kind) рдордзреНрдпреЗ рдПрдХрд╛ GPU рд╕рд╛рдареА рдЙрджрд╛рд╣рд░рдгрд╛рджрд╛рдЦрд▓ рдЧреЛрд▓ рдЖрдХрдбреЗ рдЖрд╣реЗрдд, рдлрд░рдХ рдХрд┐рддреА рдореЛрдард╛
рдЖрд╣реЗ рддреЗ рджрд╛рдЦрд╡рдгреНрдпрд╛рд╕рд╛рдареА тАФ рд╣рд╛ benchmark рдирд╛рд╣реА.
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
python3 sec/demo.py passwords
python3 - <<'EOF'
import sys, time; sys.path.insert(0, "sec"); from identity import hash_password, verify_password
a = hash_password("Summer2026!"); b = hash_password("Summer2026!")
print("same password, two users тЖТ same hash?", a[2] == b[2], "┬╖ salts differ?", a[0] != b[0])
print("right password тЖТ", verify_password("Summer2026!", a), "┬╖ one letter off тЖТ", verify_password("summer2026!", a))
for n in (1_000, 200_000, 600_000):
t = time.perf_counter(); hash_password("Summer2026!", salt=b"s" * 16, iterations=n)
print(f"{n:>7,} iterations тЖТ {(time.perf_counter() - t) * 1000:6.1f} ms per guess")
EOF
тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ
passwords рд╣реЗ print рдХрд░рддреЗ:
тФАтФА stored: salt + 200,000 PBKDF2 rounds тЖТ 7f2c954f85f5934bde900ac7тАж (never the password itself)
login with the right password тЖТ True ┬╖ wrong тЖТ False
тФАтФА how fast can a stolen hash be guessed? (one GPU, illustrative)
md5 (unsalted) 50,000,000,000 guesses/s
sha256 (salted) 10,000,000,000 guesses/s
pbkdf2 200k iterations 50,000 guesses/s
use Argon2id / bcrypt / scrypt / PBKDF2 with a salt ┬╖ add MFA ┬╖ rate-limit logins ┬╖ check breached passwords
рддреБрдордЪрд╛ snippet рд╣реЗ print рдХрд░рддреЛ (milliseconds рддреБрдордЪреНрдпрд╛ machine рд╡рд░ рдЕрд╡рд▓рдВрдмреВрди тАФ рд╣реЗ рдПрдХрд╛ laptop рд╡рд░рдЪреЗ рдЖрд╣реЗрдд):
same password, two users тЖТ same hash? False ┬╖ salts differ? True
right password тЖТ True ┬╖ one letter off тЖТ False
1,000 iterations тЖТ 0.2 ms per guess
200,000 iterations тЖТ 48.9 ms per guess
600,000 iterations тЖТ 137.0 ms per guess
ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ
рдПрдХрд╛рдЪ password рдЪреЗ рджреЛрди рд╡реЗрдЧрд╡реЗрдЧрд│реЗ hashes рдмрдирддрд╛рдд, рдХрд╛рд░рдг рдкреНрд░рддреНрдпреЗрдХ user рдЪреЗ рд╕реНрд╡рддрдГрдЪреЗ salt рдЕрд╕рддреЗ. рдПрдХ рдЕрдХреНрд╖рд░ рдмрджрд▓рд▓реЗ рддрд░реА fail рд╣реЛрддреЗ. рдЖрдгрд┐ рдЦрд░реНрдЪ iterations рд╕реЛрдмрдд рд╡рд╛рдврддреЛ: 600,000 рдЪрд╛ рдЦрд░реНрдЪ 200,000 рдЪреНрдпрд╛ рд╕рд╛рдзрд╛рд░рдг рддрд┐рдкреНрдкрдЯ рдЖрд╣реЗ. User login рд╡реЗрд│реА рдПрдХрджрд╛рдЪ 0.1 seconds рдерд╛рдВрдмрддреЛ. рдЪреЛрд░ рддреА рдХрд┐рдВрдордд рдкреНрд░рддреНрдпреЗрдХ рдЕрдВрджрд╛рдЬрд╛рд╕рд╛рдареА рднрд░рддреЛ тАФ рдЖрдгрд┐ demo рдЪреНрдпрд╛ рдЖрдХрдбреНрдпрд╛рдВрдиреБрд╕рд╛рд░, рд╣рд│реВ KDF рд╡рд░ рд╣рд▓реНрд▓рд╛ рдХрд░рдгреЗ salt рдирд╕рд▓реЗрд▓реНрдпрд╛ MD5 рдкреЗрдХреНрд╖рд╛ рджрд╣рд╛ рд▓рд╛рдЦ рдкрдЯ рд╣рд│реВ рдЖрд╣реЗ.
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
sha256(password)рдХрд┐рдВрд╡рд╛md5(password)тАФ рдЬрд▓рдж hashes, salt рдЕрд╕рд▓реЗ рддрд░реА- рд╕рдЧрд│реНрдпрд╛рдВрд╕рд╛рдареА рдПрдХрдЪ global salt, рдХрд┐рдВрд╡рд╛ random рдирд╕рд▓реЗрд▓реЗ salt
- 2015 рдордзреНрдпреЗ рдирд┐рд╡рдбрд▓реЗрд▓рд╛ рдЖрдгрд┐ рдХрдзреАрдЪ рди рд╡рд╛рдврд╡рд▓реЗрд▓рд╛ work factor тАФ рдкреБрдврдЪреНрдпрд╛ login рд╡реЗрд│реА рдирд╡реНрдпрд╛ рдЦрд░реНрдЪрд╛рдиреЗ re-hash рдХрд░рд╛
- timing рдореЛрдЬрддрд╛ рдпреЗрдИрд▓ рдЕрд╢рд╛ code рдордзреНрдпреЗ hashes рдЪреА
==рдиреЗ рддреБрд▓рдирд╛ рдХрд░рдгреЗ - "рдЪреБрдХреАрдЪрд╛ password" рд╡рд┐рд░реБрджреНрдз "рдЕрд╕рд╛ user рдирд╛рд╣реА" рдЕрд╕реЗ messages рдЬреЗ рдЕрдВрджрд╛рдЬ рдХрд░рдгрд╛рд▒реНрдпрд╛рд▓рд╛ рдХреЛрдгрддреЗ emails рдЖрд╣реЗрдд рддреЗ рд╕рд╛рдВрдЧрддрд╛рдд
- staff рдЖрдгрд┐ admin accounts рд╕рд╛рдареА рдлрдХреНрдд SMS рдиреЗ MFA тАФ passkeys рдХрд┐рдВрд╡рд╛ authenticator app рд▓рд╛ рдкреНрд░рд╛рдзрд╛рдиреНрдп рджреНрдпрд╛
- login рдирдВрддрд░рд╣реА рддреЛрдЪ session id рдареЗрд╡рдгреЗ (session fixation)
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд
рдЦрд▒реНрдпрд╛ account рд╡рд░ тАФ library рд╡рд╛рдкрд░рд╛; KDF рдХрдзреАрд╣реА рд╕реНрд╡рддрдГ рд▓рд┐рд╣реВ рдирдХрд╛. Python рдордзреНрдпреЗ Argon2id
(argon2-cffi; рддреНрдпрд╛рдЪреЗ defaults RFC 9106 рдкрд╛рд│рддрд╛рдд):
from argon2 import PasswordHasher
ph = PasswordHasher()
stored = ph.hash(password) # "$argon2id$v=19$m=65536,t=3,p=4$тАж" тАФ salt included
ph.verify(stored, attempt) # raises VerifyMismatchError when wrong
if ph.check_needs_rehash(stored): # the cost was raised since this hash was made
stored = ph.hash(attempt)
Node.js рдордзреНрдпреЗ bcrypt:
import bcrypt from 'bcrypt';
const stored = await bcrypt.hash(password, 12); // cost 12
const ok = await bcrypt.compare(attempt, stored);
Django тАФ Argon2 рдкрд╣рд┐рд▓рд╛ рдареЗрд╡рд╛; рдЬреБрдиреЗ hashes рдкреБрдврдЪреНрдпрд╛ login рд╡реЗрд│реА upgrade рд╣реЛрддрд╛рдд:
PASSWORD_HASHERS = [
"django.contrib.auth.hashers.Argon2PasswordHasher",
"django.contrib.auth.hashers.PBKDF2PasswordHasher",
"django.contrib.auth.hashers.BCryptSHA256PasswordHasher",
]
Login рд╡реЗрд│реА рдирд╡рд╛ session id рджреНрдпрд╛ (express-session рд╕рд╣ Express):
req.session.regenerate(err => { if (err) return next(err); req.session.userId = user.id; res.redirect('/'); });
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рд╢рдХреНрдп рдЕрд╕реЗрд▓ рддрд░ passwords рдЕрдЬрд┐рдмрд╛рдд рд╕рд╛рдард╡реВ рдирдХрд╛ тАФ passkeys рдЖрдгрд┐ MFA рджреЗрдгрд╛рд░рд╛ managed identity provider (рдзрдбрд╛ 05) рд╡рд╛рдкрд░рд╛. рд╕рд╛рдард╡рд╛рд╡реЗрдЪ рд▓рд╛рдЧрд▓реЗ рддрд░, library рд╕рд╣ Argon2id рд╡рд╛рдкрд░рд╛, login route rate-limit рдХрд░рд╛, рдЖрдгрд┐ рдирд╡реЗ passwords breached-password рдпрд╛рджреАрд╢реА рддрдкрд╛рд╕рд╛ (рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде Have I Been Pwned рдЪреА k-anonymity range API).
тПня╕П рдкреБрдвреЗ
рдЕрдиреЗрдХ apps рдиреА passwords рддрдкрд╛рд╕реВрдЪ рдирдпреЗрдд. рддреА рдореНрд╣рдгрддрд╛рдд "school account рдиреЗ login рдХрд░рд╛" рдЖрдгрд┐ token рдШреЗрддрд╛рдд. рдкреБрдвреЗ: OAuth 2 рдЖрдгрд┐ OpenID Connect тАФ рдЖрдгрд┐ token рдиреЗ рдкрд╛рд░ рдХрд░рд╛рд╡реНрдпрд╛ рд▓рд╛рдЧрдгрд╛рд▒реНрдпрд╛ рд╕рдЧрд│реНрдпрд╛ рддрдкрд╛рд╕рдгреНрдпрд╛.
git checkout lesson-05-oauth-oidc