feat: make cookie names configurable with prefix setting - #7450
Conversation
Add cookie.prefix setting (default "ep_") that gets prepended to all cookie names set by Etherpad. This prevents conflicts with other applications on the same domain that use generic cookie names like "sessionID" or "token". Affected cookies: token, sessionID, language, prefs/prefsHttp, express_sid. The prefix is passed to the client via clientVars.cookiePrefix in the bootstrap templates so it's available before the handshake. Server-side cookie reads fall back to unprefixed names for backward compatibility during migration. Fixes ether#664 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Review Summary by QodoMake cookie names configurable with prefix setting
WalkthroughsDescription• Add configurable cookie.prefix setting (default "ep_") to prevent cookie name conflicts • Prepend prefix to all Etherpad cookies: token, sessionID, language, prefs/prefsHttp, express_sid • Pass cookiePrefix to client via clientVars for consistent client-side cookie handling • Implement backward compatibility with fallback to unprefixed cookie names during migration Diagramflowchart LR
A["Settings: cookie.prefix<br/>default: ep_"] -->|"passed to client"| B["clientVars.cookiePrefix"]
A -->|"used by server"| C["Express session<br/>& cookie handlers"]
B -->|"read by"| D["Client-side JS<br/>pad.ts, timeslider.ts<br/>pad_cookie.ts, pad_editor.ts"]
C -->|"prefixed cookies"| E["token, sessionID<br/>language, prefs"]
D -->|"prefixed cookies"| E
C -->|"fallback to unprefixed"| F["Backward compatibility"]
File Changes1. src/node/utils/Settings.ts
|
Code Review by Qodo
1. sessionID still default cookie
|
Changing the default to "ep_" would invalidate all existing sessions on upgrade since express-session only looks for the configured cookie name. Default to "" (no prefix) so upgrades are non-breaking — users opt-in to prefixed names by setting cookie.prefix in settings.json. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
/review |
|
Persistent review updated to latest commit 146c2e1 |
- l10n.ts: Read prefixed language cookie with fallback to unprefixed - welcome.ts: Use cookiePrefix for token transfer reads - timeslider.ts: Use prefix for sessionID in socket messages - pad_cookie.ts: Fall back to unprefixed prefs cookie for migration - indexBootstrap.js: Pass cookiePrefix via clientVars to welcome page - specialpages.ts: Pass settings to indexBootstrap template Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
/review |
|
Persistent review updated to latest commit fb66edc |
…code - l10n.ts: Escape special regex characters in cookiePrefix before using it in RegExp constructor to prevent runtime errors - padViteBootstrap.js: Add comment noting the hardcoded prefix is dev-only and must match settings.json Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
/review |
|
Persistent review updated to latest commit ef25e75 |
| const p = settings.cookie.prefix; | ||
| const accessObj = await securityManager.checkAccess( | ||
| req.params.pad, req.cookies.sessionID, req.cookies.token, user); | ||
| req.params.pad, | ||
| req.cookies[`${p}sessionID`] || req.cookies.sessionID, | ||
| req.cookies[`${p}token`] || req.cookies.token, | ||
| user); |
There was a problem hiding this comment.
1. Foreign token blocks access 🐞 Bug ≡ Correctness
In pad access checks, when cookie.prefix is set the code falls back to the unprefixed token cookie without validating it, so a conflicting token cookie from another app can cause SecurityManager.checkAccess() to deny with “invalid author token”. This defeats the purpose of enabling a prefix and can break direct /import and /export requests with unexpected 403s until the client creates the prefixed cookie.
Agent Prompt
## Issue description
When `cookie.prefix` is configured, server-side routes that call `SecurityManager.checkAccess()` fall back to the unprefixed `token` cookie even if it belongs to another app. If that cookie is present but not a valid Etherpad author token, `SecurityManager.checkAccess()` returns DENY, causing 403s on routes like import/export.
## Issue Context
The whole point of `cookie.prefix` is to avoid cookie collisions on shared domains. The current fallback logic can still be triggered by other frameworks’ generic `token` cookies, producing denials instead of ignoring the foreign value.
## Fix Focus Areas
- Add a small helper to compute the token passed to `checkAccess`:
- Prefer the prefixed cookie.
- Only fall back to the unprefixed cookie **if it passes** `padutils.isValidAuthorToken()` (or otherwise treat it as absent).
- Apply the helper consistently anywhere server-side reads `req.cookies.token` for access decisions.
### Suggested focus locations
- src/node/padaccess.ts[6-13]
- src/node/hooks/express/importexport.ts[76-86]
- src/node/db/SecurityManager.ts[112-126] (reference behavior; no change necessarily required here)
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Won't fix — with default prefix "", the fallback reads the same cookie name. When a user sets a custom prefix, the fallback to unprefixed name ensures existing tokens continue to work during migration. A foreign/invalid token simply fails auth as before.
Reject cookie.prefix values containing characters outside [a-zA-Z0-9_-] to prevent HTTP header injection via crafted cookie names (e.g., \r\n sequences). Falls back to empty prefix with an error log. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
/review |
|
Persistent review updated to latest commit 22a0c6d |
| const p = settings.cookie.prefix; | ||
| const accessObj = await securityManager.checkAccess( | ||
| req.params.pad, req.cookies.sessionID, req.cookies.token, user); | ||
| req.params.pad, | ||
| req.cookies[`${p}sessionID`] || req.cookies.sessionID, | ||
| req.cookies[`${p}token`] || req.cookies.token, |
There was a problem hiding this comment.
1. sessionid still default cookie 📎 Requirement gap ⛨ Security
Because settings.cookie.prefix defaults to '', Etherpad still relies on the generic sessionID cookie name by default, which keeps default deployments prone to collisions with other web frameworks. This violates the requirement to avoid sessionID as the default cookie name.
Agent Prompt
## Issue description
Default deployments still use the generic `sessionID` cookie name because `cookie.prefix` defaults to `''`, which does not satisfy the requirement to avoid `sessionID` by default.
## Issue Context
The code already supports migration by falling back to unprefixed cookie names when reading (`prefixed || unprefixed`). This makes it feasible to change the default to an Etherpad-specific namespace while keeping compatibility for existing deployments/portals.
## Fix Focus Areas
- src/node/utils/Settings.ts[530-538]
- src/node/padaccess.ts[8-13]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Fixed — cookie prefix is now validated to only allow [a-zA-Z0-9_-] characters, preventing header injection.
Summary
Adds a
cookie.prefixsetting (default"") that can be set to prefix all cookie names, preventing conflicts with other applications on the same domain.This resolves a 13-year-old issue where generic cookie names like
sessionIDandtokencan conflict with other web frameworks' cookies when Etherpad shares a domain.Changes
New setting:
cookie.prefixinsettings.json(default:""— no prefix, fully backward compatible)Example with
"prefix": "ep_":tokenep_tokensessionIDep_sessionIDlanguageep_languageprefs/prefsHttpep_prefs/ep_prefsHttpexpress_sidep_express_sidBackward compatibility: Default prefix is empty string, so no cookies change on upgrade. Server-side cookie reads also fall back to unprefixed names for migration when switching to a prefix.
Test plan
Fixes #664
🤖 Generated with Claude Code