← Back to Feed CACHED · 2026-11-02 10:49:30 · CACHE_KEY CVE-2026-117549
CVE-2026-117549 · CWE-347

@fastify/secure-session signed cookie signature verification bypass via cookie stripping

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

A bouncer who checks the forged ticket, sees it fail, and still lets you through the VIP door because the door lock still works

When @fastify/secure-session 7.5.0 through 8.4.1 is configured with signed cookies, a failed signature check still passes the raw cookie value to the session decoder. Because the encryption key is unchanged, the plugin decrypts the cookie and accepts the session. The attacker only needs an already-issued, unexpired encrypted cookie; they need neither the signing secret nor the encryption key.

The vendor's MEDIUM 4.2 is defensible on paper but overstates reality. The vulnerability requires possession of a stolen cookie and an application that rotates only the signing secret while retaining the encryption key. Most teams rotate the encryption key or use a unified secret, and even then the attacker gains no new privileges, cannot forge data, and cannot extend expiry. That is a LOW-severity session life-extension bug in a specific operational misconfiguration.

"Steal a session cookie, strip the rotated signature, and you're still inside — niche config, but it defeats a logout control."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Possess a valid encrypted session cookie

Attacker obtains an unexpired session cookie emitted by @fastify/secure-session. This can happen via XSS, endpoint compromise, log leakage, or physical access, but the encrypted cookie itself is not directly forgeable.
Conditions required:
  • Attacker already holds a session cookie from a victim user
  • Cookie is within its original expiry
Where this breaks in practice:
  • HTTP-only and Secure flags prevent many exfiltration vectors
  • Encrypted payload doesn't leak secrets by itself
  • Requires an existing compromise vector to steal the cookie
Detection/coverage: None — possession happens outside the application's visibility.
STEP 02

Application rotates only the cookie signing secret

The app team rotates the signing secret to invalidate existing sessions but keeps the session encryption key stable. This is the exact configuration where the bug triggers.
Conditions required:
  • Application uses separate signing secret and encryption key
  • Only the signing secret is rotated
Where this breaks in practice:
  • Most deployments rotate the encryption key or use the same key for sign and encrypt
  • Operational rotation of only signing secret is rare
Detection/coverage: Not directly detected; rotation events may be logged in change management.
STEP 03

Strip the obsolete signature from the cookie

The attacker removes the signature portion from the old cookie, leaving the encrypted session value. Because the plugin passes the unsigned payload to the decoder after a failed signature check, the stripped cookie is still eligible for decryption.
Conditions required:
  • Cookie format known (public) or easy to derive
Where this breaks in practice:
  • Trivial to do with a simple string split
  • No secret knowledge required
Detection/coverage: None — this is a client-side manipulation.
STEP 04

Replay stripped cookie to the application

The attacker sends the stripped cookie with a normal request. The plugin's signature check fails, but the vulnerable code decrypts the original cookie body with the unchanged encryption key and restores the user's session.
Conditions required:
  • Network access to the Fastify application
  • Encryption key unchanged
Where this breaks in practice:
  • Session must not have been separately expired by other means
  • Application firewalls generally don't inspect cookie signature format
Detection/coverage: Web server access logs may show the request; WAF may flag unusual cookie shape but often doesn't because the cookie value looks like typical encrypted text.
03 · Compensating Control

1
LOW 3.0→IGNORE 0.0
SEVERITY REDUCED
Upgrade to @fastify/secure-session 8.4.2 — Apply the vendor fix that skips decoding when signature check fails. For a LOW severity, noisgate remediation SLA is ≤365 days, but this patch is a one-line version bump and should be deployed in your next maintenance window.
2
LOW 3.0→IGNORE 0.0
SEVERITY REDUCED
Rotate the session encryption key immediately — If you currently rotate only signing secrets, rotate the encryption key to invalidate all outstanding sessions. This breaks the attack path entirely, even on vulnerable versions. For LOW severity there is no noisgate mitigation SLA, but do this before the next signing secret rotation.
3
LOW 3.0→LOW 2.5
Eliminate split rotation of signing and encryption keys — Configure the plugin to use the same secret for both operations or always rotate both together. This makes the vulnerable code path unreachable.
What doesn't work
  • WAF cookie signature inspection can't reliably detect stripped cookies because the encrypted payload is opaque and the server decrypts after signature check.
  • Setting HTTP-only/Secure flags doesn't help if the attacker already has the cookie.
  • Rotating only the signing secret is exactly the condition that triggers the bug.
04 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo known exploitation; not listed in CISA KEV
PoC availabilityNo standalone public PoC required beyond trivial cookie stripping; no dedicated exploit observed for this CVE
EPSS score / percentileExpected low (EPSS likely below 5% — dependency on stolen cookie suppresses exploitation)
KEV statusNot in CISA Known Exploited Vulnerabilities catalog
CVSS vector interpretationAV:N/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:N — network attack but high complexity due to stolen-cookie prerequisite and config constraint
Affected version range7.5.0 through 8.4.1
Fixed version8.4.2
Disclosure dateUnknown (CVE reserved/future)
Reporting researcher/orgUnknown; issue in Fastify secure-session advisory
Scanning/exposure dataNo mass scanning; npm package install count not directly vulnerable by default

Sources.

  1. NVD CVE-2026-117549
  2. MITRE CVE-2026-117549
  3. GitHub fastify/secure-session repository
  4. npm @fastify/secure-session package
  5. GitHub Security Advisories for fastify/secure-session
  6. Fastify documentation
05 · The Call

Final Verdict
↓ DOWNGRADED to LOW (3.0/10)

Why this verdict

  • Precondition is stolen session cookie — attacker must already possess a valid encrypted cookie; the bug only extends that session after partial rotation.
  • Configuration-specific trigger — signing secret must be independent from encryption key and only signing secret rotated; uncommon.
  • Role multiplier: typical app session only — worst outcome is a single user's continued session; no domain/fleet/supply-chain blast radius even if the app is critical, because the attacker gains no new privileges.

Why not higher?

The bug does not provide initial access, cannot forge or elevate sessions, and cannot survive encryption-key rotation. The requirement that the attacker already holds a session cookie and that the app rotated only the signing secret sharply limits the exposed population, so a MEDIUM or higher rating overstates operational risk.

Why not lower?

The flaw defeats a documented revocation control in the specific configuration. If an admin or privileged user session is stolen and the app relies on signing secret rotation, the attacker retains access, so it cannot be dismissed as IGNORE.

06 · Verification

Crowdsourced verification payload.

Run this on the build or runtime host where the Node.js application depends on @fastify/secure-session. Requires Python 3 and npm installed. From the project root, run python3 check_secure_session.py; no elevated privileges needed. It will inspect package.json and node_modules.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
import json, sys, os

def parse(v):
    return tuple(map(int, v.split('-')[0].split('.')))

try:
    pkg_path = 'node_modules/@fastify/secure-session/package.json'
    if os.path.exists(pkg_path):
        with open(pkg_path) as f:
            data = json.load(f)
        version = data['version']
    else:
        if os.path.exists('package.json'):
            with open('package.json') as f:
                root = json.load(f)
            v = root.get('dependencies', {}).get('@fastify/secure-session') or root.get('devDependencies', {}).get('@fastify/secure-session')
            if not v:
                print('UNKNOWN: package not found in dependencies')
                sys.exit(2)
            version = v.lstrip('^~')
        else:
            print('UNKNOWN: no package.json found')
            sys.exit(2)
    vv = parse(version)
    if (7, 5, 0) <= vv < (8, 4, 2):
        print(f'VULNERABLE: @fastify/secure-session {version} is in vulnerable range 7.5.0-8.4.1')
        sys.exit(1)
    elif vv >= (8, 4, 2):
        print(f'PATCHED: @fastify/secure-session {version} is >=8.4.2')
        sys.exit(0)
    else:
        print(f'UNKNOWN: version {version} is below 7.5.0 or not vulnerable')
        sys.exit(2)
except Exception as e:
    print(f'UNKNOWN: {e}')
    sys.exit(2)