Նույնականացում և թույլտվություններ
Ապացուցել, թե ով է օգտատերը, ապա որոշել, թե ինչ է նրան թույլատրված։
30 վայրկյանում
Նույնականացումը (authentication) պատասխանում է «ո՞վ ես դու» հարցին, իսկ թույլտվությունը (authorization)՝ «ի՞նչ կարող ես անել» հարցին։ Օգտատերերն ապացուցում են իրենց ինքնությունը գաղտնաբառով, մեկանգամյա կոդով, սոցիալական մուտքով կամ բանալիներով, իսկ սերվերը նրանց «հիշում» է session-ի կամ token-ի միջոցով։ Այնուհետև դերերն ու իրավունքները որոշում են հասանելիությունը։ Անվտանգության միջադեպերի մեծ մասը սկսվում է հենց այստեղ թույլ տրված փոքր սխալներից, ուստի արժե լավ իմանալ այս մոտեցումները։
Ինչու եմ օգտագործում
- Ես auth եմ կառուցում յուրաքանչյուր պրոդուկտի համար՝ հաճախորդների մուտք վեբում և մոբայլում, աշխատակիցների մուտք ադմին պանելների համար և մեքենայական հասանելիություն գործընկերների համար։
- Ես օգտագործում եմ SMS OTP, սոցիալական մուտք և 2FA, որովհետև յուրաքանչյուր լսարանի համար պետք է հարմարության և անվտանգության տարբեր հավասարակշռություն։
- Ես դերերն ու իրավունքները նախագծում եմ շատ ուշադիր, որովհետև multi-tenant SaaS-ում մեկ բաց թողնված ստուգումը կարող է բացահայտել մեկ այլ ընկերության տվյալները։
- Ես auth-ը դիտարկում եմ որպես առանձին մոդուլ՝ իր թեստերով, որովհետև հենց այս մասն են հարձակվողները փորձում առաջինը։
Ինչպես է աշխատում
01Session-ներ ընդդեմ token-ների
Մուտքից հետո սերվերը պետք է ճանաչի ձեզ հաջորդ հարցման ժամանակ։ Session-ների դեպքում սերվերը պահում է ձեր մուտքի վիճակը (Redis-ում կամ տվյալների բազայում) և բրաուզերին տալիս է պատահական session id cookie-ի մեջ՝ ինչպես հանդերձարանի համարանիշը։ Համարանիշն ինքնին ոչինչ չի նշանակում, սերվերն է այն փնտրում իր մոտ։ Դուրս գալը հեշտ է՝ պարզապես ջնջում ես session-ը։ Token-ների դեպքում սերվերը կլիենտին տալիս է ստորագրված token, որն ինքն իր մեջ պարունակում է claim-ներ, օրինակ՝ օգտատիրոջ id և վավերականության ժամկետ, իսկ սերվերը ոչինչ չի փնտրում, այլ ստուգում է ստորագրությունը։ Session-ները լավագույն լռելյայն ընտրությունն են ձեր սեփական դոմենի վեբկայքերի համար։ Token-ները հարմար են մոբայլ հավելվածների, երրորդ կողմի կլիենտների և ծառայություն-ծառայություն կանչերի համար։ Շատ պրոդուկտներ օգտագործում են երկուսն էլ։
- Session cookie՝ HttpOnly, Secure, SameSite=Lax (կամ Strict), անգործության կարճ timeout։
- Մուտքի պահին վերագեներացրեք session id-ն՝ session fixation-ից պաշտպանվելու համար։
- Cookie-ներին պետք է CSRF պաշտպանություն, իսկ header-ներում փոխանցվող bearer token-ներին՝ XSS պաշտպանություն։
02JWT՝ access token-ներ, refresh token-ներ և չեղարկում
JWT-ն (JSON Web Token) բաղկացած է երեք մասից՝ header, claim-ներով payload և ստորագրություն։ Payload-ը կարող է կարդալ ցանկացած մեկը, ուստի երբեք գաղտնիքներ մի դրեք այնտեղ․ ստորագրությունը միայն ապացուցում է, որ այն չի փոփոխվել։ API կանչերի համար օգտագործեք կարճատև access token (5-ից 15 րոպե), իսկ նոր access token-ներ ստանալու համար՝ երկարատև refresh token, որը պահվում է ապահով տեղում։ Rotate արեք refresh token-ները՝ յուրաքանչյուր օգտագործում թողարկում է նորը և անվավեր դարձնում հինը։ Եթե հին refresh token-ը երբևէ նորից օգտագործվի, ենթադրեք, որ այն գողացվել է, և չեղարկեք token-ների ամբողջ «ընտանիքը»։ Քանի որ JWT-ները վավեր են մինչև ժամկետի ավարտը, չեղարկումը լրացուցիչ աշխատանք է պահանջում՝ token id-ների (jti) denylist Redis-ում, token-ի տարբերակ օգտատիրոջ վրա կամ օգտատիրոջ կրկնակի ստուգում յուրաքանչյուր հարցման ժամանակ։ Երբ token-ները ստուգում են մի քանի ծառայություններ, նախընտրեք ասիմետրիկ ստորագրություն (RS256 կամ EdDSA) և միշտ ստուգեք alg-ը, iss-ը, aud-ը և exp-ը։
// Verify an access token (jose library)
import { jwtVerify, createRemoteJWKSet } from 'jose';
const jwks = createRemoteJWKSet(new URL('https://auth.example.com/.well-known/jwks.json'));
export async function verifyAccessToken(token: string) {
const { payload } = await jwtVerify(token, jwks, {
issuer: 'https://auth.example.com',
audience: 'api.example.com',
algorithms: ['RS256'],
});
if (await denylist.has(payload.jti as string)) throw new Error('Token revoked');
return payload;
}
03OAuth2, OpenID Connect և սոցիալական մուտք
OAuth2-ը պատվիրակված հասանելիության ստանդարտ է՝ օգտատերը թույլ է տալիս հավելվածին գործել իր անունից՝ առանց իր գաղտնաբառը տալու։ OpenID Connect-ը (OIDC) դրա վրա կառուցված շերտ է, որն ավելացնում է ինքնությունը՝ ID token, որն ասում է, թե ով է օգտատերը։ «Sign in with Google»-ը կամ «Sign in with Apple»-ը հենց OIDC է։ Վեբ և մոբայլ հավելվածների համար օգտագործեք Authorization Code flow-ը PKCE-ով (Proof Key for Code Exchange), որը պաշտպանում է կոդը որսալուց․ հին Implicit flow-ն այլևս deprecated է։ Միշտ ստուգեք state պարամետրը CSRF-ի դեմ, ստուգեք ID token-ի ստորագրությունը, issuer-ը, audience-ը և nonce-ը, իսկ սոցիալական հաշիվները կապեք պրովայդերի կայուն subject id-ով, ոչ թե միայն email-ով։ iOS հավելվածները, որոնք առաջարկում են երրորդ կողմի սոցիալական մուտք, պետք է առաջարկեն նաև գաղտնիությանն ուղղված տարբերակ, օրինակ՝ Sign in with Apple (App Store guideline 4.8)։
04Գաղտնաբառեր, OTP և 2FA
Երբեք մի պահեք գաղտնաբառեր, պահեք hash-եր։ Օգտագործեք Argon2id (OWASP նվազագույնը՝ 19 MiB հիշողություն, 2 իտերացիա, parallelism 1) կամ bcrypt՝ 10 կամ ավելի cost-ով․ հաշվի առեք, որ bcrypt-ն օգտագործում է միայն առաջին 72 բայթը։ Ֆրեյմվորքներն այդ ամենն անում են ձեր փոխարեն՝ Laravel-ի Hash facade-ը կամ Node-ում argon2 փաթեթը։ Նոր գաղտնաբառերը ստուգեք արտահոսած գաղտնաբառերի հայտնի ցուցակներով և սահմանափակեք մուտքի փորձերի քանակը։ SMS-ով կամ email-ով մեկանգամյա գաղտնաբառերը հարմար են այն շուկաների համար, որտեղ ամեն ինչ հեռախոսով է․ գեներացրեք պատահական 6-նիշանոց կոդ, պահեք միայն դրա hash-ը կարճ ժամկետով (մոտ 5 րոպե), թույլ տվեք մի քանի փորձ և սահմանափակեք ուղարկումները ըստ հեռախոսահամարի և IP-ի, որպեսզի կանխեք SMS-ի ծախսերի չարաշահումը։ Երկգործոն նույնականացումն ավելացնում է երկրորդ ապացույց՝ TOTP հավելվածներ, passkey-ներ (WebAuthn) կամ SMS՝ որպես ավելի թույլ պահեստային տարբերակ։ Պարտադրեք 2FA աշխատակիցների և ադմինների համար։
// Laravel: OTP verification with rate limiting
$key = 'otp:'.$request->phone;
if (RateLimiter::tooManyAttempts($key, 5)) {
abort(429, 'Too many attempts, try again later.');
}
RateLimiter::hit($key, 600);
$otp = OtpCode::where('phone', $request->phone)
->where('expires_at', '>', now())
->latest()->first();
if (! $otp || ! Hash::check($request->code, $otp->code_hash)) {
throw ValidationException::withMessages(['code' => 'Invalid or expired code.']);
}
$otp->delete();
RateLimiter::clear($key);
05Դերեր, իրավունքներ և API բանալիներ
RBAC-ը՝ դերերի վրա հիմնված հասանելիության կառավարումը, օգտատերերին տալիս է դերեր (owner, manager, cashier), իսկ դերերը կրում են իրավունքներ (orders.refund, menu.edit)։ Կոդում ստուգեք իրավունքները, ոչ թե դերերի անունները․ «կարող է վերադարձնել գումարը» ստուգումը դիմանում է նոր դերերի ավելացմանը, իսկ «մենեջեր է»-ն՝ ոչ։ Ստուգումները պահեք սերվերում՝ մեկ տեղում, օրինակ՝ Laravel-ի policy-ներում և gate-երում կամ NestJS-ի guard-երում, իսկ UI-ում կոճակները թաքցրեք միայն որպես հարմարություն։ Multi-tenant հավելվածներում յուրաքանչյուր ստուգում ներառում է նաև tenant-ը՝ A ընկերության մենեջերը B ընկերությունում որևէ իրավունք չունի։ Մեքենայական կլիենտների համար թողարկեք scope-երով API բանալիներ՝ գեներացրեք երկար պատահական բանալի, ցույց տվեք այն մեկ անգամ, պահեք միայն դրա hash-ը, ավելացրեք տեսանելի prefix՝ այն նույնականացնելու համար (օրինակ՝ live_ կամ test_), և աջակցեք ժամկետի ավարտին, rotation-ին և յուրաքանչյուր բանալու rate limit-ին։ Գրանցեք, թե ով որ բանալին է օգտագործել։
06Անվտանգ լռելյայն կարգավորումների ստուգացանկ
Auth-ի բագերի մեծ մասը գալիս է մանրուքներից։ Cookie-ները սահմանեք HttpOnly-ով, որպեսզի JavaScript-ը չկարողանա դրանք կարդալ, Secure-ով, որպեսզի դրանք փոխանցվեն միայն HTTPS-ով, SameSite=Lax կամ Strict՝ CSRF-ի դեմ, և նեղ Path-ով։ Հնարավորության դեպքում session cookie-ների համար օգտագործեք __Host- prefix-ը։ «Անհայտ օգտատեր» և «սխալ գաղտնաբառ» դեպքերի համար վերադարձրեք նույն սխալը, որպեսզի խուսափեք հաշիվների enumeration-ից։ Սահմանափակեք մուտքի, OTP-ի և գաղտնաբառի վերականգնման հարցումները։ Վերականգնման հղումները դարձրեք մեկանգամյա և կարճատև։ Գրանցեք անվտանգության իրադարձությունները (մուտքեր, անհաջող փորձեր, դերերի փոփոխություններ, impersonation) և ահազանգեք կտրուկ աճերի դեպքում։ Սապորտի impersonation-ի համար սահմանեք ժամանակային սահմանափակում, պահանջեք պատճառ և աուդիտ արեք այն։
Set-Cookie: __Host-session=9f8c...; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=7200
Օգտագործեք, երբ
- Session-ներ՝ անվտանգ cookie-ներով, ձեր սեփական վեբկայքի և ադմին պանելի համար՝ մեկ դոմենի վրա։
- Կարճատև JWT access token-ներ՝ rotate արվող refresh token-ների հետ, մոբայլ հավելվածների և բազմաթիվ ծառայությունների համար։
- OIDC սոցիալական մուտք՝ գրանցման խոչընդոտները նվազեցնելու համար, SMS OTP՝ որտեղ օգտատերերը հիմնականում հեռախոսով են։
- Scope-երով API բանալիներ գործընկերների և սերվեր-սերվեր ինտեգրացիաների համար։
Մի՛ օգտագործեք, երբ
- Մի օգտագործեք JWT-ները որպես session-ի փոխարինող պարզ, մեկ դոմենի վեբկայքի համար միայն այն պատճառով, որ դա մոդայիկ է։
- Մի պահեք token-ները localStorage-ում, երբ HttpOnly cookie-ն կաշխատեր։
- Մի գրեք ձեր սեփական կրիպտոգրաֆիան, hashing-ը կամ OAuth սերվերը, երբ կա աջակցվող գրադարան կամ պրովայդեր։
Այլընտրանքներ
- Laravel Sanctum / Passport
Ներկառուցված session և token auth կամ լիարժեք OAuth2 սերվեր Laravel հավելվածների համար։
- Auth providers (Auth0, Clerk, Cognito, Keycloak)
Hosted կամ self-hosted ինքնության համակարգ․ ավելի արագ մեկնարկ, ավելի քիչ վերահսկողություն, մշտական ծախս։
- Passkeys (WebAuthn)
Ֆիշինգին դիմացկուն, առանց գաղտնաբառի մուտք․ ավելի ու ավելի հաճախ՝ 2FA-ի լավագույն լռելյայն ընտրությունը։
- Better Auth / Auth.js
Open-source auth գրադարաններ TypeScript և Next.js նախագծերի համար։
Լավագույն պրակտիկաներ
- Hash արեք գաղտնաբառերը Argon2id-ով կամ bcrypt-ով՝ աջակցվող գրադարանի միջոցով։
- Կարճատև access token-ներ, rotate արվող refresh token-ներ և չեղարկման ռազմավարություն։
- OAuth-ի համար՝ Authorization Code PKCE-ով․ ստուգեք state-ը, nonce-ը և token-ի claim-ները։
- Սահմանափակեք մուտքը, OTP-ի ուղարկումն ու ստուգումը, ինչպես նաև գաղտնաբառի վերականգնումը։
- Ստուգեք իրավունքները սերվերում՝ միշտ tenant-ի սահմաններում։
- Պահեք միայն OTP կոդերի, API բանալիների և վերականգնման token-ների hash-երը։
- Պարտադրեք 2FA աշխատակիցների և ադմինների համար․ գրանցեք անվտանգության իրադարձությունները և ահազանգեք դրանց մասին։
Տարածված սխալներ
- Երկարատև JWT-ներ՝ առանց չեղարկելու հնարավորության գողացված հեռախոսի կամ աշխատանքից ազատված աշխատակցի դեպքում։
- Կլիենտից ուղարկված օգտատիրոջ id-ին կամ դերին վստահելը՝ նույնականացված կոնտեքստի փոխարեն։
- OTP-ի անսահմանափակ ուղարկումներ, որոնք ձեր SMS gateway-ը վերածում են ուրիշի անվճար հարձակման գործիքի։
- «none» ալգորիթմն ընդունելը կամ JWT-ների audience-ն ու issuer-ը չստուգելը։
- Ադմինի կոճակները UI-ում թաքցնելը, բայց API endpoint-ները չպաշտպանված թողնելը։
Խորը վերլուծություն
07Որտեղ են ապրում token-ները՝ բրաուզերներ, BFF pattern և մոբայլ պահոց
Ցանկացած token, որը JavaScript-ը կարող է կարդալ, XSS սխալը կարող է գողանալ։ Ահա թե ինչու access և refresh token-ները localStorage-ում պահելը ռիսկային է single-page հավելվածի համար։ Backend-for-Frontend (BFF) pattern-ը token-ները ամբողջությամբ հանում է բրաուզերից. ձեր դոմենի վրա բարակ սերվերը կատարում է OAuth flow-ը, token-ները պահում է սերվերում և բրաուզերին տալիս է միայն HttpOnly, Secure, SameSite session cookie։ API կանչերն անցնում են BFF-ով, որը կցում է access token-ը։ Մոբայլում հենվելու cookie jar չկա, ուստի refresh token-ները պահեք պլատֆորմի անվտանգ պահոցում՝ iOS-ում Keychain, Android-ում Keystore-ով ապահովված պահոց, երբեք պարզ preferences-ում։ OAuth 2.0 Security Best Current Practice-ը (RFC 9700, հունվար 2025) նաև խորհուրդ է տալիս հնարավորության դեպքում sender-constrained token-ներ, օրինակ՝ DPoP (RFC 9449), որը token-ը կապում է client-ի մոտ գտնվող բանալու հետ, այնպես որ միայն գողացված token-ն անօգուտ է։
- SPA ձեր սեփական դոմենում՝ session cookie կամ BFF, ոչ թե localStorage։
- Մոբայլ՝ անվտանգ պահոց գումարած refresh token-ի ռոտացիա՝ կապված սարքի հետ։
- Ծառայությունից ծառայություն՝ client credentials կամ mTLS, կարճատև token-ներ։
08Passkey-ներն ու WebAuthn-ը ներսից
Passkey-ը բանալիների զույգ է, որը օգտատիրոջ սարքը ստեղծում է մեկ կայքի համար։ Մասնավոր բանալին երբեք դուրս չի գալիս սարքից կամ օգտատիրոջ password manager-ից. սերվերը պահում է միայն հանրային բանալին։ Մուտք գործելու համար սերվերը ուղարկում է պատահական challenge, սարքը խնդրում է մատնահետք, դեմք կամ PIN և ստորագրում է challenge-ը։ Սերվերը ստուգում է ստորագրությունը պահված հանրային բանալիով։ Ամենակարևորը՝ բրաուզերը ստորագրվողի մեջ ներառում է origin-ը, ուստի նման տեսք ունեցող դոմենի վրա կեղծ կայքը չի կարող վավեր ստորագրություն ստանալ. passkey-ները դիմացկուն են phishing-ին այնպես, ինչպես SMS կոդերն ու TOTP-ն չեն։ Ձեր կայքը relying party-ն է, որը նույնականացվում է RP ID-ով, օրինակ՝ example.com։ Երկու կողմում էլ օգտագործեք պահպանվող գրադարան, պահեք վերականգնման առնվազն մեկ ուղի և թույլ տվեք օգտատերերին գրանցել մի քանի passkey։
// Browser, using @simplewebauthn/browser
import { startAuthentication } from '@simplewebauthn/browser';
const optionsJSON = await api.post('/webauthn/login/options'); // fresh, single-use challenge
const assertion = await startAuthentication({ optionsJSON });
await api.post('/webauthn/login/verify', assertion);
// Server (@simplewebauthn/server verifyAuthenticationResponse) must check:
// expected challenge, expected origin and RP ID, the signature against the
// stored public key, and user verification if your policy requires it.
09Timing հարձակումներ, enumeration և credential stuffing
Հարձակվողները սովորում են փոքրիկ տարբերություններից։ Եթե «անհայտ email»-ը վերադառնում է 5 մվ-ում, իսկ «սխալ գաղտնաբառը»՝ 80 մվ-ում, որովհետև միայն երկրորդն է hash հաշվում, նրանք կարող են ցուցակել, թե որ հաշիվներն են գոյություն ունենում։ Անհայտ օգտատերերի համար գործարկեք կեղծ hash և վերադարձրեք նույն հաղորդագրությունը։ Գաղտնիքները, ինչպիսիք են API key-երը, reset token-ները և webhook-ների ստորագրությունները, համեմատեք հաստատուն ժամանակի ֆունկցիաներով՝ PHP-ում hash_equals, Node-ում crypto.timingSafeEqual, երբեք ===-ով։ Credential stuffing-ը՝ այլ կայքերից արտահոսած email-ի և գաղտնաբառի զույգերի փորձարկումը, login ձևերի վրա ամենատարածված հարձակումն է։ Պաշտպանվեք շերտերով. rate limit-ներ ըստ հաշվի և ըստ IP-ի, ստուգում արտահոսած գաղտնաբառերի դեմ (Have I Been Pwned-ի range API-ն օգտագործում է k-anonymity, այնպես որ ուղարկում եք SHA-1 hash-ի միայն առաջին հինգ նիշը), CAPTCHA կասկածելի օրինաչափություններից հետո և ահազանգեր login-ի ձախողումների կտրուկ աճի դեպքում։ Հաշվի կոշտ արգելափակումը կարող է չարաշահվել՝ իրական օգտատերերին դուրս թողնելու համար, ուստի նախընտրեք աստիճանաբար աճող ուշացումներ։
import { createHmac, timingSafeEqual } from 'node:crypto';
export function isValidSignature(rawBody: Buffer, headerHex: string, secret: string) {
const expected = createHmac('sha256', secret).update(rawBody).digest();
const received = Buffer.from(headerHex, 'hex');
// timingSafeEqual throws on different lengths, so check that first
return received.length === expected.length && timingSafeEqual(received, expected);
}
10Authorization դերերից այն կողմ՝ ABAC, ReBAC և ցուցակների ֆիլտրում
Դերերը պատասխանում են «ինչ տեսակի օգտատեր է սա» հարցին, բայց իրական կանոնները հաճախ կախված են ավելիից։ Attribute-based access control-ը (ABAC) ավելացնում է պայմաններ. մենեջերը կարող է վերադարձնել միայն իր մասնաճյուղի պատվերները և միայն մինչև որոշակի սահման։ Relationship-based access control-ը (ReBAC), որը տարածվեց Google-ի Zanzibar հոդվածով և իրականացված է OpenFGA-ում և SpiceDB-ում, թույլտվությունները մոդելավորում է որպես գրաֆ. օգտատերը կարող է խմբագրել փաստաթուղթը, որովհետև պատկանում է այն թիմին, որին պատկանում է դրա պանակը։ Policy engine-ները, օրինակ՝ OPA-ն կամ Cedar-ը, կանոնները պահում են կոդից դուրս։ Ամենադժվարը ցուցակներն են. յուրաքանչյուր տող բեռնելուց հետո ստուգելը կոտրում է pagination-ը և արտահոսեցնում քանակները։ Փոխարենը authorization-ը տեղափոխեք հարցման մեջ՝ որպես scope, որը ավելացնում է tenant-ի, մասնաճյուղի կամ սեփականության պայմաններ, որպեսզի բազան վերադարձնի միայն այն տողերը, որոնք օգտատերը կարող է տեսնել։ Թեստավորեք երկու ուղղությամբ էլ. թույլատրված օգտատերերը տեսնում են տվյալները, իսկ մնացած բոլորը ստանում են 404։
Իրական իրավիճակներ
Մենեջերի հեռախոսը գողացել են
Խնդիր
Մասնաճյուղի մենեջերը գիշերը ժամը 11-ին հայտնում է, որ իր հեռախոսը գողացել են։ Հեռախոսում բացված էր ադմին հավելվածը՝ պատվերներ վերադարձնելու և գներ փոխելու թույլտվությամբ։
Ինչպես լուծել
Գործեք րոպեների, ոչ թե ժամերի ընթացքում։ Չեղարկեք այդ օգտատիրոջ refresh token-ների բոլոր ընտանիքները և սերվերային session-ները, և բարձրացրեք token version-ը օգտատիրոջ գրառման վրա, որպեսզի դեռ վավեր access token-ները ձախողվեն յուրաքանչյուր հարցման ստուգման ժամանակ։ Փակեք օգտատիրոջ բոլոր ակտիվ socket կապերը։ Ռոտացրեք այդ հեռախոսին տրված API key-երը կամ device token-ները։ Այնուհետև audit log-ում ստուգեք գողության պահից ի վեր կատարված վերադարձումները, գների փոփոխությունները կամ արտահանումները և հետ շրջեք անհրաժեշտը։ Դրանից հետո աշխատակիցների համար պարտադիր դարձրեք 2FA կամ passkey, access token-ները պահեք կարճ և ավելացրեք «դուրս գալ բոլոր սարքերից» կոճակ, որը support թիմը կարող է օգտագործել առանց ծրագրավորողի։
Հազարավոր անհաջող login-ներ բազմաթիվ IP-ներից
Խնդիր
Գիշերվա ընթացքում login endpoint-ը ստացավ 400,000 փորձ հազարավոր IP-ներից, յուրաքանչյուրը՝ տարբեր email-ով և հավանական գաղտնաբառով։ Մի քանի հաշիվ գրավվեց։
Ինչպես լուծել
Սա credential stuffing է արտահոսած գաղտնաբառերով։ Անմիջապես ավելացրեք rate limit-ներ ըստ հաշվի, ոչ միայն ըստ IP-ի, և login-ի առջև դրեք CAPTCHA կամ proof-of-work challenge, երբ ձախողումների մակարդակը կտրուկ աճում է. edge-ում WAF-ը կամ bot protection-ը օգնում է ծավալի դեմ։ Պարտադրեք գաղտնաբառի վերակայում վնասված հաշիվների համար և անվավեր դարձրեք դրանց session-ները։ Պատասխանը դարձրեք նույնը անհայտ օգտատերերի և սխալ գաղտնաբառերի համար։ Այնուհետև վերացրեք արմատական պատճառը. գրանցման և login-ի ժամանակ գաղտնաբառերը ստուգեք արտահոսածների ցուցակներով, առաջարկեք passkey-ներ կամ 2FA և ահազանգեր դրեք ձախողումների անսովոր հարաբերակցության վրա, որպեսզի հաջորդ ալիքը բռնվի րոպեների ընթացքում։
Enterprise հաճախորդը single sign-on է ուզում
Խնդիր
SaaS-ի խոշոր հաճախորդը ցանկանում է, որ իր աշխատակիցները մուտք գործեն ընկերության identity provider-ով, և որ հաշիվներն ավտոմատ հեռացվեն, երբ աշխատակիցները հեռանում են։
Ինչպես լուծել
Աջակցեք OIDC-ին, և SAML-ին, եթե հաճախորդը պահանջում է, կարգավորված ըստ tenant-ի՝ issuer, client id, secret կամ metadata։ Ստուգեք, որ հաճախորդը տիրապետում է իր email դոմենին, ապա այդ դոմենով օգտատերերին ուղղորդեք tenant-ի identity provider։ Օգտատերերին ստեղծեք just in time՝ առաջին login-ի ժամանակ, խմբերը համապատասխանեցնելով ձեր դերերին, և հաշիվները կապեք provider-ի subject id-ով, ոչ թե email-ով։ Թույլ տվեք tenant-ին պարտադրել SSO, որպեսզի նրա աշխատակիցների համար գաղտնաբառերն անջատված լինեն, բայց պահեք արտակարգ դեպքի owner login։ Ավտոմատ հեռացման համար իրականացրեք SCIM provisioning կամ գոնե պարբերական ստուգումներ և կարճացրեք session-ները, որպեսզի deprovision արված օգտատերերն արագ կորցնեն հասանելիությունը։
Արագ ուղեցույց
- Laravel՝ նոր session id login-ից հետո
$request->session()->regenerate();- Laravel՝ հին hash-երի թարմացում login-ի ժամանակ
if (Hash::needsRehash($user->password)) { $user->update(['password' => Hash::make($plain)]); }- Sanctum token՝ ability-ներով
$user->createToken('pos-terminal', ['orders:create'])->plainTextToken- Sanctum՝ ability-ի ստուգում
$request->user()->tokenCan('orders:create')- Անվտանգ պատահական token (PHP)
bin2hex(random_bytes(32))- Անվտանգ պատահական token (Node)
crypto.randomBytes(32).toString('base64url')- Հաստատուն ժամանակի համեմատում (PHP)
hash_equals($expected, $given)- Կարդալ JWT claim-երը առանց ստուգման (միայն debug)
import { decodeJwt } from 'jose'; decodeJwt(token)- OIDC discovery փաստաթուղթ
https://auth.example.com/.well-known/openid-configuration
Իմ իրական նախագծերում
- Multi-tenant POS SaaS-ում՝ RS256 JWT-ներ, որոնք վերաստուգվում են յուրաքանչյուր հարցման ժամանակ denylist-ով, 2FA աշխատակիցների և օպերատորների համար, device token-ներ դրամարկղերի համար, հյուրերի SMS OTP և scope-երով live/test API բանալիներ՝ rate limit-երով և rotation-ով։
- Նույն SaaS-ն աջակցում է աշխատակիցների ժամանակով սահմանափակված impersonation, որը պահանջում է պատճառ, ինչպես նաև պլատֆորմի, ընկերության և custom դերեր՝ կառուցված իրավունքների բանալիներից։
- Ռեստորանային ցանցի պլատֆորմում՝ email և գաղտնաբառ, գրանցում SMS OTP-ով, Google և Apple մուտք վեբում և նեյթիվ հավելվածներում, ադմինի OTP մուտք և 12 ադմին դեր։
- WordPress խանութը Laravel-ի տեղափոխելիս ես պահպանեցի WordPress-ի գաղտնաբառերի hash-երի համատեղելիությունը, որպեսզի հաճախորդները ստիպված չլինեին վերականգնել իրենց գաղտնաբառերը։
Հարցեր և պատասխաններ
Q1Session-ներ, թե՞ JWT․ ո՞րն եք ընտրում և ինչո՞ւ։
Իմ սեփական դոմենի վեբկայքի և ադմինի համար՝ սերվերային session-ներ անվտանգ HttpOnly cookie-ով։ Դրանք պարզ են, անմիջապես չեղարկվող և լավ աջակցվում են ֆրեյմվորքների կողմից։ JWT-ները իմաստ ունեն, երբ բազմաթիվ ծառայություններ պետք է ստուգեն ինքնությունն առանց ընդհանուր session store-ի, կամ մոբայլ և երրորդ կողմի կլիենտների համար։ Նույնիսկ այդ դեպքում ես access token-ները պահում եմ կարճատև և զուգակցում rotate արվող refresh token-ների ու չեղարկման մեխանիզմի հետ։ Տարածված սխալն այն է, որ JWT են ընտրում մեկ դոմենի պարզ վեբ հավելվածի համար, իսկ հետո denylist-ով վատ վերակառուցում են session-ները։ Իրական հարցն այն է, թե ովքեր են կլիենտները և ինչպես է աշխատելու չեղարկումը։
Q2Ինչպե՞ս են աշխատում refresh token-ների rotation-ը և կրկնակի օգտագործման հայտնաբերումը։
Կլիենտն ունի կարճատև access token և երկարատև refresh token։ Երբ access token-ի ժամկետն ավարտվում է, կլիենտն ուղարկում է refresh token-ը և ստանում նոր access token ու նոր refresh token, իսկ հին refresh token-ը նշվում է որպես օգտագործված։ Մեկ մուտքից ստացված բոլոր token-ներն ունեն ընդհանուր family id։ Եթե օգտագործված refresh token-ը նորից ներկայացվի, ապա կամ օրինական կլիենտի, կամ հարձակվողի մոտ կա գողացված պատճեն, և դուք չեք կարող իմանալ՝ որի, ուստի չեղարկում եք ամբողջ ընտանիքը և պարտադրում նոր մուտք։ Refresh token-ները սերվերում պահեք hash արված, կապեք դրանք սարքի հետ և ժամկետանց դարձրեք անգործունները։
Q3Ո՞րն է տարբերությունը OAuth2-ի և OpenID Connect-ի միջև։
OAuth2-ը թույլտվության մասին է՝ այն թույլ է տալիս կլիենտին ստանալ access token՝ օգտատիրոջ անունից API կանչելու համար՝ առանց օգտատիրոջ գաղտնաբառի։ Այն չի սահմանում, թե ով է օգտատերը։ OpenID Connect-ը ինքնության շերտ է OAuth2-ի վրա։ Այն ավելացնում է ID token՝ ստորագրված JWT այնպիսի claim-ներով, ինչպիսիք են subject id-ն, email-ը և նույնականացման ժամանակը, ինչպես նաև userinfo endpoint և discovery։ Այսպիսով, «Sign in with Google»-ը OIDC է, իսկ «թույլ տուր այս հավելվածին կարդալ իմ օրացույցը»-ն՝ OAuth2։ Երկուսի համար էլ օգտագործեք Authorization Code flow-ը PKCE-ով և ստուգեք state-ը, nonce-ը, issuer-ը և audience-ը։
Q4Ինչպե՞ս եք անվտանգ պահում գաղտնաբառերը։
Hash եմ անում դրանք դանդաղ, salt-ով, հիշողություն պահանջող ալգորիթմով, որը նախատեսված է գաղտնաբառերի համար․ Argon2id-ն առաջին ընտրությունն է՝ առնվազն OWASP-ի նվազագույնով՝ 19 MiB հիշողություն, երկու իտերացիա և մեկ parallelism։ bcrypt-ը 10 կամ ավելի cost-ով նույնպես ընդունելի է՝ հաշվի առնելով դրա 72 բայթանոց մուտքային սահմանափակումը։ Երբեք մի օգտագործեք պարզ SHA-256 կամ MD5 և երբեք մի գաղտնագրեք գաղտնաբառերը հետադարձելի ձևով։ Օգտագործեք ֆրեյմվորքի hashing API-ն, որն ինքն է կառավարում salt-երը և թույլ է տալիս rehash անել, երբ պարամետրերը փոխվում են։ Ավելացրեք rate limiting և արտահոսած գաղտնաբառերի ստուգում, ինչպես նաև օգտագործեք ընդհանուր սխալի հաղորդագրություններ, որպեսզի հարձակվողները չկարողանան պարզել, թե որ հաշիվները գոյություն ունեն։
Q5Ինչպե՞ս կիրականացնեիք դերեր և իրավունքներ multi-tenant հավելվածում։
Օգտատերերն ունեն անդամակցություն յուրաքանչյուր tenant-ում, և յուրաքանչյուր անդամակցություն ունի մեկ կամ մի քանի դեր։ Դերերը իրավունքների բանալիների հավաքածուներ են, օրինակ՝ orders.refund կամ reports.view․ կան ներկառուցված դերեր, և tenant-ները կարող են ստեղծել իրենց սեփականները։ Կոդը ստուգում է իրավունքները, երբեք դերերի անունները, մեկ կենտրոնական մեխանիզմի միջոցով, օրինակ՝ policy-ների կամ guard-ի։ Յուրաքանչյուր ստուգում նաև հաստատում է, որ ռեսուրսը պատկանում է ընթացիկ tenant-ին, իսկ tenant-ների միջև հասանելիության փորձը վերադարձնում է 404։ Իմ սեփական օպերատորների պլատֆորմային դերերն առանձնացված են tenant-ի դերերից, իսկ զգայուն գործողությունները, օրինակ՝ impersonation-ը, պահանջում են պատճառ և աուդիտ են արվում։ Թեստերը յուրաքանչյուր մոդուլի համար ներառում են սխալ tenant-ի դեպք։
Q6Ինչպե՞ս եք պաշտպանում SMS OTP մուտքը չարաշահումից։
Երկու վտանգ կա՝ կոդերի գուշակում և SMS-ի վրա փողի վատնում։ Գուշակման դեմ օգտագործեք պատահական 6-նիշանոց կոդ, պահեք միայն դրա hash-ը, ժամկետանց դարձրեք այն մոտ հինգ րոպեից, թույլ տվեք մոտ հինգ փորձ և անվավեր դարձրեք այն օգտագործումից հետո։ SMS-ի ծախսերի չարաշահման դեմ սահմանափակեք ուղարկումները ըստ հեռախոսահամարի, IP-ի և սարքի, ավելացրեք սպասման ժամանակ կրկնակի ուղարկումների միջև, արգելափակեք premium-rate և անսպասելի երկրների կոդերը և դիտարկեք CAPTCHA կրկնվող հարցումներից հետո։ Հետևեք ուղարկումների ծավալին և ահազանգեք կտրուկ աճերի դեպքում։ SMS-ը կրկին ուղարկեք միայն ակնհայտ կապի խափանումների դեպքում, որպեսզի դանդաղ պրովայդերը նույն օգտատիրոջը կրկնօրինակ հաղորդագրություններ չառաջացնի։
Սենիոր մակարդակի հարցեր
S1Նախագծեք authentication մեկ պրոդուկտի համար, որն ունի կայք, մոբայլ հավելվածներ և partner API։
Մեկ identity միջուկ, մուտքի երեք ոճ։ Կայքը և ադմինը օգտագործում են սերվերային session-ներ HttpOnly SameSite cookie-ներում՝ CSRF պաշտպանությամբ և աշխատակիցների համար 2FA-ով կամ passkey-ներով։ Մոբայլ հավելվածներն օգտագործում են Authorization Code flow PKCE-ով կամ first-party login endpoint՝ ստանալով կարճատև access token և ռոտացվող refresh token, որը պահվում է անվտանգ պահոցում և կապված է սարքի գրառման հետ, որպեսզի support-ը կարողանա չեղարկել մեկ սարքը։ Գործընկերները ստանում են կա՛մ OAuth client credentials, կա՛մ scope-ով API key-եր, որոնք պահվում են hash արված վիճակում՝ ռոտացիայով և ըստ բանալու rate limit-ներով։ Երեքն էլ հանգում են նույն user կամ client մոդելին և նույն թույլտվությունների ստուգումներին՝ սահմանափակված ըստ tenant-ի։ Փոխզիջում՝ երեք մեխանիզմ պահելը ավելի թանկ է, քան մեկը, բայց բոլորին մեկ token մոդել պարտադրելը սովորաբար թուլացնում է կայքը կամ բարդացնում գործընկերների կյանքը։
S2Որտե՞ղ պետք է single-page հավելվածը պահի իր token-ները։
Իդեալական դեպքում ոչ մի տեղ, որտեղ JavaScript-ը կարող է հասնել։ localStorage-ը և sessionStorage-ը կարդացվում են էջի ցանկացած սկրիպտով, ուստի մեկ XSS սխալը կամ վնասված կախվածությունը արտահոսեցնում է երկարակյաց հավատարմագրերը։ Հիշողության մեջ պահելն ավելի լավ է, բայց կորչում է վերաբեռնման ժամանակ, ուստի դեռ refresh մեխանիզմ է պետք։ Իմ նախընտրությունը session cookie-ն է, երբ SPA-ն և API-ն կիսում են նույն site-ը, կամ Backend-for-Frontend, որը OAuth-ը կատարում է սերվերում և բրաուզերին տալիս միայն HttpOnly, Secure, SameSite cookie։ Cookie-ները բերում են CSRF ռիսկ, որը լավ լուծվում է SameSite-ով գումարած CSRF token-ներով կամ Origin-ի ստուգմամբ։ Եթե token-ները պետք է հասնեն բրաուզեր, access token-ները պահեք հիշողության մեջ րոպեներով, ռոտացրեք refresh token-ները կրկնակի օգտագործման հայտնաբերմամբ և դիտարկեք DPoP-ը։ Փոխզիջումը BFF-ի համար լրացուցիչ սերվերային քայլն է՝ XSS-ից շատ ավելի փոքր վնասի շառավղի դիմաց։
S3Ինչպե՞ս եք authorization-ը արդյունավետ դարձնում ցուցակի և որոնման endpoint-ներում։
Յուրաքանչյուր տող բեռնելուց հետո ստուգելը կոտրում է pagination-ը, վատնում հարցումներ և կարող է տեղեկություն արտահոսեցնել ընդհանուր քանակներով։ Ուստի ես թույլտվությունները թարգմանում եմ հարցման ֆիլտրերի։ Tenant-ին պատկանող յուրաքանչյուր հարցում ստանում է պարտադիր tenant պայման, իսկ դերերի կամ ատրիբուտների կանոնները դառնում են scope-ներ. մասնաճյուղի մենեջերները տեսնում են այն պատվերները, որոնց branch_id-ն իրենց մասնաճյուղերից է, գանձապահները՝ միայն իրենց հերթափոխերը։ Policy-ները դեռ պաշտպանում են մեկ օբյեկտի գործողությունները, օրինակ՝ refund կամ delete։ Որոնման շարժիչների համար ես յուրաքանչյուր փաստաթղթի հետ ինդեքսավորում եմ հասանելիության ատրիբուտները՝ tenant, մասնաճյուղ և տեսանելիություն, և միշտ ֆիլտրում եմ դրանցով։ Relationship-based մոդելների դեպքում OpenFGA-ի նման համակարգերը կարող են ցուցակել օբյեկտների այն id-ները, որոնց օգտատերը հասանելիություն ունի, բայց մեծ արդյունքների համար պետք է ֆիլտր, որը կարելի է տեղափոխել բազայի մեջ։ Փոխզիջումը կանոնների տրամաբանության կրկնությունն է policy-ների և scope-ների միջև, ուստի երկուսին էլ թեստեր են պետք։
S4Ինչպե՞ս կավելացնեիք SSO enterprise tenant-ների համար multi-tenant SaaS-ում։
Identity provider-ները դիտարկեք որպես ըստ tenant-ի կոնֆիգուրացիա։ Յուրաքանչյուր tenant կարող է գրանցել OIDC provider, կամ SAML, որտեղ enterprise-ները պնդում են, ստուգված email դոմեններով։ Login էջը նախ email է խնդրում և ուղղորդում tenant-ի provider, երբ դոմենը համընկնում է։ Callback-ի ժամանակ ես ամբողջությամբ ստուգում եմ ID token-ը կամ assertion-ը, ապա գտնում օգտատիրոջը provider-ով և subject id-ով, ստեղծելով նրան just in time և identity provider-ի խմբերը համապատասխանեցնելով tenant-ի դերերին։ Tenant-ները կարող են պարտադրել SSO՝ անջատելով գաղտնաբառով login-ը իրենց օգտատերերի համար, մինչդեռ պահպանվում է break-glass owner հաշիվ։ Offboarding-ի համար SCIM-ը թույլ է տալիս provider-ին անմիջապես ապաակտիվացնել օգտատերերին. առանց դրա կարճ session-ները սահմանափակում են ռիսկը։ Հիմնական փոխզիջումները support-ի բեռն են, քանի որ սխալ կարգավորված provider-ները դառնում են ձեր ticket-ները, և թեստավորման մատրիցը տարբեր provider-ների միջև։
Բառարան
- Credential stuffing
- Ավտոմատացված login-ի փորձեր՝ այլ կայքերից արտահոսած email-ի և գաղտնաբառի զույգերով։
- Account enumeration
- Գոյություն ունեցող հաշիվների բացահայտում՝ տարբեր սխալի հաղորդագրություններից կամ պատասխանի ժամանակից։
- PKCE
- Proof Key for Code Exchange՝ միանգամյա գաղտնիք, որը թույլ չի տալիս գողացված OAuth authorization code-ը օգտագործել ուրիշի կողմից։
- DPoP
- Demonstrating Proof of Possession՝ token-ը կապում է client-ի մոտ պահվող բանալու հետ, այնպես որ միայն արտահոսած token-ը չի կարող օգտագործվել։
- BFF
- Backend-for-Frontend՝ փոքր սերվեր, որը պահում է վեբ հավելվածի OAuth token-ները և բրաուզերին տալիս միայն session cookie։
- Passkey
- Phishing-ին դիմացկուն մուտքի տվյալ՝ սարքում պահվող մասնավոր բանալուց և սերվերում պահվող հանրային բանալուց։
- Relying party
- Կայքը կամ հավելվածը, որն ընդունում է login-ը, օրինակ՝ OIDC client-ը կամ passkey-ը ստուգող կայքը։
- ReBAC
- Relationship-based access control՝ թույլտվություններ, որոնք բխում են օգտատեր, թիմ, պանակ և փաստաթուղթ կապերից։
- Session fixation
- Հարձակում, որի ժամանակ զոհին ստիպում են օգտագործել հարձակվողին արդեն հայտնի session id. կանխվում է login-ի ժամանակ id-ն վերագեներացնելով։
- SCIM
- Ստանդարտ API, որը թույլ է տալիս ընկերության identity provider-ին ավտոմատ ստեղծել, թարմացնել և հեռացնել օգտատերերի հաշիվները ձեր հավելվածում։
Շարունակեք սովորել
Պրոդուկտի գաղափար ունե՞ք։Եկեք ստեղծենք այն։
Պատմեք ձեր գաղափարի, պրոդուկտի կամ թիմի մասին։ Սովորաբար պատասխանում եմ մեկ աշխատանքային օրվա ընթացքում։