Skip to content

[TESTING][SECURITY]: CSRF protection manual test plan (tokens, SameSite cookies, origin validation) #2409

@crivetimihai

Description

@crivetimihai

[TESTING][SECURITY]: CSRF protection manual test plan (tokens, SameSite cookies, origin validation)

Goal

Produce a comprehensive manual test plan for validating Cross-Site Request Forgery (CSRF) attacks are prevented through SameSite cookie attributes, secure cookie configuration, and token-based authentication design.

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

Scope Clarification

What Exists

Feature Status Location
SameSite cookie attribute ✅ Implemented config.py:484 - defaults to "lax"
HttpOnly cookie attribute ✅ Implemented utils/security_cookies.py:66
Secure cookie attribute ✅ Implemented utils/security_cookies.py:67 (production/config)
JWT Bearer token auth ✅ Primary API auth Header-based, not cookie-based
Cookie-based admin UI auth ✅ Implemented utils/security_cookies.py:62-70

What Does NOT Exist

Feature Status
Explicit CSRF token middleware ❌ Not implemented
/api/csrf-token endpoint ❌ Does not exist
X-CSRF-Token header validation ❌ Not implemented
Double-submit cookie pattern ❌ Not implemented
Origin/Referer header validation ❌ Not implemented

CSRF Protection Strategy

The gateway uses a token-based authentication design that provides inherent CSRF protection:

  1. JSON API: Uses Authorization: Bearer <token> header - not vulnerable to CSRF as browsers don't auto-attach custom headers
  2. Admin UI: Uses jwt_token cookie with SameSite=lax attribute - browser prevents cookie from being sent on cross-origin POST requests

User Stories

Story 1: SameSite Cookie Protection

As a security administrator
I want session cookies to use SameSite attribute
So that cookies are not sent with cross-origin POST requests

Acceptance Criteria

Feature: SameSite cookie protection
  Scenario: Admin UI cookie has SameSite=Lax
    When a user logs into the admin UI
    Then the jwt_token cookie should include SameSite=lax

  Scenario: Cross-origin POST doesn't include cookies
    When a cross-origin POST request is made
    Then session cookies should not be sent (browser enforcement)

Story 2: Secure Cookie Attributes

As a security administrator
I want cookies to have proper security attributes
So that cookie theft and injection attacks are prevented

Acceptance Criteria

Feature: Secure cookie attributes
  Scenario: HttpOnly prevents JavaScript access
    When the jwt_token cookie is set
    Then it should include HttpOnly attribute

  Scenario: Secure flag on HTTPS
    When running in production mode
    Then cookies should include Secure attribute

Story 3: Bearer Token API Protection

As a security administrator
I want API endpoints to use Bearer token authentication
So that CSRF attacks cannot forge API requests

Acceptance Criteria

Feature: Bearer token protection
  Scenario: API rejects cookie-only auth
    When a request is made with only cookies (no Authorization header)
    Then the API should reject with 401 Unauthorized

  Scenario: API accepts Bearer token
    When a request includes Authorization: Bearer <token>
    Then the request should be processed

Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                     CSRF Protection Strategy                         │
│                                                                      │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │ JSON API Protection (Primary)                               │    │
│  │                                                             │    │
│  │  • Uses Authorization: Bearer <token> header               │    │
│  │  • Browsers don't auto-attach custom headers               │    │
│  │  • Cross-origin requests cannot include auth               │    │
│  │  • CSRF not applicable for header-based auth               │    │
│  └─────────────────────────────────────────────────────────────┘    │
│                                                                      │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │ Admin UI Protection (Cookie-based)                          │    │
│  │                                                             │    │
│  │  Cookie: jwt_token                                         │    │
│  │    ├── SameSite=lax (blocks cross-origin POST)            │    │
│  │    ├── HttpOnly (prevents JS access)                      │    │
│  │    ├── Secure (HTTPS only in production)                  │    │
│  │    └── Path=/ (scoped to app)                             │    │
│  │                                                             │    │
│  │  HTMX requests read cookie and send as header             │    │
│  └─────────────────────────────────────────────────────────────┘    │
│                                                                      │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │ NOT Implemented (Out of Scope)                              │    │
│  │                                                             │    │
│  │  ✗ CSRF token generation/validation                        │    │
│  │  ✗ Origin/Referer header checking                         │    │
│  │  ✗ Double-submit cookie pattern                           │    │
│  └─────────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────────┘

Test Environment Setup

# Start the docker compose stack
docker compose up -d

# Verify health
curl http://localhost:8080/health

# Register a test user
curl -X POST -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","password":"TestPassword123!"}' \
  http://localhost:8080/auth/email/register

# Extract token from response
export TOKEN="<token_from_response>"

Test Cases

TC-CSRF-001: SameSite Cookie Attribute

Objective: Verify cookies have SameSite attribute

Steps:

# Step 1: Login via admin UI and check Set-Cookie header
curl -s -D - -X POST \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "[email protected]&password=YourPassword123!" \
  http://localhost:8080/admin/login 2>&1 | grep -i "set-cookie"

# Look for: SameSite=Lax (or Strict)

Expected Results:

  • Cookie includes SameSite=lax or SameSite=strict
  • Default configuration is "lax" (see config.py:484)

TC-CSRF-002: HttpOnly Cookie Attribute

Objective: Verify cookies have HttpOnly attribute

Steps:

# Step 1: Check cookie attributes after login
curl -s -D - -X POST \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "[email protected]&password=YourPassword123!" \
  http://localhost:8080/admin/login 2>&1 | grep -i "set-cookie"

# Look for: HttpOnly attribute

Expected Results:

  • jwt_token cookie includes HttpOnly
  • JavaScript cannot access the cookie

TC-CSRF-003: Secure Cookie Attribute (HTTPS)

Objective: Verify cookies have Secure attribute on HTTPS

Steps:

# Step 1: Check environment setting
# In production: ENVIRONMENT=production or SECURE_COOKIES=true

# Step 2: Login over HTTPS and verify
curl -s -D - -k -X POST \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "[email protected]&password=YourPassword123!" \
  https://localhost:8443/admin/login 2>&1 | grep -i "set-cookie"

# Look for: Secure attribute

Expected Results:

  • HTTPS cookies include Secure attribute in production
  • Cookies not sent over HTTP in production mode

TC-CSRF-004: API Bearer Token Requirement

Objective: Verify JSON API requires Bearer token, not cookies

Steps:

# Step 1: Try API with only cookies (should fail)
curl -s -w "\nStatus: %{http_code}\n" \
  -b "jwt_token=some-token" \
  http://localhost:8080/gateways

# Step 2: Try API with Bearer token (should succeed)
curl -s -w "\nStatus: %{http_code}\n" \
  -H "Authorization: Bearer $TOKEN" \
  http://localhost:8080/gateways

Expected Results:

  • Cookie-only request: 401 Unauthorized
  • Bearer token request: 200 OK with data

TC-CSRF-005: Cross-Origin Request Simulation

Objective: Verify SameSite blocks cross-origin POST with cookies

Steps:

# Step 1: Simulate cross-origin POST (Origin header from different domain)
curl -s -w "\nStatus: %{http_code}\n" \
  -X POST \
  -H "Origin: http://evil.com" \
  -H "Content-Type: application/json" \
  -b "jwt_token=$COOKIE_TOKEN" \
  -d '{"name":"test"}' \
  http://localhost:8080/gateways

# Note: In a real browser, SameSite=lax would prevent the cookie
# from being sent at all on cross-origin POST. This curl test
# simulates what would happen if the cookie was somehow sent.

Expected Results:

  • Request fails or is rejected
  • Browser enforcement prevents cookie sending on cross-origin POST

TC-CSRF-006: Admin UI Form Submission

Objective: Verify admin UI forms work with cookie auth

Steps:

# Step 1: Login to admin UI
curl -s -c /tmp/cookies.txt -X POST \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "[email protected]&password=YourPassword123!" \
  http://localhost:8080/admin/login

# Step 2: Access admin page with cookie
curl -s -b /tmp/cookies.txt \
  http://localhost:8080/admin/ | head -50

Expected Results:

  • Login sets jwt_token cookie
  • Subsequent requests with cookie are authenticated
  • Same-origin requests work correctly

TC-CSRF-007: GET Requests Not State-Changing

Objective: Verify GET requests don't modify state

Steps:

# Step 1: Verify GET endpoints are read-only
curl -s -H "Authorization: Bearer $TOKEN" \
  http://localhost:8080/gateways | jq 'length'

# Step 2: Verify no state change from GET
# (Idempotent - multiple calls return same result)

Expected Results:

  • GET requests are read-only
  • No state modification possible via GET
  • Safe from CSRF by design (safe method)

TC-CSRF-008: Cookie Configuration Check

Objective: Verify cookie security configuration

Steps:

# Step 1: Check default configuration
grep -n "cookie_samesite\|secure_cookies" \
  mcpgateway/config.py

# Expected:
# cookie_samesite: str = Field(default="lax")
# secure_cookies: bool = Field(default=False) # True in production

Expected Results:

  • cookie_samesite defaults to "lax"
  • secure_cookies configurable via environment

Test Matrix

Test Case SameSite HttpOnly Secure Bearer Cookie Auth
TC-CSRF-001
TC-CSRF-002
TC-CSRF-003
TC-CSRF-004
TC-CSRF-005
TC-CSRF-006
TC-CSRF-007
TC-CSRF-008

Success Criteria

  • All 8 test cases pass
  • jwt_token cookie has SameSite=lax attribute
  • jwt_token cookie has HttpOnly attribute
  • jwt_token cookie has Secure attribute in production
  • JSON API requires Bearer token (not cookie-only)
  • Admin UI works with cookie authentication
  • GET requests are safe/idempotent

Related Files

  • mcpgateway/utils/security_cookies.py - Cookie security configuration (lines 62-70)
  • mcpgateway/config.py:484 - cookie_samesite setting (default: "lax")
  • mcpgateway/config.py - secure_cookies setting
  • mcpgateway/admin.py - Admin UI login/logout handlers
  • mcpgateway/templates/admin.html - Admin UI template

What This Test Plan Does NOT Cover

The following CSRF protections are NOT implemented in the gateway:

  • Explicit CSRF tokens - No /csrf-token endpoint or X-CSRF-Token header validation
  • Origin/Referer validation - Server does not validate these headers
  • Double-submit cookie pattern - Not implemented

The gateway relies on:

  1. Bearer token authentication for APIs (inherently CSRF-safe)
  2. SameSite cookies for admin UI (browser-enforced protection)

If explicit CSRF token protection is needed, it would require new implementation.


Related Issues

  • None identified

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