[BUG] EverShop Uses a Public Default Secret for Session Cookies
EverShop Uses a Public Default Secret for Session Cookies
Summary
EverShop uses the public value keyboard cat when
system.session.cookieSecret is not configured. The value is used to sign
EverShop session cookies and other signed cookies.
An attacker can therefore generate valid signatures for arbitrary cookie
values. In the production authentication flow, however, the cookie contains
only a session identifier. EverShop loads the session from PostgreSQL and
loads the administrator from the server-side userID. The public secret
alone does not create an authenticated administrator session.
This is a hard-coded default session secret. It becomes an authentication bypass when combined with a separate session-ID disclosure, session fixation, or attacker-controlled server-side session state.
Evidence
The public fallback is defined here:
export const getCookieSecret = (): string =>
getConfig('system.session.cookieSecret', 'keyboard cat');The value is passed to both express-session and cookie-parser:
const cookieSecret = getCookieSecret();
const sess = {
store: new (sessionStorage(session))({ pool }),
secret: cookieSecret,
// ...
};
app.use(cookieParser(cookieSecret));Source: addDefaultMiddlewareFuncs.ts
After login, EverShop stores the administrator ID in the server-side session:
if (this.session) {
this.session.userID = user.admin_user_id;
}For an admin request, EverShop uses the cookie as a Session ID, loads the
corresponding PostgreSQL session, and then queries the active administrator
using the stored userID:
const sessionID = cookies[adminSessionCookieName];
const adminSessionData = await getSession(sessionID);
currentAdminUser = await select()
.from('admin_user')
.where('admin_user_id', '=', adminSessionData.userID)
.and('status', '=', 1)
.load(pool);Source: [context]getCurrentUser.ts
EverShop's JWT path uses separate configured secrets and does not fall back to
keyboard cat:
if (!secret) {
throw new Error(`JWT secret for ${tokenType} is not configured`);
}Proof of concept
Run this only against a local EverShop test instance. The following creates a correctly signed admin-session cookie for an attacker-chosen Session ID:
const signature = require('cookie-signature');
const sessionId = 'attacker-chosen-session-id';
const signed = `s:${sessionId}.${signature.sign(sessionId, 'keyboard cat')}`;
console.log(encodeURIComponent(signed));Send the result to a private admin route:
curl -i \
-H 'Cookie: asid=<url-encoded-signed-cookie>' \
http://127.0.0.1:3000/api/user/session/tokensExpected result: 401 Unauthorized. The signature is valid, but the chosen
Session ID has no authenticated server-side session. This demonstrates the
boundary of the issue: knowing the cookie secret signs a client value, but it
does not create the PostgreSQL session or the administrator record referenced
by that value.
As a positive control, log in to the local test instance, retain the returned
asid cookie, and request the same route. The request succeeds because the
cookie points to a server-side PostgreSQL session containing a valid
administrator userID.
Impact
The default keyboard cat secret allows anyone who knows the public source to
produce valid signatures for EverShop session-cookie values.
The secret alone does not demonstrate arbitrary administrator impersonation in the current production flow because the session contents and administrator identity are stored and checked server-side. An authentication bypass requires an additional session-state weakness, such as disclosure or fixation of a valid Session ID, or attacker-controlled creation of the referenced session.
Remediation
- Remove
keyboard catas the production default. - Require a deployment-specific high-entropy
cookieSecret. - Fail closed at startup when the production secret is missing or equals a public placeholder.
- Use separate secrets for administrator sessions, storefront sessions, and unrelated signed cookies.
- Rotate the secret and invalidate existing sessions after deployment of the fix.
Source: evershopcommerce/evershop