Chương 07 · Edge & Security

CDN, Rate Limiting, API Gateway, Security at Scale

CDN architecture, 4 thuật toán rate limiting, API Gateway pattern, DDoS/WAF, edge auth, secret management, observability ở quy mô.

1. Content Delivery Network (CDN)

1.1. Khái niệm

CDN = mạng server (edge POP — Point of Presence) phân tán toàn cầu, cache content gần user. Request đi đến edge gần nhất thay vì origin xa.

╔══════════════════════╗ ║ Origin (US-East) ║ ╚══════════════════════╝ ▲ │ Cache miss (rare) ┌───────────────┼───────────────┐ │ │ │ ┌────▼─────┐ ┌──────▼──────┐ ┌────▼─────┐ │ POP US │ │ POP EU │ │ POP APAC │ ← edge cache └──────────┘ └─────────────┘ └──────────┘ ▲ ▲ ▲ │ │ │ US user EU user APAC user

1.2. Lợi ích

  • Latency thấp — user APAC hit POP Singapore (~30ms) thay vì origin Virginia (~250ms).
  • Giảm tải origin — 90%+ traffic phục vụ từ edge cache.
  • Bandwidth tiết kiệm — cache hit không qua network origin.
  • DDoS protection — edge hấp thụ traffic spike.
  • Geographic redundancy — 1 region down, edge khác phục vụ.

1.3. Cache control

# Static asset — cache lâu, immutable URL versioning
Cache-Control: public, max-age=31536000, immutable
# /static/app.v123.js — đổi nội dung = đổi URL

# HTML — cache ngắn vì có thể đổi
Cache-Control: public, max-age=300, must-revalidate

# Personalized API response — không cache hoặc private
Cache-Control: private, max-age=60
# private = chỉ browser cache, không CDN/proxy

# Sensitive — không cache
Cache-Control: no-store

1.4. CDN tools

  • Cloudflare — phổ biến, free tier mạnh, edge compute (Workers).
  • AWS CloudFront — tích hợp AWS ecosystem.
  • Fastly — programmable VCL, low TTFB.
  • Akamai — enterprise, lâu đời.
  • Bunny.net — affordable, performance tốt.

1.5. CDN cho dynamic content

Truyền thống CDN cache static. Hiện đại có thể cache dynamic ngắn hạn:

  • Edge SWR (Cloudflare, Vercel) — stale-while-revalidate ở edge.
  • Edge SSR — render trang ở edge (Cloudflare Workers, Vercel Edge Functions).
  • API caching — GET response với cache-friendly header.

1.6. Cache invalidation

# Cloudflare API:
curl -X POST "https://api.cloudflare.com/client/v4/zones/<id>/purge_cache" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"files":["https://example.com/api/products"]}'

# Hoặc purge by tag:
curl -X POST ... -d '{"tags":["product-list"]}'

Pattern: tag CDN cache với Cache-Tag header → bulk purge khi data đổi.

2. Rate Limiting

Giới hạn request từ 1 client để: tránh abuse, công bằng giữa user, bảo vệ backend.

2.1. Bốn thuật toán

2.1.a. Fixed Window

Đếm request trong window cố định (vd: mỗi phút). Reset mỗi window.

// Window: 1 phút, max 100 req
async function fixedWindow(userId: string) {
  const window = Math.floor(Date.now() / 60_000);   // window key
  const key = `rl:${userId}:${window}`;
  const count = await redis.incr(key);
  if (count === 1) await redis.expire(key, 60);
  if (count > 100) throw new Error('Rate limit exceeded');
}

Vấn đề: "burst boundary" — user request 100 lần ở giây 59 + 100 lần ở giây 1 phút sau = 200 request trong 2 giây.

2.1.b. Sliding Window Log

Lưu timestamp mọi request. Đếm request trong N giây gần nhất.

async function slidingLog(userId: string, limit = 100, windowMs = 60_000) {
  const key = `rl:${userId}`;
  const now = Date.now();
  const cutoff = now - windowMs;

  // Xóa entry cũ
  await redis.zremrangebyscore(key, '-inf', cutoff);

  // Đếm trong window
  const count = await redis.zcard(key);
  if (count >= limit) throw new Error('Rate limit exceeded');

  // Add request mới
  await redis.zadd(key, now, `${now}-${Math.random()}`);
  await redis.expire(key, Math.ceil(windowMs / 1000));
}

(+) Chính xác. (−) Tốn memory (lưu mọi timestamp).

2.1.c. Sliding Window Counter

Compromise giữa fixed (rẻ) và log (chính xác). Weight 2 window kế nhau:

// Tại giây 30 của window hiện tại:
// rate = (count_previous_window * 30/60) + count_current_window
// Smooth giữa 2 window cố định

2.1.d. Token Bucket

Mỗi user có "bucket" với N token. Mỗi request tốn 1 token. Token tự refill rate R/s. Cho phép burst đến N nhưng sustained rate = R.

async function tokenBucket(userId: string, capacity = 100, refillRatePerSec = 10) {
  const key = `tb:${userId}`;
  const now = Date.now();

  // Lua script atomic — Redis EVAL
  const lua = `
    local key = KEYS[1]
    local cap = tonumber(ARGV[1])
    local rate = tonumber(ARGV[2])
    local now = tonumber(ARGV[3])

    local data = redis.call('HMGET', key, 'tokens', 'last')
    local tokens = tonumber(data[1]) or cap
    local last = tonumber(data[2]) or now

    local elapsed = math.max(0, now - last) / 1000
    tokens = math.min(cap, tokens + elapsed * rate)

    if tokens < 1 then return 0 end
    tokens = tokens - 1

    redis.call('HMSET', key, 'tokens', tokens, 'last', now)
    redis.call('EXPIRE', key, 3600)
    return 1
  `;

  const allowed = await redis.eval(lua, 1, key, capacity, refillRatePerSec, now);
  if (!allowed) throw new Error('Rate limit exceeded');
}

Token Bucket là phổ biến nhất production. AWS, Stripe đều dùng.

2.1.e. Leaky Bucket

Queue với fixed leak rate. Request quá fast → queue đầy → reject. Smooth rate output.

Tương tự token bucket nhưng output rate cố định — không cho burst.

2.2. So sánh

AlgorithmMemoryBurstUse case
Fixed WindowRất ítCó boundary issueĐơn giản, OK cho rough limit
Sliding LogCao (mọi timestamp)SmoothCần chính xác cao
Sliding CounterÍtSmooth approximationProduction sweet spot
Token BucketÍtCho burst đến capacityAPI rate limit (Stripe, AWS)
Leaky BucketÍtSmooth output rateNetwork shaping, queue

2.3. Distributed rate limiting

Multiple instance share state qua Redis. Atomic operation (INCR, Lua script) đảm bảo accuracy.

Vấn đề: Redis hot key cho heavy user. Giải:

  • Shard rate limit theo userId.
  • Sample-based (chỉ count 1/10 request, multiply).
  • Two-tier: per-instance counter + sync với Redis định kỳ.

2.4. HTTP response

HTTP/1.1 429 Too Many Requests
Retry-After: 60                # client retry sau 60s
X-RateLimit-Limit: 100          # max
X-RateLimit-Remaining: 0        # còn lại
X-RateLimit-Reset: 1709337600   # Unix timestamp khi reset

2.5. Rate limit theo gì?

  • IP address — cho anonymous request. Cẩn thận NAT (nhiều user share IP).
  • API key / userId — cho authenticated.
  • Endpoint-specific — login khắt khe hơn search.
  • Tier-based — free user 100/h, pro user 10000/h.

3. API Gateway

3.1. Vai trò

Single entry point cho tất cả API. Đứng giữa client và microservice.

Client ──► API Gateway ──► User Service │ ├──► Order Service │ ├──► Search Service │ └──► Notification Service

Gateway xử lý "cross-cutting concerns":

  • Authn / Authz — verify JWT, OAuth.
  • Rate limiting.
  • Request/Response transformation — REST ↔ gRPC.
  • Routing — path → service.
  • Caching.
  • Logging / Metrics.
  • Versioning — /v1/, /v2/.
  • Aggregation — 1 client request → query nhiều service, gộp response.

3.2. BFF — Backend For Frontend

Pattern: 1 gateway riêng cho mỗi client type (web, mobile, public API). BFF gọi nhiều microservice và assemble response phù hợp với UI.

     Web ──────────► BFF Web ──────► User svc
                                  └─► Product svc
                                  └─► Cart svc

     Mobile ───────► BFF Mobile ──► (same services, optimized payload)

     3rd-party ────► Public API GW ─► (rate-limited, restricted)

3.3. Tools

  • Kong — open-source, plugin system phong phú.
  • AWS API Gateway — managed, tích hợp Lambda.
  • Apigee (Google) — enterprise.
  • Tyk — open-source.
  • Envoy + custom config — flexible.
  • nginx + Lua / OpenResty — DIY.

3.4. Anti-pattern: God Gateway

Gateway tích hợp business logic → biến thành monolith trá hình. Quy tắc: gateway chỉ làm infrastructure concern — auth, rate limit, route. Business logic ở service.

4. DDoS Protection

Distributed Denial-of-Service — attacker phối hợp nhiều máy gửi flood traffic làm app sập.

4.1. Phân loại

  • Volumetric (L3/L4) — flood bandwidth. UDP flood, ICMP flood, amplification (DNS, NTP).
  • Protocol (L4) — exploit protocol. SYN flood, Ping of Death.
  • Application (L7) — request hợp lệ nhưng đắt (heavy SQL, scrape data). Khó phát hiện hơn.

4.2. Defense layers

  1. CDN edge absorption — Cloudflare, AWS Shield. Edge có hàng Tbps capacity, hấp thụ volumetric attack.
  2. Rate limiting — per IP, per session, per endpoint.
  3. Geographic filtering — block country bạn không phục vụ.
  4. Challenge — CAPTCHA, JS challenge khi suspect bot.
  5. Anycast — distribute attack tự nhiên qua nhiều POP.
  6. Anomaly detection — ML model phát hiện pattern bất thường.
  7. Capacity buffer — autoscale up khi traffic tăng.

4.3. AWS Shield Standard / Advanced

  • Standard: free, tự động, bảo vệ L3/L4.
  • Advanced: $3000/month, bảo vệ L7, hỗ trợ chuyên gia, refund khi attack.

4.4. Phòng tránh ở app code

  • Pagination max size — không cho query 1M record/request.
  • Query timeout (statement_timeout Postgres).
  • Connection pool max — không vô hạn.
  • Idempotency check.
  • Negative cache — tránh ai đó request id không tồn tại spam DB.

5. WAF — Web Application Firewall

Filter HTTP request theo rule. Chặn request độc hại trước khi đến app.

5.1. Bảo vệ những gì

  • SQL Injection — pattern ' OR 1=1, UNION SELECT.
  • XSS<script>, onerror=.
  • CSRF — referer check.
  • Path traversal../../../etc/passwd.
  • Bot — User-Agent suspicious, signature.
  • Account takeover — credential stuffing.

5.2. OWASP Core Rule Set

Bộ rule open-source phổ biến. Tools (ModSecurity, Cloudflare WAF, AWS WAF) đều support.

5.3. Trade-off

  • (+) Block known attack pattern tự động.
  • (−) False positive — block request hợp lệ.
  • (−) Chỉ chặn known pattern; zero-day vẫn lọt.

Quy tắc: WAF là 1 layer; không phải solution duy nhất. Combine với input validation, prepared statement (cho SQLi), CSP (cho XSS).

6. Edge Authentication

Verify auth token ở edge thay vì để mỗi service. Tiết kiệm latency, centralize auth logic.

6.1. JWT validation at edge

Stateless: edge có public key, verify JWT signature. Không cần round-trip về central auth service.

// Cloudflare Worker / Lambda@Edge
export default {
  async fetch(request: Request) {
    const auth = request.headers.get('Authorization');
    if (!auth?.startsWith('Bearer ')) return new Response('401', { status: 401 });

    const token = auth.slice(7);
    try {
      const payload = await jwtVerify(token, PUBLIC_KEY);
      // Forward request với userId trong header
      const newReq = new Request(request);
      newReq.headers.set('X-User-Id', payload.sub);
      return fetch(newReq);
    } catch {
      return new Response('401', { status: 401 });
    }
  },
};

6.2. Trade-off

  • (+) Latency thấp — verify ở edge gần user.
  • (+) Backend service không cần verify lại (trust X-User-Id header từ edge).
  • (−) Stateless JWT không thể "logout immediate" — phải đợi expire.
  • (−) JWT lớn hơn session ID nhiều → bandwidth.
  • (−) Compromise key = compromise toàn hệ.

Pattern thực dụng: Short-lived JWT (15 phút) + refresh token (lâu hơn, có thể revoke ở DB).

6.3. mTLS giữa service

Mutual TLS — client và server đều có cert. Service Mesh (Istio, Linkerd) handle tự động qua sidecar.

Lợi: zero-trust network — kể cả attacker đã vào internal network, không gọi được service mà không có cert.

7. Secret Management

Database password, API key, encryption key — KHÔNG hardcode trong code, KHÔNG commit git.

7.1. Anti-patterns

  • Secret trong git repo (kể cả .env không gitignore).
  • Secret trong Dockerfile.
  • Secret trong Slack message / email.
  • Secret trong log file.
  • Cùng secret cho dev/staging/prod.

7.2. Tools

  • HashiCorp Vault — open-source, feature đầy đủ, dynamic secrets.
  • AWS Secrets Manager / Parameter Store — managed, tích hợp IAM.
  • GCP Secret Manager.
  • Azure Key Vault.
  • Doppler, Infisical — SaaS, simpler.
  • Kubernetes Secrets — base64 (NOT encryption!), thường dùng with KMS.

7.3. Best practices

  • Rotation — đổi secret định kỳ (90 ngày). Tools support automated rotation.
  • Least privilege — service chỉ access secret cần thiết.
  • Audit log — mọi access đến secret được log.
  • Encryption at rest — secret luôn encrypted khi lưu.
  • Encryption in transit — TLS giữa app và secret store.
  • Short-lived — dynamic secrets (Vault sinh password 1h cho DB).

7.4. Detect leak

  • git-secrets, truffleHog — pre-commit hook scan secret.
  • GitHub Secret Scanning — auto detect commit có pattern API key.
  • Snyk, SonarQube — CI scan.

Khi secret leak: rotate immediate, không "thôi để cho qua". Attacker monitor public repo.

8. Observability at Scale

Đã đề cập tracing ở Ch6. Đây tổng hợp 3 pillars + best practice ở scale.

8.1. Three pillars

  • Logs — text events, structured (JSON). Tools: ELK, Loki, Splunk, Datadog Logs.
  • Metrics — numeric time-series. Tools: Prometheus, InfluxDB, Datadog, Grafana.
  • Traces — request path. Tools: Jaeger, Tempo, Honeycomb, Datadog APM.

8.2. Cost control

Logs ở scale tốn nhiều: 1M req/day × 1KB/req = 1GB/day, nhân 30 service. Đắt khi dùng SaaS.

Strategies:

  • Log levels — INFO/WARN/ERROR ở prod; DEBUG chỉ khi cần.
  • Sampling — log 1% successful + 100% errors.
  • Retention tier — hot 7 ngày, cold 90 ngày, archive 1 năm.
  • Self-host — Loki/Grafana cho cost-conscious team.
  • Cardinality control — metric label phải bounded (KHÔNG put userId làm label).

8.3. Structured logging

// ❌ Plain text
logger.info(`User ${userId} created order ${orderId}`);

// ✓ Structured JSON
logger.info('order.created', {
  user_id: userId,
  order_id: orderId,
  amount: order.total,
  trace_id: ctx.traceId,
});

Lợi: queryable. {level=error AND trace_id=xxx}. Faceted search.

8.4. Correlation ID

Mỗi request có unique ID propagate qua mọi service. Log mọi service kèm ID này → search 1 request flow.

app.use((req, res, next) => {
  req.traceId = req.header('X-Trace-Id') || crypto.randomUUID();
  res.setHeader('X-Trace-Id', req.traceId);
  next();
});

// Mọi log:
logger.info('event', { traceId: req.traceId, ... });

Tốt nhất: tích hợp với OpenTelemetry → trace_id tự động trong log.

8.5. Metrics quan trọng — Golden Signals (Google SRE)

  1. Latency — P50, P95, P99 (cả success và error).
  2. Traffic — request/s.
  3. Errors — rate of 5xx, 4xx.
  4. Saturation — CPU, memory, disk, queue depth.

8.6. Alerting

Alert based on SLO violation, không noise:

  • Error rate > 1% trong 5 phút → page on-call.
  • P99 > 1s trong 10 phút → warning.
  • Disk > 80% → warning.
  • Disk > 95% → page.
  • Replication lag > 30s → warning.

Alert fatigue là enemy. 1 alert <-> 1 actionable problem.

9. Bài tập

  1. Setup Cloudflare cho 1 static site: cache control header, Cache-Tag, purge by tag.
  2. Implement 4 thuật toán rate limiting trong Node.js + Redis. Đo memory, accuracy, performance khác biệt.
  3. Token Bucket atomic với Lua script: chi tiết. Test với concurrent request đồng thời, đảm bảo không over-allow.
  4. Setup Kong API Gateway local: route /users/* → user-service, /orders/* → order-service. Thêm plugin: rate limit, JWT auth, request logging.
  5. Phòng thủ cơ bản:
    • Setup CSP cho 1 web app (đã học Networking Ch10).
    • Add rate limit cho /login (5 attempt/15 phút per IP).
    • JWT verify ở edge với Cloudflare Worker.
  6. Secret management: cài Vault local, store DB password, app fetch dynamic secret. Test rotation.
  7. Setup Prometheus + Grafana cho 1 service Node.js. Expose 4 golden signals. Tạo dashboard + alert khi error rate > 1%.
  8. OpenTelemetry: integrate vào 2 service Node.js. Trace request cross-service. Visualize trên Jaeger.

10. Quiz

Quiz cuối Chương 7

CDN giải vấn đề chính:

  • Encryption
  • Database scaling
  • Latency cho user xa origin + giảm tải origin + DDoS absorption + bandwidth tiết kiệm
  • Compute
CDN = mạng edge cache toàn cầu. User APAC hit POP Singapore (~30ms) thay origin Virginia (~250ms). 90%+ traffic phục vụ từ edge → origin rảnh. Edge có Tbps capacity hấp thụ DDoS. Cloudflare/CloudFront/Fastly là common.

Token Bucket rate limiter:

  • Đếm fixed window
  • Lưu mọi timestamp
  • Smooth output rate cố định
  • Bucket có N token, mỗi request tốn 1 token, refill rate R/s; cho phép burst đến N nhưng sustained = R
Phổ biến nhất production. AWS, Stripe đều dùng. User offline lâu → bucket đầy → cho burst trở lại. Rate sustained không đổi. Implement atomic qua Redis Lua. Token Bucket = (capacity, refill_rate). Leaky Bucket khác: output rate cố định, không cho burst.

Fixed Window rate limiting có vấn đề "burst boundary":

  • User có thể request 100 lần ở giây 59 + 100 lần ở giây 1 phút sau = 200 trong 2 giây
  • Tốn nhiều memory
  • Slow
  • Không atomic
Window cố định reset mỗi N giây. Ở ranh giới window, có thể double rate. Sửa: Sliding Window (Log hoặc Counter), Token Bucket. Sliding Counter là sweet spot — chính xác như Sliding Log nhưng tốn ít memory.

API Gateway xử lý cross-cutting concerns:

  • Business logic
  • Auth, rate limit, route, logging, transformation, caching — KHÔNG business logic
  • Database query
  • UI rendering
Gateway là infrastructure layer. Khi gateway tích business logic → "God Gateway" anti-pattern. Quy tắc: gateway làm "đường" (auth, route, rate, log); service làm "nhà" (business logic). BFF cũng tương tự nhưng riêng cho mỗi client type.

DDoS L7 (application layer) khó phát hiện hơn L3/L4 vì:

  • Cần thêm hardware
  • Network limit
  • Request hợp lệ về syntax (đúng HTTP, đúng API), chỉ "đắt" khi xử lý — khó phân biệt với traffic thật
  • CDN không protect
L3/L4 attack có pattern rõ (SYN flood, UDP amplification) — CDN/firewall block dễ. L7: attacker gửi request hợp lệ vào endpoint đắt (search complex query). Detect cần ML/anomaly + rate limit khôn ngoan + capacity buffer.

JWT validation at edge tradeoff chính:

  • Slow
  • Tốn RAM
  • Không secure
  • (+) Latency thấp, không cần round-trip auth service. (−) Stateless → không "logout immediate", phải đợi expire — pattern: short-lived JWT (15min) + refresh token có thể revoke
JWT tự chứa info, edge có public key verify. Backend trust X-User-Id từ edge. Nhược: revoke khó. Pattern thực dụng: access token 15min stateless + refresh token với DB lookup (revoke được). Cloudflare Worker, Lambda@Edge phổ biến cho pattern này.

Secret management best practice:

  • Lưu trong Vault/Secrets Manager, rotation định kỳ, least privilege, audit log; KHÔNG hardcode/commit/log
  • .env file commit git
  • Secret giống nhau dev/prod
  • Cùng app code chứa
Mỗi env (dev/staging/prod) có secret riêng. Vault/AWS Secrets Manager/Doppler quản lý. Tools rotation tự động. Audit log mọi access. Detect leak qua git-secrets/truffleHog. Khi leak: ROTATE NGAY, không "thôi cho qua".

Google SRE "Four Golden Signals" cho monitoring:

  • CPU, RAM, Disk, Network
  • Latency, Traffic, Errors, Saturation — base cho alert SLO
  • QPS, BPS, IPS, FPS
  • Health, Memory, Cache, Threads
SRE Book của Google. Latency P50/P95/P99 (cả success/error). Traffic = req/s. Errors = rate 5xx/4xx. Saturation = CPU/mem/disk/queue. Đủ để alert SLO violation. Alert ít noise: 1 alert ↔ 1 actionable problem; tránh alert fatigue.

Hoàn thành Chương 7. Tiếp theo: Chương 8 — Design Case Studies →