- Notifications
You must be signed in to change notification settings - Fork1k
Description
tl;dr follows several starts and minimum done to allow CSRF to work, but needs a few more bits
Summary
Add configurable CSRF protection options to allow organizations to customize CSRF enforcement based on their security requirements and deployment scenarios. This addresses the need for flexible CSRF configuration while maintaining security best practices for enterprise customers who require granular security controls.
Background
Current CSRF Implementation
CSRF enforcement is enabled and applied globally in the server middleware with specific built-in exemptions (e.g., GET requests, CSP reports, agent and SCIM routes, Bearer tokens, matching cookie/header tokens) and validates the X-CSRF-TOKEN header using the nosurf token for non-exempt requests. The frontend sets the X-CSRF-TOKEN header from a CSRF meta tag rendered by the server.
CSRF protection was originally enabled in Coder v2.6.0 viaPR #11283 to protect against cross-site request forgery attacks, particularly important after the introduction of application sharing in v2.9.0.
Related GitHub Issues
Closed Issues:
- #13922 - POST to /api fails with 400 with CSRF error — User experiencing CSRF errors with a custom JS app until X-CSRF-TOKEN is supplied
- #13535 - Swagger CSRF Error — Swagger UI requests fail without X-CSRF-TOKEN; using the cookie value as the header resolves it
Key Discussion Points from Issues:
- CSRF protection prevents XSS-style cross-site actions from user-shared applications; this became especially relevant as application sharing became more prominent
- Current enforcement requires X-CSRF-TOKEN in browser-based POST/PUT/PATCH/DELETE calls when cookie auth is present; Swagger needed special handling to avoid cookie vs header conflicts
- A
--dangerous-disable-csrf
option was discussed but rejected due to security risk; endpoint-specific disable was noted as "difficult to configure"
Current Limitations
- Global enforcement is on by default with code-defined exemptions; operators cannot enable/disable or customize behavior at runtime
- No operator-controlled route-level customization; exemptions are hardcoded (e.g., CSP reports, agents, proxies, SCIM, DERP) and not adjustable via config
- Integration challenges: Integrations using browser-based fetches (with cookies) must supply X-CSRF-TOKEN, which complicates simple JS integrations
- Enterprise compliance: Often requires granular controls and auditability beyond on/off enforcement
Proposed Solution
Configuration Options
Add multiple CSRF configuration modes to provide flexibility while maintaining security:
1. Global CSRF Mode Configuration
CLI Flag:
--csrf-mode=strict|selective|disabled
Environment Variable:
CODER_CSRF_MODE=strict
YAML Configuration:
csrfMode:strict
2. CSRF Mode Definitions
strict
(Default - Current Behavior)
- CSRF protection enabled for non-exempt routes requiring authentication
- Maintains backward compatibility
- Recommended for production environments
selective
- CSRF protection enabled for sensitive operations only
- Supports configurable protected/exempt route patterns
- Suitable for environments with custom integrations
disabled
- CSRF protection completely disabled
- Includes prominent security warnings
- Only for controlled environments with alternative mitigations
3. Route-Specific Configuration (forselective
mode)
Configuration File:
csrfMode:selectivecsrfProtectedRoutes: -"/api/v2/workspaces/*/builds" -"/api/v2/templates/*" -"/api/v2/users/*/password"csrfExemptRoutes: -"/api/v2/csp/reports"
Notes:
- Patterns support wildcards (
*
,**
), leveraging the existing pattern matcher utility
Implementation Approach
Phase 1: Basic Mode Support
- Implement
strict
,selective
, anddisabled
modes - Default to
strict
for backward compatibility - Add validation and startup warnings
Phase 2: Route-Specific Configuration
- Use existing route pattern utilities for
selective
mode to compile protected/exempt regexes - Provide secure defaults (e.g., exempt GET, CSP reports; protect mutations on resources)
- Allow operator overrides via env/CLI/YAML
Phase 3: Enhanced Security Features
- Token scoping (operation- or path-scoped tokens)
- Time-based token expiration controls
- Audit logging for CSRF-related events and configuration changes
Security Considerations
Risk Mitigation
- Clear Warnings: Prominent warnings when CSRF protection is reduced
- Audit Logging: Log CSRF configuration changes and CSRF bypass events
- Default Security: Maintain
strict
as the default - Documentation: Guidance on secure configurations and trade-offs
Security Benefits
- Flexible Compliance: Configurable modes to meet policy requirements
- Controlled Exposure: Selective mode targets high-risk operations
- Integration Support: Safer patterns for custom apps and API clients
- Monitoring: Better visibility into CSRF-related activity
Use Cases
Enterprise Environments
csrfMode:selectivecsrfProtectedRoutes: -"/api/v2/workspaces/*/builds" -"/api/v2/templates/*" -"/api/v2/users/*"csrfExemptRoutes: -"/api/v2/csp/reports"
Custom Integration Environments
csrfMode:selectivecsrfExemptRoutes: -"/api/v2/organizations/*/members/*/workspaces"# For custom workspace launch flows
Development/Testing Environments
CODER_CSRF_MODE=disabled
Configuration Examples
Helm Chart Configuration
coder:env: -name:CODER_CSRF_MODEvalue:"selective"config:csrfProtectedRoutes: -"/api/v2/workspaces/*/builds" -"/api/v2/templates/*"csrfExemptRoutes: -"/api/v2/csp/reports"
Docker Compose
environment: -CODER_CSRF_MODE=selective -CODER_CSRF_PROTECTED_ROUTES=/api/v2/workspaces/*/builds,/api/v2/templates/* -CODER_CSRF_EXEMPT_ROUTES=/api/v2/csp/reports
Backward Compatibility
- Default behavior (
strict
) remains unchanged - Existing deployments continue to work without configuration changes
- No breaking changes to API responses or authentication flows
- Provides a gradual migration path for organizations needing flexibility
Documentation Requirements
- Security Guide: CSRF modes, trade-offs, integration guidance
- Migration Guide: Transitioning to selective or disabled
- Best Practices: Recommended configurations per deployment scenario
- Troubleshooting: Common errors (e.g., cookie vs header auth conflicts in Swagger/UI) and solutions
Acceptance Criteria
- Implement
strict
,selective
, anddisabled
CSRF modes - Default behavior remains
strict
(current implementation) - Route-specific configuration for
selective
mode - Comprehensive security warnings for reduced protection modes
- Audit logging for CSRF configuration changes/events
- Documentation covering implications and best practices
- Backward compatibility maintained
- Configuration validation and error handling
- Integration tests for all CSRF modes
Priority
High — Addresses enterprise security requirements while maintaining flexibility for various deployment scenarios. Requested by enterprise customers for compliance and security control requirements.
References
- Original CSRF Implementation:PR #11283
- Issue:POST to /api fails with 400 with CSRF error #13922 "POST to /api fails with 400 with CSRF error" (custom JS app; X-CSRF-TOKEN header requirement)
- Issue:Swagger CSRF Error #13535 "Swagger CSRF Error" (Swagger requests need X-CSRF-TOKEN)
- Customer Request: Enterprise customer feedback regarding CSRF configuration flexibility
- Server CSRF middleware and exemptions (uses nosurf, X-CSRF-TOKEN, route exemptions)
- Frontend CSRF meta/header behavior
- Swagger UI cookie/header handling to avoid CSRF confusion
- Route pattern matching utility for configurable routes
Additional Considerations
Alternative Approaches Considered
- Complete CSRF Disable (global): Too broad and high risk
- Header-Based Bypass Only: Insufficient operator control
- Cookie-Based Configuration: Complex UX and brittle
Future Enhancements
- Dynamic Configuration: Runtime changes without restart
- Role-/User-Specific CSRF Rules
- SIEM Integration: Forward CSRF events to external systems
- Advanced Token Management: Token scope and lifetimes