Skip to content

[TESTING][SECURITY]: Security headers manual test plan (CSP, HSTS, CORS, clickjacking) #2396

@crivetimihai

Description

@crivetimihai

[TESTING][SECURITY]: Security headers manual test plan (CSP, HSTS, CORS, clickjacking)

Goal

Produce a comprehensive manual test plan for validating all security headers are correctly set and protect against common web attacks including XSS, clickjacking, MIME sniffing, and information disclosure.

Why Now?

Security testing is critical for GA release:

  1. Production Readiness: Security must be validated before release
  2. Compliance: Required by security standards and audits
  3. Defense in Depth: Validates multiple protection layers
  4. Attack Mitigation: Prevents common exploitation techniques
  5. User Trust: Security issues erode confidence

User Stories

Story 1: Content Security Policy Protection

As a security administrator
I want Content Security Policy headers to be enforced
So that XSS attacks are mitigated through browser-level controls

Acceptance Criteria

Feature: Content Security Policy headers
  Background:
    Given the gateway is running with SECURITY_HEADERS_ENABLED=true
    And the admin UI is enabled

  Scenario: CSP header is present on all responses
    When I make a request to any endpoint
    Then the response should include "Content-Security-Policy" header
    And the CSP should include "default-src 'self'"

  Scenario: CSP allows configured CDN sources for scripts
    When I access the admin UI
    Then the CSP script-src should include CDN allowlist
    And inline scripts should be blocked unless nonce-protected

  Scenario: CSP allows WebSocket connections
    When I check the CSP connect-src directive
    Then it should include "wss:" for WebSocket support
    And it should include "ws:" for development environments

  Scenario: CSP violation is reported
    Given CSP reporting is configured
    When a CSP violation occurs
    Then the violation should be logged
    And the violating resource should be blocked

Story 2: HSTS Protection

As a security administrator
I want HSTS headers to enforce HTTPS connections
So that downgrade attacks and cookie hijacking are prevented

Acceptance Criteria

Feature: HTTP Strict Transport Security
  Background:
    Given the gateway is running with HTTPS enabled
    And HSTS_ENABLED=true

  Scenario: HSTS header is present on HTTPS responses
    When I make an HTTPS request to the gateway
    Then the response should include "Strict-Transport-Security" header
    And the max-age should be at least 31536000 (1 year)

  Scenario: HSTS includes subdomains when configured
    Given HSTS_INCLUDE_SUBDOMAINS=true
    When I make an HTTPS request
    Then the HSTS header should include "includeSubDomains"

  Scenario: HSTS preload directive when configured
    Given HSTS_PRELOAD=true
    When I make an HTTPS request
    Then the HSTS header should include "preload"

  Scenario: HSTS is not set on HTTP responses
    When I make an HTTP request (non-HTTPS)
    Then the response should NOT include "Strict-Transport-Security" header

Story 3: Clickjacking Protection

As a security administrator
I want X-Frame-Options headers to prevent clickjacking
So that the application cannot be embedded in malicious frames

Acceptance Criteria

Feature: Clickjacking protection headers
  Background:
    Given the gateway is running with SECURITY_HEADERS_ENABLED=true

  Scenario: X-Frame-Options DENY is set by default
    Given X_FRAME_OPTIONS is not configured
    When I make a request to the gateway
    Then the response should include "X-Frame-Options: DENY"

  Scenario: X-Frame-Options SAMEORIGIN when configured
    Given X_FRAME_OPTIONS=SAMEORIGIN
    When I make a request to the gateway
    Then the response should include "X-Frame-Options: SAMEORIGIN"

  Scenario: CSP frame-ancestors aligns with X-Frame-Options
    When I make a request to the gateway
    Then the CSP should include "frame-ancestors 'none'" for DENY
    Or the CSP should include "frame-ancestors 'self'" for SAMEORIGIN

Story 4: CORS Configuration

As a developer integrating with the gateway
I want CORS headers to be properly configured
So that cross-origin requests work securely

Acceptance Criteria

Feature: CORS header configuration
  Background:
    Given the gateway is running with CORS enabled

  Scenario: Preflight OPTIONS request is handled
    When I send an OPTIONS request with Origin header
    Then the response should include "Access-Control-Allow-Origin"
    And the response should include "Access-Control-Allow-Methods"
    And the response should include "Access-Control-Allow-Headers"
    And the response status should be 200 or 204

  Scenario: Credentials are allowed when configured
    Given CORS_ALLOW_CREDENTIALS=true
    When I send a request with credentials
    Then the response should include "Access-Control-Allow-Credentials: true"
    And Access-Control-Allow-Origin should NOT be "*"

  Scenario: Origin validation for non-wildcard CORS
    Given CORS is configured with specific origins
    When I send a request from an allowed origin
    Then the request should succeed with proper CORS headers

    When I send a request from a disallowed origin
    Then the CORS headers should not include that origin

  Scenario: Exposed headers are configurable
    Given specific headers need to be exposed
    When I configure Access-Control-Expose-Headers
    Then those headers should be accessible to the client

Story 5: Additional Security Headers

As a security administrator
I want all recommended security headers to be set
So that the application is protected against various attacks

Acceptance Criteria

Feature: Additional security headers
  Background:
    Given the gateway is running with SECURITY_HEADERS_ENABLED=true

  Scenario: X-Content-Type-Options prevents MIME sniffing
    When I make a request to the gateway
    Then the response should include "X-Content-Type-Options: nosniff"

  Scenario: Referrer-Policy limits referrer information
    When I make a request to the gateway
    Then the response should include "Referrer-Policy"
    And the value should be "strict-origin-when-cross-origin" or more restrictive

  Scenario: X-Download-Options protects IE users
    When I make a request to the gateway
    Then the response should include "X-Download-Options: noopen"

  Scenario: Server header is removed for information disclosure prevention
    When I make a request to the gateway
    Then the response should NOT include identifying server information
    Or the Server header should be generic (e.g., just "uvicorn")

  Scenario: X-XSS-Protection is set to 0 (rely on CSP)
    When I make a request to the gateway
    Then the response should include "X-XSS-Protection: 0"
    And CSP should be the primary XSS protection mechanism

Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                        Client Browser                                │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │ Security Header Enforcement                                  │    │
│  │  • CSP: Block inline scripts, restrict sources              │    │
│  │  • HSTS: Force HTTPS, prevent downgrades                    │    │
│  │  • X-Frame-Options: Block framing attempts                  │    │
│  │  • CORS: Enforce same-origin policy exceptions              │    │
│  └─────────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────┐
│                        MCP Gateway                                   │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │ Security Headers Middleware                                  │    │
│  │  security_headers.py                                        │    │
│  │                                                             │    │
│  │  Request Flow:                                              │    │
│  │  1. Request arrives                                         │    │
│  │  2. Process request normally                                │    │
│  │  3. Add security headers to response                        │    │
│  │  4. Return response with headers                            │    │
│  │                                                             │    │
│  │  Headers Added:                                             │    │
│  │  ├── Content-Security-Policy                                │    │
│  │  ├── Strict-Transport-Security (HTTPS only)                 │    │
│  │  ├── X-Frame-Options                                        │    │
│  │  ├── X-Content-Type-Options                                 │    │
│  │  ├── X-XSS-Protection                                       │    │
│  │  ├── X-Download-Options                                     │    │
│  │  ├── Referrer-Policy                                        │    │
│  │  └── Server (removed/sanitized)                             │    │
│  └─────────────────────────────────────────────────────────────┘    │
│                                                                      │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │ CORS Middleware (FastAPI)                                    │    │
│  │  • Origin validation                                        │    │
│  │  • Preflight handling                                       │    │
│  │  • Credential support                                       │    │
│  │  • Exposed headers                                          │    │
│  └─────────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────────┘

Test Environment Setup

Environment Variables

# Security headers configuration
export SECURITY_HEADERS_ENABLED=true

# HSTS settings
export HSTS_ENABLED=true
export HSTS_MAX_AGE=31536000
export HSTS_INCLUDE_SUBDOMAINS=true
export HSTS_PRELOAD=false

# Frame options
export X_FRAME_OPTIONS=DENY

# CORS settings
export ALLOWED_ORIGINS="https://example.com,https://app.example.com"
export CORS_ALLOW_CREDENTIALS=true
export CORS_ALLOW_METHODS="GET,POST,PUT,DELETE,OPTIONS"
export CORS_ALLOW_HEADERS="Authorization,Content-Type,X-API-Key"
export CORS_EXPOSE_HEADERS="X-Request-ID,X-RateLimit-Remaining"

# CSP settings (if configurable)
export CSP_REPORT_URI="/csp-report"

Test Setup Script

#!/bin/bash
# setup-security-headers-test.sh

# Start gateway with security headers enabled
export SECURITY_HEADERS_ENABLED=true
export HSTS_ENABLED=true
make dev &
sleep 5

# Verify gateway is running
curl -s http://localhost:4444/health | jq .

Test Cases

TC-SH-001: CSP Header Present

Objective: Verify Content-Security-Policy header is present on all responses

Prerequisites:

  • Gateway running with SECURITY_HEADERS_ENABLED=true

Steps:

# Step 1: Check CSP on API endpoint
curl -s -I http://localhost:4444/health | grep -i "content-security-policy"

# Step 2: Check CSP on admin UI
curl -s -I http://localhost:4444/ui/ | grep -i "content-security-policy"

# Step 3: Check CSP on static assets
curl -s -I http://localhost:4444/static/js/app.js | grep -i "content-security-policy"

Expected Results:

  • All responses include Content-Security-Policy header
  • CSP includes default-src 'self'
  • CSP includes appropriate script-src and style-src directives

TC-SH-002: CSP Script Source Restrictions

Objective: Verify CSP restricts script sources appropriately

Steps:

# Step 1: Extract CSP header
CSP=$(curl -s -I http://localhost:4444/ui/ | grep -i "content-security-policy" | cut -d: -f2-)
echo "CSP: $CSP"

# Step 2: Verify script-src directive
echo "$CSP" | grep -o "script-src[^;]*"

# Step 3: Test inline script blocking (browser test)
# Create HTML file that tries to execute inline script
cat > /tmp/csp-test.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Security-Policy" content="script-src 'self'">
</head>
<body>
    <script>alert('This should be blocked')</script>
</body>
</html>
EOF

Expected Results:

  • script-src restricts to 'self' and allowed CDNs
  • Inline scripts are blocked unless using nonce
  • Browser console shows CSP violation for blocked scripts

TC-SH-003: HSTS Header on HTTPS

Objective: Verify HSTS header is set correctly on HTTPS responses

Prerequisites:

  • Gateway running with HTTPS enabled
  • HSTS_ENABLED=true

Steps:

# Step 1: Make HTTPS request and check HSTS
curl -s -I --insecure https://localhost:4444/health | grep -i "strict-transport-security"

# Step 2: Verify max-age value
HSTS=$(curl -s -I --insecure https://localhost:4444/health | grep -i "strict-transport-security")
echo "$HSTS"
# Should show max-age=31536000 or configured value

# Step 3: Check for includeSubDomains
echo "$HSTS" | grep -i "includeSubDomains"

# Step 4: Verify HSTS NOT present on HTTP
curl -s -I http://localhost:4444/health | grep -i "strict-transport-security"
# Should return nothing

Expected Results:

  • HTTPS responses include Strict-Transport-Security header
  • max-age is at least 31536000 (1 year)
  • includeSubDomains present if configured
  • HTTP responses do NOT include HSTS header

TC-SH-004: X-Frame-Options Header

Objective: Verify clickjacking protection via X-Frame-Options

Steps:

# Step 1: Check X-Frame-Options header
curl -s -I http://localhost:4444/ui/ | grep -i "x-frame-options"

# Step 2: Test with DENY setting
export X_FRAME_OPTIONS=DENY
# Restart gateway
curl -s -I http://localhost:4444/ui/ | grep -i "x-frame-options"
# Should show: X-Frame-Options: DENY

# Step 3: Test with SAMEORIGIN setting
export X_FRAME_OPTIONS=SAMEORIGIN
# Restart gateway
curl -s -I http://localhost:4444/ui/ | grep -i "x-frame-options"
# Should show: X-Frame-Options: SAMEORIGIN

# Step 4: Verify CSP frame-ancestors alignment
curl -s -I http://localhost:4444/ui/ | grep -i "content-security-policy" | grep -o "frame-ancestors[^;]*"

Expected Results:

  • X-Frame-Options header is present
  • Value matches configuration (DENY or SAMEORIGIN)
  • CSP frame-ancestors aligns with X-Frame-Options

TC-SH-005: CORS Preflight Handling

Objective: Verify CORS preflight (OPTIONS) requests are handled correctly

Steps:

# Step 1: Send preflight request
curl -s -I -X OPTIONS http://localhost:4444/api/tools \
  -H "Origin: https://example.com" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: Authorization,Content-Type"

# Step 2: Verify CORS headers in response
# Should include:
# Access-Control-Allow-Origin: https://example.com (or *)
# Access-Control-Allow-Methods: POST (or list including POST)
# Access-Control-Allow-Headers: Authorization, Content-Type

# Step 3: Check response status
# Should be 200 or 204

Expected Results:

  • OPTIONS request returns 200 or 204
  • Access-Control-Allow-Origin is present
  • Access-Control-Allow-Methods includes requested method
  • Access-Control-Allow-Headers includes requested headers

TC-SH-006: CORS Origin Validation

Objective: Verify CORS validates origins correctly

Prerequisites:

  • CORS configured with specific origins (not wildcard)

Steps:

# Step 1: Request from allowed origin
curl -s -I http://localhost:4444/api/tools \
  -H "Origin: https://example.com" | grep -i "access-control"

# Step 2: Request from disallowed origin
curl -s -I http://localhost:4444/api/tools \
  -H "Origin: https://evil.com" | grep -i "access-control"

# Step 3: Request with credentials from allowed origin
curl -s -I http://localhost:4444/api/tools \
  -H "Origin: https://example.com" \
  -H "Cookie: session=abc123" | grep -i "access-control"

Expected Results:

  • Allowed origins receive Access-Control-Allow-Origin header
  • Disallowed origins do NOT receive CORS headers
  • Credentials work only with specific origin (not wildcard)

TC-SH-007: CORS Credentials Support

Objective: Verify CORS credentials are handled securely

Prerequisites:

  • CORS_ALLOW_CREDENTIALS=true
  • CORS configured with specific origins

Steps:

# Step 1: Check credentials header
curl -s -I http://localhost:4444/api/tools \
  -H "Origin: https://example.com" | grep -i "access-control-allow-credentials"

# Step 2: Verify origin is NOT wildcard when credentials allowed
ORIGIN=$(curl -s -I http://localhost:4444/api/tools \
  -H "Origin: https://example.com" | grep -i "access-control-allow-origin" | cut -d: -f2-)
echo "Origin: $ORIGIN"
# Should NOT be * when credentials are allowed

# Step 3: Test without Origin header
curl -s -I http://localhost:4444/api/tools | grep -i "access-control"

Expected Results:

  • Access-Control-Allow-Credentials: true is present
  • Access-Control-Allow-Origin is specific origin, not wildcard
  • Requests without Origin header don't get CORS headers

TC-SH-008: X-Content-Type-Options Header

Objective: Verify MIME sniffing prevention

Steps:

# Step 1: Check header on various content types
curl -s -I http://localhost:4444/health | grep -i "x-content-type-options"
curl -s -I http://localhost:4444/ui/ | grep -i "x-content-type-options"
curl -s -I http://localhost:4444/static/styles.css | grep -i "x-content-type-options"

# All should return: X-Content-Type-Options: nosniff

Expected Results:

  • All responses include X-Content-Type-Options: nosniff
  • Content-Type header is accurate for each response

TC-SH-009: Referrer-Policy Header

Objective: Verify referrer information is controlled

Steps:

# Step 1: Check Referrer-Policy header
curl -s -I http://localhost:4444/ui/ | grep -i "referrer-policy"

# Step 2: Verify acceptable value
# Acceptable values (from most to least restrictive):
# - no-referrer
# - same-origin
# - strict-origin
# - strict-origin-when-cross-origin

Expected Results:

  • Referrer-Policy header is present
  • Value is "strict-origin-when-cross-origin" or more restrictive

TC-SH-010: Server Header Information Disclosure

Objective: Verify server header doesn't leak sensitive information

Steps:

# Step 1: Check Server header
curl -s -I http://localhost:4444/health | grep -i "^server:"

# Step 2: Verify no version information
# Should NOT show detailed version like "uvicorn 0.23.2" or "Python/3.11"
# Acceptable: "uvicorn" or no Server header at all

# Step 3: Check for other identifying headers
curl -s -I http://localhost:4444/health | grep -iE "^(x-powered-by|x-aspnet|x-runtime):"
# Should return nothing

Expected Results:

  • Server header is absent or contains minimal information
  • No X-Powered-By or similar identifying headers
  • No version numbers exposed

TC-SH-011: Security Headers Disabled

Objective: Verify behavior when security headers are disabled

Prerequisites:

  • SECURITY_HEADERS_ENABLED=false

Steps:

# Step 1: Restart gateway with headers disabled
export SECURITY_HEADERS_ENABLED=false
# Restart gateway

# Step 2: Check for security headers
curl -s -I http://localhost:4444/health | grep -iE "(content-security-policy|x-frame-options|x-content-type-options)"

# Step 3: Verify CORS still works (separate middleware)
curl -s -I -X OPTIONS http://localhost:4444/api/tools \
  -H "Origin: https://example.com" | grep -i "access-control"

Expected Results:

  • Security headers are NOT present when disabled
  • CORS headers still work (separate configuration)
  • Application still functions normally

TC-SH-012: Vary Header for Caching

Objective: Verify Vary header prevents cache poisoning

Steps:

# Step 1: Check Vary header on CORS responses
curl -s -I http://localhost:4444/api/tools \
  -H "Origin: https://example.com" | grep -i "^vary:"

# Should include Origin in Vary header

# Step 2: Verify caching behavior difference
curl -s -I http://localhost:4444/api/tools \
  -H "Origin: https://example.com" | grep -i "access-control-allow-origin"

curl -s -I http://localhost:4444/api/tools \
  -H "Origin: https://other.com" | grep -i "access-control-allow-origin"

Expected Results:

  • Vary header includes "Origin" when CORS is active
  • Different origins get different CORS responses
  • Cache cannot serve wrong origin's CORS headers

TC-SH-013: MCP Endpoint Security Headers

Objective: Verify security headers on MCP-specific endpoints

Steps:

# Step 1: Check headers on SSE endpoint
curl -s -I http://localhost:4444/mcp/sse | head -20

# Step 2: Check headers on WebSocket upgrade
curl -s -I http://localhost:4444/mcp/ws \
  -H "Upgrade: websocket" \
  -H "Connection: Upgrade" | head -20

# Step 3: Check headers on streamable HTTP
curl -s -I http://localhost:4444/mcp/http | head -20

Expected Results:

  • SSE endpoint has appropriate security headers
  • WebSocket endpoint handles upgrade correctly
  • All MCP endpoints have consistent security header policy

TC-SH-014: CSP for WebSocket Connections

Objective: Verify CSP allows WebSocket connections

Steps:

# Step 1: Extract connect-src directive
CSP=$(curl -s -I http://localhost:4444/ui/ | grep -i "content-security-policy")
echo "$CSP" | grep -o "connect-src[^;]*"

# Step 2: Verify wss: and ws: are allowed
echo "$CSP" | grep -E "(wss:|ws:)"

# Step 3: Browser test - attempt WebSocket connection
# This requires browser testing to verify CSP doesn't block WebSocket

Expected Results:

  • connect-src includes wss: for secure WebSocket
  • connect-src includes ws: for development
  • WebSocket connections succeed from UI

Test Matrix

Test Case CSP HSTS X-Frame CORS X-Content-Type Referrer Server
TC-SH-001
TC-SH-002
TC-SH-003
TC-SH-004
TC-SH-005
TC-SH-006
TC-SH-007
TC-SH-008
TC-SH-009
TC-SH-010
TC-SH-011
TC-SH-012
TC-SH-013
TC-SH-014

Browser Testing Checklist

Some security header behaviors can only be verified in a browser:

  • CSP blocks inline scripts (check console for violations)
  • CSP blocks scripts from unauthorized sources
  • HSTS redirects HTTP to HTTPS after first visit
  • X-Frame-Options prevents embedding in iframe
  • CORS preflight succeeds for cross-origin requests
  • WebSocket connections work with CSP

Security Header Reference

Header Purpose Recommended Value
Content-Security-Policy XSS prevention default-src 'self'; script-src 'self' [cdns]; ...
Strict-Transport-Security Force HTTPS max-age=31536000; includeSubDomains
X-Frame-Options Clickjacking DENY or SAMEORIGIN
X-Content-Type-Options MIME sniffing nosniff
X-XSS-Protection Legacy XSS filter 0 (rely on CSP)
X-Download-Options IE file safety noopen
Referrer-Policy Referrer control strict-origin-when-cross-origin
Access-Control-Allow-Origin CORS Specific origins (not * with credentials)

Success Criteria

  • All 14 test cases pass
  • CSP header present and correctly configured
  • HSTS header present on HTTPS responses only
  • X-Frame-Options prevents clickjacking
  • CORS correctly validates origins
  • No sensitive information in Server header
  • Browser testing confirms headers are enforced

Related Files

  • mcpgateway/middleware/security_headers.py - Security headers middleware
  • mcpgateway/config.py - Security header configuration
  • mcpgateway/main.py - CORS middleware configuration

Related Issues

Metadata

Metadata

Assignees

Labels

MUSTP1: Non-negotiable, critical requirements without which the product is non-functional or unsafedocumentationImprovements or additions to documentationmanual-testingManual testing / test planning issuessecurityImproves securitytestingTesting (unit, e2e, manual, automated, etc)

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions