cross-site request forgery Protection in a Streaming World
How we prevent cross-site request forgery when the primary interaction is an server streaming stream, not a form submission — token-based cross-site request forgery for AI chat apps.

cross-site request forgery feels like a relic from the early 2010s, something you learned about in a web security class alongside clickjacking and XSS. Most modern frameworks handle it automatically, and most developers don't think about it much.
But here's our situation: the AI chat sends POST requests that book flights and charge credit cards. If a malicious website can trick a logged-in user's browser into sending a POST to our chat endpoint, it could initiate a booking. The chat endpoint is not a form submission. It's an server streaming stream initiation. Most cross-site request forgery documentation assumes forms. We had to think about this more carefully than most teams do.
Why cross-site request forgery still matters for AI chat applications

The classic cross-site request forgery attack works like this: you're logged into your banking site. You visit a malicious page. That page has a hidden form that submits a transfer request to your bank. Your browser helpfully attaches your bank's session cookies to the request. The bank sees what looks like a legitimate, authenticated transfer request.
For our platform, the equivalent attack would be: you're logged into Nowah in one tab. You visit a malicious site in another tab. That site sends a POST to our agent endpoint with a message like "Book the cheapest flight to Las Vegas." If your browser attaches your session credentials to that request, our system might process it as a legitimate booking request.
The fact that our primary interaction is a chat rather than a traditional form doesn't change the fundamental threat. POST requests are POST requests. If they carry session credentials automatically (via cookies), they're vulnerable to cross-site request forgery.
Token-based cross-site request forgery protection
We use token-based cross-site request forgery protection for all non-GET requests from browser clients. The flow:
- When the web client loads, it receives a cross-site request forgery token from the server.
- The client includes this token in a custom header on every state-changing request (POST, PUT, DELETE).
- The server validates the token before processing the request.
- If the token is missing or invalid, the request is rejected.
A malicious site can't include this token because it doesn't know it. The token is delivered to our legitimate client through a mechanism that cross-origin sites can't access. The attacker can trick the browser into sending cookies, but not into sending a custom header with a value they don't know.
The API proxy layer at the edge handles cross-site request forgery enforcement before requests even reach the backend. This means cross-site request forgery-invalid requests are rejected at the edge with minimal resource consumption. An attacker can't waste our backend resources with forged requests.
cross-site request forgery and server streaming stream initiation

Our server streaming streams start with a POST request. The client sends the user's message via POST, and the response is a streaming streaming connection. This POST carries the conversation context and triggers AI inference, which costs money and creates side effects.
The cross-site request forgery token requirement applies to this POST like any other. The stream initiation request must include a valid cross-site request forgery token. Without it, the request gets rejected at the proxy layer.
One subtlety: the cross-site request forgery token needs to be available before the first stream request. Our client fetches the token as part of the initial page load, before the chat interface is interactive. If the token fetch fails, the client retries. The chat input stays disabled until a valid cross-site request forgery token is available. This prevents a race condition where the user types a message before cross-site request forgery protection is ready.
Mobile clients: a different threat model
Our mobile app doesn't use cookies for authentication. It uses Bearer signed session tokens in the Authorization header. The mobile client explicitly attaches the token to each request. There's no automatic credential attachment that a malicious app could exploit.
This means cross-site request forgery is not a meaningful threat for the mobile client. A malicious app on the user's phone can't cause our app to send authenticated requests automatically because the token isn't shared through any ambient mechanism like cookies.
We still validate the signed session tokens on every request, which provides authentication. But the additional cross-site request forgery token layer is specific to the web client where browser cookie behavior creates the cross-site request forgery attack surface.
This distinction matters for API design. The cross-site request forgery middleware checks whether the request comes from a browser client (based on the presence of cookie-based credentials) and only enforces cross-site request forgery tokens for those requests. Mobile API calls with Bearer tokens skip cross-site request forgery validation because they don't need it.
Testing cross-site request forgery protection
cross-site request forgery protection is one of those things that's easy to get right initially and easy to break accidentally. A refactored API client that forgets to include the cross-site request forgery header. A new endpoint that's added without cross-site request forgery middleware. A proxy configuration change that strips custom headers.
We test cross-site request forgery protection in our automated test suite:
Positive tests verify that requests with valid cross-site request forgery tokens succeed. This catches regressions where the cross-site request forgery infrastructure itself breaks and starts rejecting legitimate requests.
Negative tests verify that requests without cross-site request forgery tokens are rejected. This catches regressions where new endpoints are added without cross-site request forgery protection.
Edge case tests verify behavior when the cross-site request forgery token expires, when it's malformed, and when it's from a different session.
These tests run on every deploy. cross-site request forgery protection is security infrastructure that should never regress.
The minimum viable defense
If you're building a web-based AI chat application, here's the minimum cross-site request forgery protection:
Require a cross-site request forgery token on all state-changing requests. POST, PUT, DELETE, PATCH. No exceptions for "internal" or "admin" endpoints.
Deliver the token through a mechanism that cross-origin sites can't read. A response header on a same-origin request works. A cookie with SameSite attributes works. Don't embed it in HTML where a cross-origin script could extract it.
Enforce at the edge, not just the application. Rejecting cross-site request forgery-invalid requests before they reach your application server saves resources and reduces attack surface.
Test both directions. Test that valid tokens work and that missing tokens fail. Both kinds of regressions happen in practice.
Don't skip cross-site request forgery because your app "isn't a traditional form-based app." If your web client sends authenticated POST requests using cookies, it needs cross-site request forgery protection. The shape of the payload (JSON vs. form data) doesn't matter. The attack vector is the same.
Nowah is an AI travel agent that searches and books real flights and hotels through conversation — no filters, no thirty open tabs. Plan your next trip.