79 · Production Security Hardening

Configure CSP/HSTS headers in AppRouter, enable CSRF, add rate limiting, and scan for committed secrets.

Episode 79 — Production Security Hardening

What you'll learn

Why this matters

A functionally complete app is not automatically a secure one. Production systems on BTP are internet-accessible — malicious actors scan for misconfigured endpoints, weak tokens, and missing headers within hours of a deployment. Running through the security checklist before go-live is cheaper than responding to an incident.

Security control inventory

Authentication & Authorization

Transport

API hardening

Secret management

Configuring CSP headers in AppRouter

// approuter/xs-app.json
{
  "compression": { "enabled": true },
  "responseHeaders": [
    {
      "name": "Content-Security-Policy",
      "value": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://*.hana.ondemand.com; frame-ancestors 'none'"
    },
    {
      "name": "X-Frame-Options",
      "value": "DENY"
    },
    {
      "name": "X-Content-Type-Options",
      "value": "nosniff"
    },
    {
      "name": "Strict-Transport-Security",
      "value": "max-age=31536000; includeSubDomains"
    }
  ]
}

Enabling CSRF protection

// approuter/xs-app.json route
{
  "source": "^/api/(.*)$",
  "target": "/api/$1",
  "destination": "cap-backend",
  "csrfProtection": true,    // AppRouter will require X-CSRF-Token header on mutations
  "authenticationType": "xsuaa"
}

The React frontend fetches the CSRF token:

// Fetch the CSRF token before any POST/PUT/DELETE
const tokenRes = await fetch('/api/xsrf-token', {
  headers: { 'X-CSRF-Token': 'Fetch' }
});
const csrfToken = tokenRes.headers.get('X-CSRF-Token');

// Use it in subsequent mutations
await fetch('/api/odata/v4/OrderService/Orders', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': csrfToken
  },
  body: JSON.stringify({ ... })
});

Rate limiting with express-rate-limit

npm install express-rate-limit
// srv/server.js
const rateLimit = require('express-rate-limit');
const cds       = require('@sap/cds');

cds.on('bootstrap', app => {
  // Strict limit on auth-adjacent endpoints
  const authLimiter = rateLimit({
    windowMs: 15 * 60 * 1000,  // 15 minutes
    max: 20,
    message: 'Too many requests from this IP, please try again later'
  });

  app.use('/api/auth', authLimiter);
  app.use('/api/upload', authLimiter);

  // General API limit
  const generalLimiter = rateLimit({
    windowMs: 1 * 60 * 1000,   // 1 minute
    max: 200
  });

  app.use('/api', generalLimiter);
});

Scanning for committed secrets

# Install truffleHog
pip install truffleHog3

# Scan the git history for secrets
trufflehog3 --regex --entropy=False .

# Or use gitleaks
brew install gitleaks
gitleaks detect --source . -v

If a secret is found in git history:
1. Rotate the credential immediately — assume it is compromised
2. Remove it from git history (git filter-repo --path-glob '*.env' --invert-paths)
3. Force-push the cleaned history (coordinate with the team)
4. Audit CF environment variables and service keys for the same credential

Disabling mock auth in production

// package.json — production profile overrides
{
  "cds": {
    "requires": {
      "[production]": {
        "auth": { "kind": "xsuaa" }
      },
      "[development]": {
        "auth": {
          "kind": "mocked",
          "users": { ... }
        }
      }
    }
  }
}

CAP activates the [production] profile when NODE_ENV=production (CF sets this automatically).

Common mistakes

Mistake Fix
Leaving "kind":"mocked" in the top-level requires (not profile-scoped) Scope mock auth to [development] only
No CSRF on AppRouter routes Set "csrfProtection": true on all mutation routes
frame-ancestors * in CSP Use 'none' to prevent clickjacking
Service keys in package.json Use CF environment variables or cds bind
No rate limiting on upload endpoints A single client can exhaust Object Store bandwidth

Checkpoint ✓

You have worked through the security control inventory, configured CSP/HSTS/X-Frame-Options headers in AppRouter, enabled CSRF protection, added rate limiting on sensitive endpoints, and know how to scan for committed secrets and rotate exposed credentials.

Sign in for progress tracking & Q&A →