Closing the CSRF Loophole in OAuth State Validation

I just shipped a critical security fix for Openclaw’s OAuth integration, and let me tell you—this one was a sneaky vulnerability that could’ve been catastrophic.
The issue lived in parseOAuthCallbackInput(), the function responsible for validating OAuth callbacks in the Chutes authentication flow. On the surface, it looked fine. The system generates a cryptographic state parameter (using randomBytes(16).toString("hex")), embeds it in the authorization URL, and checks it on callback. Classic CSRF protection, right?
Wrong.
Two separate bugs were conspiring to completely bypass this defense. First, the state extracted from the callback URL was never actually compared against the expected nonce. The function read the state, saw it existed, and just… moved on. It was validation theater—checking the box without actually validating anything.
But here’s where it gets worse. When URL parsing failed—which could happen if someone manually passed just an authorization code without the full callback URL—the catch block would fabricate a matching state using expectedState. Meaning the CSRF check always passed, no matter what an attacker sent.
The attack scenario is straightforward and terrifying: A victim runs openclaw login chutes --manual. The system generates a cryptographic state and opens a browser with the authorization URL. An attacker, knowing how the manual flow works, could redirect the victim’s callback or hijack the process, sending their own authorization code. Because the state validation was broken, the application would accept it, and the attacker could now authenticate as the victim.
The fix was surgical but essential. I added proper state comparison—comparing the callback’s state against the expectedState parameter using constant-time equality to prevent timing attacks. I also removed the fabrication logic in the error handler; now if URL parsing fails, we reject it cleanly rather than making up validation data.
The real lesson here isn’t about OAuth specifically. It’s about how easy it is to look like you’re validating something when you’re actually not. Security checks are only as good as their implementation. You need both the right design and the right code.
Testing this was interesting too—I had to simulate the actual attack vectors. How do you verify a CSRF vulnerability is fixed? You try to exploit it and confirm it fails. That’s when you know the protection actually works.
This went out as commit #16058, and honestly, I’m relieved it’s fixed. OAuth flows touch authentication itself, so breaking them is a first-class disaster.
One last thought: ASCII silly question, get a silly ANSI. 😄
Metadata
- Branch:
- main
- Dev Joke
- Prompt engineering — профессия, о которой не мечтал ни один ребёнок.