Luyện Phỏng Vấn IT — 2000+ Câu Hỏi Phỏng Vấn IT Có Đáp Án 2026
Bảo Mật
XSS inject malicious scripts vào browser victim — React JSX tự động escape (dangerouslySetInnerHTML vẫn nguy hiểm); dùng DOMPurify cho user HTML, HttpOnly cookies, và Content Security Policy.
- XSS (Cross-Site Scripting) cho phép attacker inject và execute malicious scripts trong browser của victim, đánh cắp sessions, redirect users, modify page content.
- Ba loại: Stored XSS — script được lưu trong DB (comment, profile), mỗi user xem page bị attack; Reflected XSS — script trong URL parameter, server reflect lại trong response (phishing links); DOM-based XSS — client-side code đọc từ URL/storage và inject vào DOM mà không qua server (document.location, window.location.hash → innerHTML).
- Phòng chống: Context-appropriate encoding — HTML encode khi insert vào HTML, JS encode khi insert vào JavaScript, URL encode khi insert vào URL; React JSX tự động escape — dangerouslySetInnerHTML vẫn nguy hiểm.
- Không dùng innerHTML với user content — dùng textContent hoặc createElement.
- DOMPurify: sanitize HTML khi cần render user-provided HTML (rich text editor output) — DOMPurify.sanitize(userHtml).
- Content Security Policy: block inline scripts và unauthorized external scripts — defense in depth.
- HttpOnly cookies: JavaScript không thể đọc session cookie dù có XSS.
- Subresource Integrity (SRI): verify CDN scripts không bị tamper — integrity attribute trên <script>.
- Template literals và DOM APIs: const div = document.createElement('div'); div.textContent = userInput — safe; div.innerHTML = userInput — unsafe.
CSRF (Cross-Site Request Forgery) exploit việc browser tự động gửi cookies khi request đến domain — attacker tạo form/image trên evil.com gửi request đến bank.com, browser tự động attach session cookie của user.
Ví dụ: <img src='https://bank.com/transfer?to=attacker&amount=1000'> — nếu user đang logged in và bank không có CSRF protection, transfer xảy ra.
- Phòng chống: SameSite cookie attribute là defense chính hiện nay — SameSite=Strict: cookie không gửi trong bất kỳ cross-site request nào (bao gồm link navigation); SameSite=Lax (default Chrome): cookie gửi trong top-level GET navigation nhưng không trong POST/iframe/img — đủ cho hầu hết cases; SameSite=None: phải có Secure, gửi trong tất cả cross-site requests.
- CSRF tokens: random token lưu trong session, include trong form hidden field và verify server-side — attacker không thể đọc token vì same-origin policy.
- Double Submit Cookie: gửi token trong cả cookie và request body/header — server verify chúng match; không cần server-side state.
- Custom request headers (AJAX): XMLHttpRequest và fetch không thể set custom headers cross-origin mà không qua CORS preflight — thêm X-Requested-With: XMLHttpRequest hoặc Content-Type: application/json tự nhiên ngăn CSRF vì attacker không thể tạo cross-origin request với custom headers.
- Origin header checking: verify Origin/Referer header match expected domain — không đủ một mình vì có thể bị spoof trong một số cases.
OAuth 2.0 là authorization framework — delegated authorization (không phải authentication).
Phân biệt: OAuth cho authorization (app có quyền gì), OpenID Connect (OIDC) là layer trên OAuth cho authentication (ai đang login, trả về ID token).
Authorization Code flow chi tiết:
- App redirect user đến Authorization Server với client_id, redirect_uri, scope, state (CSRF protection), code_challenge (PKCE);
- User authenticate và consent;
- Auth Server redirect về app với authorization code;
- App exchange code cho access token qua back-channel request (POST, client_secret không expose trong URL);
- App dùng access token để call protected APIs;
- Refresh token để get new access token khi expire
PKCE (Proof Key for Code Exchange): app tạo code_verifier (random string), gửi code_challenge = hash(code_verifier) trong step 1, gửi code_verifier trong step 4 — server verify hash match — ngăn authorization code interception attack, bắt buộc cho public clients (SPA, mobile).
Implicit flow: deprecated — trả access token trực tiếp trong URL, exposure risk.
Client credentials flow: machine-to-machine, không có user — backend service gọi API.
Scopes: granular permissions (read:email, write:calendar) — request chỉ scopes cần thiết.
Token storage: access token trong memory (SPA), refresh token trong HttpOnly cookie — không dùng localStorage cho sensitive tokens.
OAuth 2.1 (đang chuẩn hóa) gộp các best practices: PKCE bắt buộc cho mọi client, xóa Implicit và Resource Owner Password Credentials flows.
HTTPS là HTTP over TLS (Transport Layer Security) — encrypt toàn bộ communication, verify server identity, đảm bảo data integrity.
- TLS handshake: Client Hello (supported cipher suites, TLS version) → Server Hello (chosen cipher, certificate) → Client verify certificate với CA (Certificate Authority) → Key exchange (ECDH — không share private key) → Session keys derived → Encrypted communication.
- Certificate chain: leaf certificate → intermediate CA → root CA (trusted bởi browser/OS); Let's Encrypt cung cấp free certificates tự động renew.
- HSTS (HTTP Strict Transport Security): Strict-Transport-Security: max-age=31536000; includeSubDomains — browser chỉ kết nối HTTPS trong 1 năm, ngăn SSL stripping attack; HSTS Preload list (chromium preload) — hardcoded trong browsers.
- TLS 1.3 improvements: 1-RTT handshake (vs TLS 1.2 2-RTT), 0-RTT resumption cho subsequent connections, removed insecure cipher suites.
- Certificate Transparency: public log của mọi issued certificate — detect unauthorized certificate issuance cho domain của bạn.
- Mixed content: HTTPS page load HTTP resources — browsers block active mixed content (scripts, XHR), warn về passive (images).
- Bắt buộc vì: MITM attack prevention, required cho HTTP/2 (browsers only support HTTP/2 over TLS), Service Workers, PWA, getUserMedia, required cho modern browser features; SEO ranking factor; browser 'Not Secure' warning tăng bounce rate.
Same-origin policy ngăn JavaScript từ một origin (scheme+host+port) đọc response từ origin khác — bảo vệ khỏi CSRF và data theft.
- CORS cho phép server opt-in cho cross-origin requests qua response headers.
- Simple requests (GET/POST với basic Content-Type) không cần preflight — browser gửi request và check response headers.
- Preflight (OPTIONS request): cho non-simple requests (custom headers, PUT/DELETE, Content-Type: application/json) — browser gửi OPTIONS trước, server phải respond với allowed methods/headers trước khi browser gửi actual request.
- Response headers: Access-Control-Allow-Origin: https://trusted.com (hoặc cho public APIs nhưng không thể dùng với credentials); Access-Control-Allow-Methods: GET, POST, PUT, DELETE; Access-Control-Allow-Headers: Content-Type, Authorization; Access-Control-Max-Age: 86400 (cache preflight 24h, giảm OPTIONS requests); Access-Control-Allow-Credentials: true (để gửi cookies/auth headers — phải specify exact origin, không dùng ).
- Common mistakes: add CORS headers ở nginx nhưng app cũng trả CORS headers → duplicate headers → browser reject; thêm * nhưng cần credentials → không work; không handle OPTIONS method trong route → 405 error.
- CORS chỉ restrict browser requests — Postman, curl, server-to-server không bị ảnh hưởng.
- Credentials mode: fetch(url, { credentials: 'include' }) gửi cookies; cần server set Access-Control-Allow-Credentials: true và exact origin.
Security headers là server response headers kiểm soát browser behavior để prevent attacks.
- Quan trọng nhất: Strict-Transport-Security: max-age=31536000; includeSubDomains (HSTS — force HTTPS, prevent SSL stripping, 1 năm duration); X-Content-Type-Options: nosniff (prevent MIME type sniffing — browser không tự đoán content type khác Content-Type header, ngăn polyglot file attacks); X-Frame-Options: DENY hoặc SAMEORIGIN (prevent clickjacking — page không thể được embed trong iframe của domain khác); CSP frame-ancestors thay thế X-Frame-Options và flexible hơn.
- Referrer-Policy: no-referrer-when-downgrade (default) hoặc strict-origin-when-cross-origin — control bao nhiêu URL info gửi trong Referer header; no-referrer cho sensitive pages.
- Permissions-Policy (thay thế Feature-Policy): kiểm soát browser APIs — Permissions-Policy: geolocation=(), camera=(), microphone=() — disable features không dùng đến.
- X-XSS-Protection: deprecated, browsers modern tự handle — không cần set.
- Cross-Origin-Opener-Policy: same-origin — isolate browsing context, enable SharedArrayBuffer, required cho high-resolution timers.
- Cross-Origin-Embedder-Policy: require-corp — cùng với COOP enable process isolation.
- Helmet.js trong Express: app.use(helmet()) tự động set tất cả headers với sensible defaults, có thể customize per-header. securityheaders.com để test và grade headers của site.
JWT stateless auth dùng signed token thay vì server-side session — không cần DB lookup mỗi request.
- Login: verify credentials → tạo JWT (sign với secret) → gửi token cho client.
- Request: client gửi token trong Authorization header (Bearer token).
- Server: middleware verify token, extract user info, attach vào req.user.
- Lưu trữ: access token → memory (JS variable), refresh token → httpOnly cookie. KHÔNG lưu localStorage (XSS risk).
Refresh token cho long-lived sessions.
OWASP Top 10 2021 (phiên bản mới nhất được áp dụng rộng rãi, 2025 edition đang ra) — A01: Broken Access Control, A03: Injection, A07: Auth Failures là ba rủi ro quan trọng nhất cho Node.js dev.
- OWASP Top 10 là danh sách 10 rủi ro bảo mật web phổ biến nhất, cập nhật định kỳ bởi Open Web Application Security Project. A01: Broken Access Control (lên #1 từ #5) — user truy cập resources không được phép: thiếu authorization checks, IDOR (Insecure Direct Object Reference). A02: Cryptographic Failures — dữ liệu nhạy cảm không được mã hóa, dùng thuật toán yếu (MD5, SHA1 cho password). A03: Injection — SQL, NoSQL, command injection. A04: Insecure Design — thiếu threat modeling, security requirements. A05: Security Misconfiguration — default credentials, verbose errors, unnecessary features enabled. A06: Vulnerable Components — outdated dependencies với known CVEs. A07: Auth Failures — weak passwords, session fixation, no rate limiting trên login. A08: Software Integrity Failures — CI/CD pipeline không secure, unsigned packages. A09: Security Logging Failures — không log security events, không alert. A10: SSRF (Server-Side Request Forgery) — attacker khiến server fetch internal URLs.
- Trong Node.js context: A03 (SQL injection → parameterized queries), A02 (bcrypt/argon2 thay MD5), A01 (middleware authorization), A06 (npm audit + Dependabot), A07 (rate limiting + account lockout).
Session-based dễ revoke, cần Redis cho multi-server; JWT stateless dễ scale nhưng không thể revoke trước expiry — hybrid (short-lived JWT + server-side refresh token) là best practice 2025. Session-based: server lưu session data trong memory/Redis, client chỉ lưu session ID trong cookie.
- Server stateful — phải lookup session mỗi request.
- Ưu điểm: dễ revoke (xóa session), không expose user data, server kiểm soát hoàn toàn.
- Nhược điểm: cần shared session store (Redis) cho multi-server, horizontal scaling phức tạp. JWT: server stateless, client lưu toàn bộ claims trong token.
- Không cần DB lookup để verify (chỉ verify signature).
- Ưu điểm: stateless → dễ scale, microservices có thể verify token mà không cần centralized auth.
- Nhược điểm: không thể revoke trước khi expire (phải dùng blacklist = stateful), token size lớn hơn session ID.
- Decision matrix: DÙNG Session khi: cần instant revocation (admin ban user, password change → invalidate tất cả sessions), single-server hoặc đã có Redis, traditional web app với form-based auth.
- DÙNG JWT khi: microservices (service-to-service auth), public API cho mobile apps, SSO across multiple domains, cần stateless architecture.
- Hybrid approach: JWT cho access (short-lived, 15min) + server-side session cho refresh tokens — best of both worlds.
Pitfall: JWT không phải encryption — payload là base64, bất kỳ ai có token đều đọc được claims — không đặt sensitive data vào payload.
OWASP 2025 khuyến nghị argon2id (memory-cost=64MB, time-cost=3) là best practice; bcrypt vẫn secure với cost=12 nhưng bị truncate ở 72 bytes.
- Cả hai đều là password hashing functions thiết kế đặc biệt để chậm — khác với SHA-256/MD5 được tối ưu để NHANH. bcrypt: battle-tested từ 1999, cost factor (
saltRounds) tăng exponentially; Node.js:bcrypt.hash(password, 12). Argon2: winner Password Hashing Competition 2015, ba variants: argon2d (GPU resistant), argon2i (side-channel resistant), argon2id (cả hai — recommended). - Tham số: memory-cost (RAM, default 64MB), time-cost (iterations), parallelism.
- Node.js:
argon2.hash(password)vớiargon2.argon2idvariant. - So sánh: Argon2id resistant với GPU attacks và side-channel attacks tốt hơn bcrypt; OWASP khuyến nghị argon2id (memory-cost=64MB, time-cost=3, parallelism=4). bcrypt giới hạn 72 bytes input (passwords dài hơn bị truncate — security issue); argon2 không có giới hạn.
- Thực tế 2025: argon2id là best practice mới, bcrypt vẫn secure nếu cost đủ cao và không có passwords >72 bytes.
- Migration: hash mới bằng argon2id khi user login (verify với bcrypt, rehash với argon2id, save mới) — zero downtime migration.
Pitfall: await bcrypt.hash(password, 10) trong test CI sẽ slow down tests đáng kể — dùng bcrypt.hash(password, 1) hoặc mock trong unit tests.
Token bucket cho phép bursts; sliding window không có burst issue của fixed window; dùng Redis-based implementation (Upstash) cho distributed deployment — in-memory rate limiter chỉ work trên single instance.
- Rate limiting ngăn abuse, brute-force, DoS. Fixed window: đếm requests trong window cố định (ví dụ 0:00-0:59, 1:00-1:59).
- Dễ implement, dễ bypass: gửi 100 req cuối window cũ + 100 req đầu window mới = 200 req trong 2 giây. Sliding window: track timestamps của requests trong rolling time window — không có burst issue của fixed window; cần store per-user timestamps trong Redis (sorted set).
ZADD key timestamp timestamp; ZREMRANGEBYSCORE key 0 (now-window); ZCARD keyđể đếm requests. Token bucket: user có bucket tokens, mỗi request tiêu 1 token, tokens được refill theo rate (1 token/giây). - Cho phép short bursts (bucket full = burst capacity) nhưng sustainable rate bị limit. Leaky bucket: requests vào queue, xử lý ở fixed rate — smooth output không có bursts; tốt cho APIs cần predictable throughput.
- Implementation:
@upstash/ratelimit(Redis-based, serverless-friendly);express-rate-limit(in-memory mặc định,rate-limit-redischo distributed);ioredis+ custom script. - Differentiated limits:
/api/auth/login(5 req/15min per IP),/api/*(100 req/min per user), public endpoints (1000 req/min).
Pitfall: in-memory rate limiter không work trong multi-instance deployment — mỗi instance có counter riêng, limit thực tế là n×limit.
Clickjacking embed site trong invisible iframe để đánh lừa user click — fix bằng Content-Security-Policy: frame-ancestors 'none' (CSP, recommended) và X-Frame-Options: DENY (legacy, set cả hai để backward compatibility).
Clickjacking là attack embed target site trong invisible iframe, user nghĩ đang click button trên trang của attacker nhưng thực ra đang click element trong iframe — thực hiện actions trên target site mà user không biết. Ví dụ: evil.com embed bank.com trong iframe với opacity: 0, vị trí button 'Transfer Money' trùng với button 'Click to win!' của evil.com. X-Frame-Options header (legacy): X-Frame-Options: DENY — không cho phép embed trong bất kỳ iframe nào; X-Frame-Options: SAMEORIGIN — chỉ cho phép same origin iframe. CSP frame-ancestors directive (modern, recommended): Content-Security-Policy: frame-ancestors 'none' (tương đương DENY); frame-ancestors 'self' (tương đương SAMEORIGIN); frame-ancestors https://trusted.com — flexible hơn X-Frame-Options (cho phép specific origins). Nên set cả hai để backward compatibility. Helmet.js: helmet.frameguard({ action: 'deny' }) set X-Frame-Options; config CSP riêng với frame-ancestors. Khi nào cho phép iframe: legitimate embedding (payment widgets, video player, maps) — whitelist specific origins với frame-ancestors. Pitfall: nếu cần embed legitimate content từ site của bạn vào third-party (widget, badge), phải loại trừ endpoint đó khỏi frame-ancestors restrictions hoặc dùng SAMEORIGIN.
CSP (Content Security Policy) là HTTP header whitelist trusted sources cho scripts, styles, images, fonts — defense in depth chống XSS. default-src: fallback cho directives không specify; script-src: chỉ load scripts từ approved sources; style-src, img-src, font-src, connect-src (fetch/XHR), frame-src, form-action.
- Directives: 'self' (same origin), 'none' (block all), specific domains, 'unsafe-inline' (allow inline — weakens CSP), 'unsafe-eval' (allow eval — dangerous).
- Strict CSP với nonces: script-src 'nonce-{random}' — mỗi request tạo random nonce, inline scripts chỉ chạy nếu có matching nonce attribute; phòng chống injection tốt hơn domain whitelist.
- Hash-based: sha256-{base64hash} thay vì nonce cho static inline scripts.
- Report-Only mode: Content-Security-Policy-Report-Only header — observe violations mà không block, collect reports để debug trước khi enforce. report-uri/report-to: endpoint nhận violation reports dạng JSON — essential để monitor và catch issues sau deploy (ví dụ third-party script bị block).
- Trusted Types API: ngăn DOM XSS bằng cách enforce typed DOM operations — require-trusted-types-for 'script'.
Pitfall: CSP với 'unsafe-inline' và 'unsafe-eval' cho toàn bộ site thực tế vô dụng — common mistake khi copy từ examples mà không hiểu.
- Bắt đầu với Report-Only, collect violations, fix, rồi enforce.
SQL Injection là attack inject SQL code qua user input để manipulate queries, bypass authentication, extract/delete data, hoặc execute OS commands (xp_cmdshell trong MSSQL).
- Classic example: username input là ' OR '1'='1' -- → query trở thành SELECT * FROM users WHERE username = '' OR '1'='1' -- ' AND password = '...' — luôn true, bypass login.
- Second-order injection: input được store vào DB, sau đó dùng trong query khác mà không sanitize — khó phát hiện.
- Phòng chống chính: parameterized queries/prepared statements — placeholder thay vì string concatenation: db.query('SELECT * FROM users WHERE id = $1', [userId]) — user input không bao giờ được interpret là SQL code.
- ORM như Prisma, Sequelize, TypeORM tự động parameterize — nhưng raw queries vẫn nguy hiểm nếu dùng template literals: prisma.$queryRaw
SELECT FROM users WHERE id = ${userId}(safe với tagged template) vs prisma.$queryRawUnsafe('SELECT FROM users WHERE id = ' + userId) (unsafe). - Stored procedures có thể vẫn bị nếu dùng dynamic SQL bên trong.
- Input validation là defense in depth nhưng không thể thay thế parameterized queries — validation có thể bị bypass.
- Principle of least privilege: DB user chỉ có quyền cần thiết — SELECT/INSERT/UPDATE nhưng không DROP TABLE, không xp_cmdshell.
- WAF (Web Application Firewall) thêm layer bảo vệ nhưng không đủ một mình.
Cookie security attributes kiểm soát cookie exposure và transmission.
- HttpOnly: cookie không accessible qua document.cookie hay JavaScript — ngăn XSS đánh cắp session token; vẫn tự động gửi trong HTTP requests.
- Secure: cookie chỉ gửi qua HTTPS — ngăn network sniffing, bắt buộc trong production.
- SameSite: Strict (không gửi cookie trong bất kỳ cross-site request nào — kể cả click link từ email đến site của bạn; rất secure nhưng friction cao cho federated services); Lax (gửi trong top-level navigation GET nhưng không trong img/iframe/fetch cross-site — good balance, default Chrome từ 2020); None (gửi tất cả cross-site — phải kết hợp với Secure, dùng cho third-party cookies như OAuth flows, embedded widgets).
- Best practice cho session cookies: Set-Cookie: sessionId=abc; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600.
- Cookie prefixes tăng security: __Secure- prefix (phải có Secure flag, HTTPS only); __Host- prefix (phải có Secure, không có Domain, Path=/ — strongest, pinned to host).
- Max-Age vs Expires: Max-Age (seconds relative) ưu tiên hơn Expires (absolute datetime) vì không phụ thuộc client clock.
- Session vs Persistent cookies: session cookie (không có Max-Age/Expires) bị xóa khi browser close; persistent có expiry.
- Third-party cookies: đang bị phase out bởi browsers — Privacy Sandbox, Storage Access API là alternatives.
Commit lockfile, chạy npm audit trong CI, dùng Dependabot/Renovate cho auto-update PRs, Socket.dev để detect suspicious packages (không chỉ known CVEs) — cẩn thận install package mới không review (typosquatting).
Dependency security quản lý supply chain risks — dependencies là attack vector phổ biến (log4shell, event-stream incident, ua-parser-js hijack). npm audit: scan known vulnerabilities trong CVE databases, npm audit --json để parse programmatically; npm audit fix tự fix patch versions, --force cho major versions (cẩn thận breaking changes). Mức severity: critical/high cần fix ngay; moderate/low assess risk trước. Dependabot (GitHub built-in): tự động tạo PRs để update dependencies với changelogs — configure trong .github/dependabot.yml với schedule và allowed update types. Renovate (Mend): alternative mạnh hơn Dependabot, grouping updates, auto-merge với passing CI, monorepo support. Snyk: deeper scanning (không chỉ CVEs), license compliance, container scanning, IaC scanning, IDE plugins. Socket.dev: phân tích behavior của packages (không chỉ known CVEs) — detect typosquatting, suspicious code patterns, permission escalation. Nguyên tắc: lock file (package-lock.json, pnpm-lock.yaml) phải commit — reproducible installs; không run npm install --legacy-peer-deps trong production bừa bãi; review package trước install (bundlephobia size, npm downloads, GitHub activity, last publish); prefer packages ít dependencies (dependency tree nhỏ = attack surface nhỏ). Software Bill of Materials (SBOM): generate list of all dependencies và versions — compliance requirement cho enterprise và government. npm publish với provenance — verify package genuinely published từ specific CI run.
Không bao giờ commit secrets vào git — dùng env vars cho level 1, Vault/AWS Secrets Manager cho production với dynamic secrets có TTL; Kubernetes secrets là base64 không phải encrypted (dùng Sealed Secrets hoặc External Secrets Operator).
Secrets (API keys, DB passwords, JWT secrets) không bao giờ được nằm trong code hay version control. Layers of secrets management: Environment variables (level 1): inject qua CI/CD, Docker, K8s secrets — không commit .env production lên git; .env.example làm template không có values thật. Secret scanning: GitHub Secret Scanning tự detect leaked secrets trong commits; GitGuardian cho CI; git-secrets pre-commit hook ngăn commit secrets. Vault solutions (level 2): HashiCorp Vault (centralized, dynamic secrets — tạo DB credentials tạm thời cho mỗi deployment, auto-rotate), AWS Secrets Manager (managed, tích hợp với EC2/Lambda/ECS, auto-rotation cho RDS), Azure Key Vault, GCP Secret Manager. Dynamic secrets: Vault tạo DB user+password unique cho mỗi app instance với TTL — compromised secret vô dụng sau khi expire, không cần rotate manually. SOPS (Secrets OPerationS): encrypt secrets files trong git với age/GPG — cho phép secrets-as-code nhưng encrypted. Kubernetes secrets: base64-encoded không phải encrypted — dùng Sealed Secrets (kubeseal) hoặc External Secrets Operator để sync từ Vault/AWS. Rotation policy: rotate secrets sau offboarding team member, sau breach, và proactively mỗi 90 ngày. Pitfall: log statements in startup console.log('Connecting to', process.env.DATABASE_URL) — log URL thường chứa credentials.
PKCE bắt buộc cho SPA/mobile (không có client secret); access token trong memory, refresh token trong httpOnly cookie — không bao giờ localStorage; BFF pattern là gold standard cho SPAs vì server giữ tokens hoàn toàn.
OAuth 2.0 có nhiều security pitfalls nếu implement không đúng. PKCE (Proof Key for Code Exchange): bắt buộc cho public clients (SPA, mobile app không có server-side). Flow: app tạo code_verifier (random 43-128 chars), tính code_challenge = base64url(sha256(code_verifier)), gửi challenge trong authorization request; khi exchange code → token, gửi verifier; server verify sha256(verifier) === challenge — ngăn authorization code interception. State parameter: random string trong authorization request, verify sau redirect — ngăn CSRF attack trên OAuth flow. Token storage SPA: access token trong memory (JS variable) — không persist qua page refresh, an toàn nhất; refresh token trong httpOnly cookie — không readable từ JS; KHÔNG dùng localStorage (XSS đánh cắp được). Backend For Frontend (BFF) pattern: SPA gọi BFF server, BFF handle OAuth flow và lưu tokens server-side — SPA không bao giờ chạm vào tokens. Scope limitation: request chỉ scopes cần thiết (principle of least privilege); users thấy permission prompt với ít permissions hơn → higher consent rate. Token binding: bind token đến client TLS certificate để ngăn token theft (draft standard). Pitfall: không validate redirect_uri strictly — partial match cho phép attacker redirect token đến subdomain độc hại.
SSRF khiến server gửi request đến internal services (AWS metadata 169.254.169.254, internal APIs) thay vì external URLs — phòng chống bằng allowlist domains, block private IP ranges, và IMDSv2 trên AWS; DNS rebinding cần check IP ngay trước khi connect.
SSRF (Server-Side Request Forgery) xảy ra khi attacker khiến server gửi requests đến URLs mà attacker kiểm soát — bao gồm internal services không accessible từ internet (AWS metadata service 169.254.169.254, internal APIs, databases). Ví dụ: webhook URL validation — fetch(req.body.webhookUrl) → attacker truyền http://169.254.169.254/latest/meta-data/iam/security-credentials/ để đọc AWS IAM credentials. Tại sao nguy hiểm: server thường có quyền truy cập nội bộ rộng hơn external clients; cloud metadata services thường accessible từ container nhưng không từ internet. Phòng chống: Allowlist: chỉ cho phép fetch đến specific domains; không thể fetch sang domains không có trong allowlist. Block private IP ranges: validate URL trước khi fetch — reject 10.x.x.x, 172.16.x.x-172.31.x.x, 192.168.x.x, 127.x.x.x, 169.254.x.x (link-local); Node.js: resolve hostname → check IP range. SSRF prevention library: ssrf-req-filter npm package — tự động block private IPs. Network-level controls: container/serverless không có quyền network đến metadata service (IMDSv2 trên AWS — require token). DNS rebinding attack: attacker kiểm soát DNS, resolve public IP khi validate, resolve private IP khi fetch — fix bằng cách bind socket và check IP ngay trước khi connect. Pitfall: check URL một lần rồi fetch lần khác → DNS rebinding attack opportunity.