๐Ÿ”ท Core

bx-csrf

Use this skill for CSRF (Cross-Site Request Forgery) protection in BoxLang web apps: CSRFGenerateToken(), CSRFVerifyToken(), CSRFHiddenField(), CSRFRotate(), token scoping with keys, auto-verification, token expiration, and boxlang.json configuration.

$ npx skills add ortus-boxlang/skills/boxlang-modules/bx-csrf
$ coldbox ai skills install ortus-boxlang/skills/boxlang-modules/bx-csrf
๐Ÿ”— https://skills.boxlang.io/skills/raw/ortus-boxlang/skills/boxlang-modules~bx-csrf

bx-csrf: CSRF Protection

BoxLang is the AI-native software productivity platform for building, modernizing and running applications, with developers and AI agents working together.

Installation

install-bx-module bx-csrf
# CommandBox
box install bx-csrf

BIFs

BIFDescription
CSRFGenerateToken( [key], [forceNew] )Generate a CSRF token (optionally scoped to a key)
CSRFVerifyToken( token, [key] )Verify a CSRF token (returns boolean)
CSRFHiddenField( [key], [forceNew] )Generate a ready-to-embed <input type="hidden"> HTML field
CSRFRotate()Rotate/invalidate all current tokens, forcing regeneration

Basic Form Protection

<!-- In your form template -->
<form method="POST" action="/submit">
    <!-- Embed the token as a hidden field -->
    <bx:output>#CSRFHiddenField()#</bx:output>
    <!-- Renders: <input type="hidden" name="csrf" value="...token..."> -->

    <input type="text" name="email" />
    <button type="submit">Submit</button>
</form>
// In your form handler
function processForm() {
    // Verify the submitted token
    if ( !CSRFVerifyToken( form.csrf ) ) {
        throw( type: "Security.CSRFTokenInvalid", message: "Invalid CSRF token" )
    }

    // Safe to process form data
    // ...
}

Scoped Tokens (Per-Form)

Use scoped tokens when you have multiple forms on the same page or need per-action tokens:

// Generate a scoped token (named "deleteUser")
token = CSRFGenerateToken( "deleteUser" )

// Verify the scoped token
if ( !CSRFVerifyToken( form.csrf, "deleteUser" ) ) {
    throw( type: "Security.CSRFTokenInvalid", message: "Invalid delete token" )
}
<!-- Scoped hidden field -->
<bx:output>#CSRFHiddenField( "deleteUser" )#</bx:output>

AJAX / API Requests (Header-Based)

// Client-side: include token in X-CSRF-Token header
// fetch("/api/update", {
//   method: "POST",
//   headers: { "X-CSRF-Token": csrfToken },
//   body: JSON.stringify(data)
// })

// Server-side: auto-verification (configure in boxlang.json)
// Or manually:
if ( !CSRFVerifyToken( getHTTPRequestData().headers[ "X-CSRF-Token" ] ?: "" ) ) {
    throw( type: "Security.CSRFTokenInvalid", message: "Missing or invalid CSRF token" )
}

Token Rotation

// Rotate all tokens (e.g., after login/logout)
CSRFRotate()
// All existing tokens are immediately invalidated
// Next call to CSRFGenerateToken() creates a fresh token

Configuration (boxlang.json)

{
  "modules": {
    "csrf": {
      "settings": {
        "cacheStorage"     : "session",   // Cache for token storage (default: session)
        "rotationInterval" : 30,          // Token lifetime in minutes (0 = never expire)
        "timeoutSkew"      : 120,         // Seconds before expiry to force-rotate
        "reapFrequency"    : 1,           // Minutes between expired-token cleanup
        "autoVerify"       : false,       // Auto-verify tokens on state-changing requests
        "headerName"       : "x-csrf-token",
        "verifyMethods"    : ["POST", "PUT", "PATCH", "DELETE"]
      }
    }
  }
}

Auto-Verification

When autoVerify: true, every POST/PUT/PATCH/DELETE request is automatically checked for a valid CSRF token in the x-csrf-token header. No manual verification code needed.

Common Pitfalls

  • โœ… Always include the CSRF token for any state-changing request (POST, PUT, DELETE, PATCH)
  • โŒ GET requests should NEVER change state โ€” CSRF protection only applies to non-GET methods
  • โœ… Use scoped tokens (key param) to differentiate between multiple forms on the same page
  • โŒ Don't skip CSRFRotate() after login/logout โ€” stale tokens from old sessions are a risk
  • โœ… For SPA/AJAX apps, retrieve the token server-side and embed it in the page on initial render