CHƯƠNG 10 · SECURITY/INFRA · FINAL · ~140 phút

CORS, CSRF, XSS,
CDN, Load Balancer

Chương cuối — Web security cơ bản (CORS, CSRF, XSS, CSP) + Infrastructure (CDN, Load Balancer, Reverse Proxy, Rate Limit). Phỏng vấn backend/DevOps/SRE hỏi 100%. Học xong, bạn có thể debug "CORS error", thiết kế hệ thống chống DDoS, hiểu nginx config.

10.1 Same-Origin Policy (SOP)

SOPnguyên tắc bảo mật cốt lõi của browser: JS từ origin A KHÔNG được đọc dữ liệu từ origin B.

Origin = scheme + host + port

https://example.com:443/path1   ┐
https://example.com:443/path2   │  cùng origin
https://example.com/            ┘  (port 443 ngầm)

http://example.com              ✗ scheme khác
https://api.example.com         ✗ host khác (subdomain ≠ same)
https://example.com:8080        ✗ port khác

Tại sao cần SOP?

Không có SOP, JS từ evil.com có thể:

  • Đọc cookie của bank.com
  • Gửi AJAX request đến bank.com (với cookie của user)
  • Đọc response → steal data

SOP block tất cả → web mới an toàn. Nhưng có lúc cần legitimate cross-origin → cần CORS để mở "ngoại lệ".

10.2 CORS — Cross-Origin Resource Sharing

CORS là cơ chế server "cho phép" origin khác đọc response của mình. Browser thực thi.

Simple request — chỉ check response headers

Request đáp ứng: GET/HEAD/POST + content type form/text/url-encoded + không có custom header → "simple". Browser gửi luôn, sau đó kiểm response:

# Browser tự thêm
Origin: https://app.example.com

# Server response
Access-Control-Allow-Origin: https://app.example.com
# Hoặc: Access-Control-Allow-Origin: *  (any origin)

Nếu header thiếu / không match origin → browser block JS đọc response. Request vẫn đến server (đó là lý do CSRF vẫn nguy hiểm — xem 10.3).

Preflight — OPTIONS request

Request "non-simple" (PUT/DELETE/PATCH, custom header, JSON body) → browser gửi OPTIONS preflight trước:

# Preflight (browser tự gửi)
OPTIONS /api/data HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization

# Server response
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400         # cache preflight 1 day

# Sau đó browser mới gửi PUT thật
PUT /api/data ...

Credentials (cookies)

Cross-origin request mặc định KHÔNG gửi cookies. Để gửi:

fetch('https://api.example.com/me', {
  credentials: 'include'   // gửi cookies
});

Server phải:

Access-Control-Allow-Origin: https://app.example.com   # KHÔNG được "*"
Access-Control-Allow-Credentials: true

CORS error phổ biến

  • "No 'Access-Control-Allow-Origin' header" → server thiếu header
  • "Wildcard cannot be used with credentials" → khi credentials: include, không được dùng *
  • "Method PUT not allowed" → server không khai báo method
⚠️ CORS không phải "security feature" của server
CORS là feature của browser bảo vệ user. Nếu attacker dùng curl/Postman → CORS không có ý nghĩa. Server vẫn cần auth riêng. CORS chỉ giới hạn: JS trong browser từ origin lạ không đọc response.

10.3 CSRF — Cross-Site Request Forgery

User đã login bank.com (có cookie session). Sau đó visit evil.com. Trang evil chứa:

<form action="https://bank.com/transfer" method="POST">
  <input name="to" value="attacker">
  <input name="amount" value="1000">
</form>
<script>document.forms[0].submit();</script>

Browser tự gửi cookie session bank.com → bank.com tin là user → chuyển tiền. CORS không giúp vì JS evil không cần đọc response, chỉ cần gửi request.

Phòng tránh CSRF

1. SameSite cookie (modern, easy)

Set-Cookie: session=...; SameSite=Lax; Secure; HttpOnly

Browser không gửi cookie với cross-site POST → request đến bank.com không có cookie → bank reject. Default Chrome 80+.

2. CSRF Token (truyền thống)

Server gửi CSRF token trong form. Khi submit, server check token match session.

<form action="/transfer" method="POST">
  <input type="hidden" name="csrf_token" value="abc123xyz">
  <input name="amount">
</form>

Evil.com không biết token (SOP chặn đọc trang bank) → không tạo được form hợp lệ.

3. Custom header

API JSON yêu cầu header X-Requested-With: XMLHttpRequest hoặc Content-Type: application/json. Browser bắt buộc preflight → phát hiện cross-origin.

4. Double-submit cookie

Token gửi cả qua cookie và body. Server check 2 cái khớp. Evil không đặt được cookie cho bank.com.

5. Origin / Referer header check

Server check Origin hoặc Referer header có khớp domain không.

10.4 XSS — Cross-Site Scripting

Attacker chèn JavaScript vào trang của bạn → script chạy trong browser của user khác → đánh cắp cookie, session, làm việc thay user.

3 loại XSS

1. Stored XSS (nguy hiểm nhất)

Script lưu trên server. Vd: comment chứa <script>...</script> → mọi user xem comment đều chạy script.

2. Reflected XSS

Script trong URL, server "echo" lại trong response. Vd: example.com/search?q=<script>alert(1)</script> → server hiển thị "Bạn tìm: <script>..." → script chạy.

3. DOM-based XSS

Server không liên quan. Attacker dụ user vào URL với fragment chứa script. JS client side đọc location.hashinnerHTML → script chạy.

Phòng tránh XSS

1. Output escaping

Mọi user input khi render HTML phải escape:

<  →  &lt;
>  →  &gt;
"  →  &quot;
&  →  &amp;

Modern framework (React, Vue, Angular) escape mặc định. Vue: {{ name }} escape; v-html không escape (nguy hiểm).

2. Tránh innerHTML / dangerouslySetInnerHTML

Dùng textContent thay innerHTML. Dùng React {name} thay dangerouslySetInnerHTML.

3. Sanitize HTML khi cần (vd rich text editor)

Dùng DOMPurify để clean HTML user submit, giữ formatting an toàn (b, i, p) bỏ script.

4. CSP (Content Security Policy)

Xem 10.5.

5. HttpOnly cookie

JS không đọc được session cookie → XSS không lấy được session.

Test XSS

# Thử vào form input:
<script>alert('XSS')</script>
<img src=x onerror=alert(1)>
javascript:alert(1)
"><script>alert(1)</script>

10.5 CSP — Content Security Policy

CSP = whitelist các origin được phép load resource (script, style, image, font, ...). Nếu inline script không trong whitelist → browser block.

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://cdn.jsdelivr.net;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self' https://api.example.com;
  frame-ancestors 'none';

Directive phổ biến

  • default-src: fallback cho mọi loại
  • script-src: nguồn script được phép
  • style-src: nguồn CSS
  • img-src: nguồn image
  • connect-src: AJAX, WebSocket destination
  • frame-ancestors: ai có thể nhúng trang này trong iframe (chống clickjacking)

CSP nghiêm: script-src 'self' → chỉ script từ chính domain, không inline, không eval. Phòng XSS hiệu quả.

Khó khăn với CSP nghiêm

  • Inline <script>...</script> bị block → cần dùng nonce hoặc hash
  • Inline onclick="..." bị block
  • Eval bị block (mọi eval(), new Function())

Best practice: bắt đầu với Content-Security-Policy-Report-Only để chỉ report, không enforce → fix vi phạm trước khi enable thật.

10.6 CDN — Content Delivery Network

CDN = mạng server cache distributed toàn cầu. Cache static asset (image, JS, CSS, video) gần user → giảm latency, giảm load lên origin.

Cách hoạt động

User (VN) ────▶ DNS resolve cloudfront.net │ ▼ (anycast routing) CDN Edge tại Singapore │ │ Cache hit? → trả ngay (5ms) │ Cache miss? → fetch từ origin │ ▼ Origin server (US, 200ms)

2 loại CDN

  • Push CDN: bạn upload asset lên CDN. Phù hợp: nội dung tĩnh, ít thay đổi (images, videos).
  • Pull CDN: CDN tự fetch từ origin lần đầu, cache lại. Phù hợp: web hiện đại — 99% dùng pull.

Cache control

CDN tôn trọng HTTP Cache-Control header từ origin:

Cache-Control: public, max-age=31536000, immutable
# CDN cache 1 năm. Phù hợp asset có hash trong URL (vd app.abc123.js)

Cache-Control: public, max-age=300
# Cache 5 phút trên CDN

Cache-Control: no-cache
# Browser revalidate mỗi lần (CDN có thể vẫn cache cho phép 304)

Cache busting

Khi deploy version mới, asset cũ vẫn cache trên CDN. Solution: thêm hash vào filename — app.abc123.jsapp.def456.js. Webpack/Vite/Next làm tự động.

Lợi ích

  • Latency thấp — server gần user
  • Giảm tải origin — đa số request không đến origin
  • Bandwidth saving — origin chỉ gửi 1 lần đến CDN, CDN serve nhiều user
  • DDoS mitigation — Cloudflare tự filter attack traffic
  • TLS termination — CDN handle TLS, origin có thể HTTP nội bộ

Major CDN

Cloudflare, Akamai, Fastly, AWS CloudFront, Google Cloud CDN, Vercel Edge.

10.7 Load Balancer

Load Balancer (LB) phân phối traffic giữa nhiều server backend → scale + failover.

Algorithms

  • Round Robin: server lần lượt — đơn giản, đều
  • Weighted Round Robin: server mạnh nhận nhiều hơn
  • Least Connections: gửi đến server đang có ít connection — tốt cho long-lived connection
  • Least Response Time: gửi đến server respond nhanh nhất
  • IP Hash: hash(client IP) → cùng client luôn cùng server (sticky session đơn giản)
  • Random: random — đôi khi tốt vô cùng (paradox)
  • Power of Two Choices: pick 2 random, chọn cái ít connection hơn — gần optimal

Health Check

LB định kỳ ping backend (vd GET /health mỗi 5s). Server fail check → remove khỏi pool. Khi healthy lại → re-add.

Sticky Session (Session Affinity)

Cùng user → cùng backend server (qua cookie hoặc IP hash). Cần khi server lưu state in-memory (vd shopping cart).

Tránh nếu có thể — dùng shared state (Redis) thay sticky → dễ scale.

L4 vs L7 LB (đã học chương 1)

  • L4: TCP/UDP — nhanh, không HTTP-aware. AWS NLB, HAProxy TCP mode.
  • L7: HTTP — content-based routing (URL, header). AWS ALB, nginx, HAProxy HTTP mode.

Active-Active vs Active-Passive

  • Active-Active: tất cả backend đang serve traffic — tốt
  • Active-Passive: 1 active, các backup standby. Failover khi active down. Dùng cho database master-slave.

10.8 Reverse Proxy vs Forward Proxy

Forward Proxy

Đứng trước client. Client gửi request qua proxy → proxy chuyển tới Internet.

Use case: corporate firewall, anonymizer (Tor exit node), content filter (school).

Client → Forward Proxy → Internet

Reverse Proxy

Đứng trước server. Client request đến proxy (tưởng đó là server) → proxy chuyển tới backend thật.

Use case: nginx, HAProxy, Cloudflare. Hide backend, LB, TLS termination, caching.

Client → Reverse Proxy → Backend servers

Sự khác biệt: forward proxy đại diện client ra ngoài; reverse proxy đại diện server với client.

10.9 nginx — Swiss Army knife

nginx = web server + reverse proxy + LB + cache + static file server. Tool quan trọng nhất với backend dev.

Reverse proxy đơn giản

server {
  listen 80;
  server_name example.com;

  location / {
    proxy_pass http://localhost:3000;       # forward đến Node app
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}

Load balancer

upstream backend {
  least_conn;                              # algorithm
  server 10.0.0.1:3000 weight=3;
  server 10.0.0.2:3000;
  server 10.0.0.3:3000 backup;             # standby
}

server {
  listen 80;
  location / {
    proxy_pass http://backend;
  }
}

HTTPS termination + redirect

# Redirect HTTP → HTTPS
server {
  listen 80;
  server_name example.com;
  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl http2;
  server_name example.com;

  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

  location / { proxy_pass http://localhost:3000; }
}

Static file + cache

location /static/ {
  alias /var/www/static/;
  expires 1y;
  add_header Cache-Control "public, immutable";
  try_files $uri =404;
}

Rate limiting (xem 10.10)

http {
  limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;

  server {
    location /api/ {
      limit_req zone=mylimit burst=20 nodelay;
      proxy_pass http://backend;
    }
  }
}

10.10 Rate Limiting

Rate Limiting = giới hạn số request/đơn vị thời gian — chống abuse, DDoS, brute force.

Algorithms

1. Fixed Window

Đếm request trong window cố định (vd "100 req/phút"). Reset đầu mỗi phút. Đơn giản nhưng "burst boundary" (cuối phút này + đầu phút sau có thể spike 200 req trong vài giây).

2. Sliding Window Log

Lưu log timestamp mỗi request. Đếm request trong khoảng [now - 1min, now]. Chính xác nhưng tốn memory.

3. Sliding Window Counter

Combine fixed window cũ + tỷ lệ window mới. Chính xác hơn fixed, ít memory hơn log.

4. Token Bucket

Bucket có capacity N. Mỗi giây refill X token. Mỗi request consume 1 token. Hết token → reject.

capacity = 100, refill = 10/s
Bucket: ●●●●●●●●●●... (max 100)
Request → consume token. Hết → 429 Too Many.
Lý tưởng: cho phép burst (dùng hết bucket nhanh) nhưng giới hạn rate dài hạn.

5. Leaky Bucket

Request vào queue có capacity. Server xử lý với rate cố định. Queue đầy → reject.

Khác token bucket: token bucket cho phép burst, leaky bucket smooth out (rate cố định).

Implement với Redis

// Sliding window log với Redis sorted set
async function checkLimit(userId, max = 100, windowSec = 60) {
  const now = Date.now();
  const windowStart = now - windowSec * 1000;
  const key = `rate:${userId}`;

  await redis.zremrangebyscore(key, 0, windowStart);     // xoá log cũ
  const count = await redis.zcard(key);                   // đếm hiện tại

  if (count >= max) return false;                          // rate limited

  await redis.zadd(key, now, `${now}-${Math.random()}`);
  await redis.expire(key, windowSec);
  return true;
}

Status code

429 Too Many Requests + header Retry-After: 60 hoặc X-RateLimit-Remaining: 0.

10.11 DDoS — Distributed Denial of Service

Attacker huy động nhiều máy (botnet) gửi request đồng thời → server quá tải → user thật không truy cập được.

Loại tấn công

  • Volumetric (L3/L4): flood network với packet/byte. Vd UDP flood, ICMP flood. Defense: scrubbing center của ISP.
  • Protocol: SYN flood (consume server connection table), Slowloris (giữ connection mở chậm). Defense: SYN cookies, timeout.
  • Application (L7): HTTP flood, request đắt CPU. Defense: rate limit, CAPTCHA, WAF.

Mitigation

  • CDN (Cloudflare, AWS Shield) — anycast hấp thụ traffic + filter
  • Rate limiting ở edge
  • WAF (Web Application Firewall) — block bot pattern
  • CAPTCHA cho request đáng nghi
  • Auto-scale backend (cẩn thận hết tiền 💸)
  • IP reputation + blocklist

Major DDoS attack 2018: GitHub bị 1.35 Tbps memcached amplification — Akamai cứu được trong 10 phút.

Bài tập

Bài 1 — CORS debug

Tạo Express server không có CORS config. Tạo HTML page khác origin gọi fetch. Quan sát error trong DevTools Console. Thêm cors middleware → fix.

Bài 2 — Preflight

Gửi request từ JS có Content-Type: application/json + custom header. Quan sát OPTIONS preflight trong DevTools Network. Server phải response gì?

Bài 3 — XSS sandbox

Tạo HTML page có <div>Hi {name}</div> với name từ URL query. Test với ?name=<script>alert(1)</script>. Có alert? Sửa: dùng textContent.

Bài 4 — CSP test

Set CSP script-src 'self'. Trang có <script>alert(1)</script> inline → block. Console show error.

Bài 5 — nginx reverse proxy

Cài nginx local. Config reverse proxy đến http://localhost:3000 (Node app). Test: curl http://localhost → response của Node.

Bài 6 — Rate limit

Implement sliding window log với Redis trong Express. Test: gửi 100 request trong 1s, thấy 429.

Bài 7 — CDN cache

Deploy site lên Vercel/Netlify (free). DevTools Network: cột "Server" thấy CDN edge. Header cf-cache-status: HIT hoặc x-vercel-cache: HIT.

🧪 Quiz cuối chương

Câu 1. Same-Origin Policy — origin = ?

  • Chỉ host
  • scheme + host + port (vd https://example.com:443)
  • Chỉ scheme
  • IP address

Đáp án: scheme + host + port. https://a.com:443 ≠ http://a.com (scheme khác) ≠ https://api.a.com (host khác).

Câu 2. CORS preflight (OPTIONS) trigger khi nào?

  • Mọi cross-origin request
  • Chỉ khi request lỗi
  • Khi request "non-simple": method PUT/DELETE/PATCH, custom header, Content-Type JSON
  • Khi cookies enabled

Đáp án: non-simple request. Simple request (GET/HEAD/POST + form/text body) không preflight.

Câu 3. CORS có chống CSRF không?

  • Không — CSRF không cần đọc response. Cookie SameSite hoặc CSRF token mới chống được
  • Có, hoàn toàn
  • Có nếu credentials: include
  • Có với HTTPS

Đáp án: không. CORS chặn JS đọc response, nhưng request vẫn đến server. CSRF lợi dụng "request gửi được" → cần SameSite cookie hoặc CSRF token.

Câu 4. Cách HIỆU QUẢ NHẤT chống CSRF trong app modern?

  • HTTPS
  • Captcha
  • SameSite=Lax cookie (default Chrome 80+) + CSRF token cho form quan trọng
  • Block cross-origin IP

Đáp án: SameSite cookie + CSRF token. SameSite Lax đã fix nhiều CSRF tự động. Token cho an toàn hơn.

Câu 5. XSS phòng tránh chính bằng?

  • Encryption
  • Output escaping (escape HTML khi render user input) + HttpOnly cookie + CSP
  • HTTPS
  • Same-Origin Policy

Đáp án: escape + HttpOnly + CSP. Modern framework (React, Vue) escape mặc định.

Câu 6. Reverse Proxy khác Forward Proxy?

  • Cùng nghĩa
  • Reverse nhanh hơn
  • Forward đứng trước client (đại diện client ra ngoài); Reverse đứng trước server (đại diện server với client)
  • Forward dùng HTTP, Reverse dùng HTTPS

Đáp án: phía client vs phía server. nginx, Cloudflare = reverse proxy. Tor exit, corporate firewall = forward.

Câu 7. Sticky session là?

  • Session không expire
  • LB gửi cùng client đến cùng backend (qua cookie/IP hash) — cần khi server lưu state in-memory
  • Cookie HttpOnly
  • Refresh token

Đáp án: cùng client → cùng backend. Tránh nếu có thể (dùng shared state Redis) — sticky khó scale.

Câu 8. Token Bucket vs Leaky Bucket?

  • Token Bucket cho phép burst (dùng hết bucket nhanh); Leaky Bucket smooth rate cố định
  • Hai cái giống nhau
  • Leaky Bucket nhanh hơn
  • Token Bucket chỉ cho TCP

Đáp án: Token = burst OK, Leaky = smooth. Token bucket phổ biến hơn vì cho phép spike traffic ngắn.

Tổng kết chương 10 — và toàn bộ giáo trình

  • Same-Origin Policy: nguyên tắc bảo mật cốt lõi browser; origin = scheme+host+port
  • CORS: server "cho phép" origin khác đọc response; preflight OPTIONS cho non-simple request
  • CSRF: lợi dụng cookie auto-gửi; phòng = SameSite cookie + CSRF token
  • XSS: chèn JS vào trang; phòng = output escaping + HttpOnly + CSP
  • CSP: whitelist origin được phép load resource
  • CDN: cache distributed; pull CDN phổ biến; cache busting với hash filename
  • Load Balancer: round-robin/least-conn/IP hash; health check; sticky session; L4 vs L7
  • Reverse Proxy (nginx, Cloudflare) vs Forward Proxy
  • nginx: web server + reverse proxy + LB + cache + TLS termination
  • Rate Limiting: fixed/sliding window, token/leaky bucket; status 429
  • DDoS: volumetric/protocol/application; mitigate qua CDN, WAF, rate limit
🎓 Kết thúc giáo trình Networking Mastery

Bạn đã đi qua 10 chương — từ OSI/TCP-IP đến CDN/Load Balancer. Cùng với DSA + OS, bạn đã có 3/6 trụ cột nền tảng IT. Còn lại: Database, OOP/Design Patterns, System Design.

Đề xuất thực hành sau Networking:

  • Cài Wireshark + nginx + Cloudflare account (free) — chơi với chúng
  • Đọc High Performance Browser Networking (Ilya Grigorik, free)
  • Build mini project: web app HTTPS + nginx reverse proxy + Cloudflare CDN + JWT auth
  • Thử CTF hoặc HackTheBox cho web security thực hành

Chúc bạn thành công ở mọi vòng phỏng vấn backend / DevOps / SRE!

← Chương trước Chương 09: Auth Hoàn thành 🎉 Quay về trang chủ Networking