Skip to content

Security — WAF, Headers & Data Protection

Sources: omni-web-gateway/lib/runtime/waf-stack.ts, omni-web-content-frontend/src/next.config.ts

Security is applied at two layers:

  1. Gateway (CloudFront + WAF) — Network-level protection, bot control, geo-blocking
  2. MFE (Next.js headers) — Application-level security headers

MFE teams get WAF protection automatically through the gateway. Security headers are configured per-MFE in next.config.ts.

The WAF is deployed in us-east-1 (required for CloudFront) and attached to the gateway distribution.

PriorityRuleActionPurpose
0Country BlockBlockBlocks traffic from high-risk countries (BR, AR, VN, RU, IN, CN, etc.)
1ASN BlockBlockBlocks specific autonomous systems
2Country ChallengeChallengeCAPTCHA for medium-risk countries (ZA, JP, ID, PH, MY, NP)
3AWS Common Rule SetEnforceOWASP Top 10 protections (XSS, SQLi, etc.)
4Known Bad InputsEnforceBlocks known malicious patterns
5IP Reputation ListEnforceBlocks IPs with bad reputation
6Bot ControlCount (observe)Detects and classifies bots

Verified bots are excluded from blocking to preserve SEO and integrations:

  • Search engines (Googlebot, Bingbot)
  • SEO crawlers
  • Advertising bots
  • Content fetchers
  • Monitoring (Pingdom, Datadog)
  • Social media (link previews)
  • Email clients (SafeLinks, prefetch)

WAF logs automatically redact sensitive data:

  • Request level: Authorization headers, cookies (access_token, refresh_token, session IDs)
  • Log level: IP addresses and email addresses are masked via CloudWatch data protection policy
dataProtectionConfig: {
dataProtections: [
{ action: 'SUBSTITUTION', field: { fieldType: 'SINGLE_HEADER', fieldKeys: ['Authorization', 'Cookie'] } },
{ action: 'SUBSTITUTION', field: { fieldType: 'SINGLE_COOKIE', fieldKeys: ['access_token', 'refresh_token', ...] } },
{ action: 'SUBSTITUTION', field: { fieldType: 'QUERY_STRING' }, excludeRuleMatchDetails: true },
{ action: 'SUBSTITUTION', field: { fieldType: 'BODY' }, excludeRuleMatchDetails: true },
],
}
  • Non-prod: Logs everything (including Bot Control count-mode matches)
  • Prod: Drops plain ALLOW actions to reduce volume; keeps BLOCK, COUNT, CHALLENGE

Configure in next.config.ts:

async headers() {
return [{
source: '/(.*)',
headers: [
{ key: 'X-DNS-Prefetch-Control', value: 'on' },
{ key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Referrer-Policy', value: 'origin-when-cross-origin' },
{ key: 'No-Vary-Search', value: 'params=("utm_campaign" "utm_content" ...)' },
],
}];
}
HeaderValuePurpose
Strict-Transport-Securitymax-age=63072000; includeSubDomains; preloadForces HTTPS for 2 years
X-Content-Type-OptionsnosniffPrevents MIME-type sniffing
Referrer-Policyorigin-when-cross-originLimits referrer info to cross-origin
X-DNS-Prefetch-ControlonEnables DNS prefetching for performance
No-Vary-SearchUTM params listTells CDN that UTM params don’t change content

The No-Vary-Search header is a performance optimization. It tells CloudFront that marketing parameters (UTM, click IDs) don’t affect the response content, so the same cached response can serve all URL variants:

params=("utm_campaign" "utm_content" "utm_medium" "utm_source" "utm_id" "utm_term" "brid" "fbclid" "gclid" "wbraid" "gbraid" "gclsrc" "gad_campaign" "gad_campaignid" "gad_source" "msclkid" "epik" "pp")

Stored in AWS Secrets Manager, accessed at runtime:

  • SANITY_API_READ_TOKEN — Sanity read token (for draft mode)
  • KONG_API_TOKEN_SECRET_ARN — API gateway credentials (clientId, clientSecret)

Injected into ECS as secrets (not environment variables):

ecsSecrets: {
SANITY_API_READ_TOKEN: ecs.Secret.fromSecretsManager(params.resolved.sanitySecret, 'apiReadToken'),
},

Fetched via scripts/fetch-secrets.sh and stored in .env.<stage> files (gitignored):

  • Sanity deploy token
  • Sanity dataset token
  • CORS token
  • Editorial token
  • SSO login URL
  • Security headers configured in next.config.ts
  • HSTS with includeSubDomains; preload
  • X-Content-Type-Options: nosniff
  • Secrets stored in Secrets Manager (not SSM or env vars)
  • Secrets injected as ECS secrets (not plain environment variables)
  • No sensitive data in client-side code (NEXT_PUBLIC_* vars are public)
  • Draft mode protected by @sanity/preview-url-secret validation
  • WAF protection via gateway (automatic)
  • No-Vary-Search for marketing parameters (CDN cache efficiency)