Skip to content

CSRF Protection Implementation

This document describes the Cross-Site Request Forgery (CSRF) protection implementation for the ERP-Unlocked web application.

Following the security recommendations from GitHub Issue #161, we have implemented CSRF protection for admin API routes since Astro v5’s built-in CSRF protection does not apply to API routes.

1. CSRF Validation Utility (apps/webapp/src/utils/csrf.ts)

Section titled “1. CSRF Validation Utility (apps/webapp/src/utils/csrf.ts)”

The core CSRF protection is implemented in the csrf.ts utility which provides:

  • Origin Header Validation: Ensures requests come from expected origins
  • Referer Header Fallback: Falls back to referer validation if origin is missing
  • Custom Header Requirement: Requires X-Requested-With: XMLHttpRequest header for admin endpoints
  • Extensible Configuration: Allows custom validation options per endpoint

The BaseAPIHandler class has been enhanced with CSRF protection:

protected async requireAdmin(locals: any, request?: Request, csrfOptions?: CSRFValidationOptions)

This method now automatically:

  1. Validates CSRF protection for admin endpoints
  2. Checks admin role authorization
  3. Logs security events for monitoring

Frontend requests to admin endpoints must include the required header:

fetch('/api/admin/unblock-ip', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest', // Required for CSRF protection
},
body: JSON.stringify({ ip }),
});
  • Validates Origin header matches expected domain
  • Falls back to Referer header if Origin is missing
  • Supports additional allowed origins via configuration
  • Requires X-Requested-With: XMLHttpRequest header
  • Prevents simple form-based CSRF attacks
  • Can be disabled via configuration if needed
  • Only validates state-changing HTTP methods (POST, PUT, DELETE, PATCH)
  • GET requests bypass CSRF checks (considered safe)
interface CSRFValidationOptions {
adminOnly?: boolean; // Require stricter checks for admin endpoints
allowedOrigins?: string[]; // Additional allowed origins
requireCustomHeader?: boolean; // Require X-Requested-With header
}
export const POST: APIRoute = async ({ request, locals }) => {
const handler = new MyAdminHandler();
return handler.handleRequest(async () => {
// This automatically includes CSRF protection
await this.requireAdmin(locals, request);
// Your admin logic here
});
};
await this.requireAdmin(locals, request, {
allowedOrigins: ['https://trusted-domain.com'],
requireCustomHeader: false,
});
  1. Prevents CSRF Attacks: Blocks malicious sites from triggering admin actions
  2. Origin Validation: Ensures requests come from legitimate sources
  3. Custom Header Protection: Prevents simple form-based attacks
  4. Comprehensive Logging: All validation attempts are logged for monitoring
  5. Configurable: Can be adjusted per endpoint as needed

All CSRF validation events are logged with:

  • Request details (method, origin, referer)
  • Validation results (success/failure)
  • Error details for failed validations
  • Request correlation IDs for debugging

The implementation is designed to be extensible and includes:

  • Token-based CSRF protection utilities (for future use)
  • Configurable validation options
  • Support for additional security headers

To test CSRF protection:

  1. Valid Request: Include X-Requested-With: XMLHttpRequest header
  2. Invalid Origin: Try request from different origin (should fail)
  3. Missing Header: Omit custom header (should fail for admin endpoints)
  4. Invalid Method: Only POST/PUT/DELETE/PATCH are validated
  • apps/webapp/src/utils/csrf.ts - Core CSRF validation logic
  • apps/webapp/src/lib/api/base-handler.ts - API handler integration
  • apps/webapp/src/pages/admin/rate-limiting.astro - Example client implementation
  • apps/webapp/src/pages/api/admin/unblock-ip.ts - Example protected endpoint