> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Cookie and Session Security

> Security audit guide for Node/Express session and cookie handling, identifying JWT and CSRF risks, token revocation, refresh tokens, secure cookie flags, and remediation examples.

This lesson walks through auditing cookie and session security in an existing Node/Express codebase. We'll use an automated reviewer (Claude Code for Beginners) that runs focused prompts against the repository to inspect session configuration, cookie flags, CSRF protection, session storage, and relevant route handlers. The goal is a concise, actionable security report with remediation examples you can apply.

Below is a sample of the interactive reviewer CLI output while it loads prompts and inspects the repo. (The prompts themselves are large and stored separately.)

```text theme={null}
/help for help, /status for your current setup

cwd: /Users/jeremy/Repos/Claude Code Course/Express-login-demo

> Try "refactor <filepath>"

? for shortcuts
● Update Todos
  ☐ Examine session configuration in server.js
  ☐ Analyze cookie security settings
  ☐ Check for CSRF protection implementation
  ☐ Review session storage configuration
  ☐ Examine route handlers for session security
  ☐ Create structured security findings report

• Let me examine the codebase structure and analyze the session/cookie security implementation.

• Read(server.js)
  └ Read 19 lines (ctrl+r to expand)

• Read(package.json)
  └ Read 25 lines (ctrl+r to expand)

• Search(pattern: "routes/**/*.js")
  └ Found 1 file (ctrl+r to expand)

• Read(routes/auth.js)
  └ Read 98 lines (ctrl+r to expand)

• Read(.env)
  └ Read 10 lines (ctrl+r to expand)

+ Vibing... (esc to interrupt)
```

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/Xs8Ruv7b0QCZf7iM/images/Claude-Code-for-Beginners/Security-Auditing-with-Claude-Code/Cookie-and-Session-Security/session-cookie-security-audit-screenshot.jpg?fit=max&auto=format&n=Xs8Ruv7b0QCZf7iM&q=85&s=61a3d511f5daad87393c7f263a332a16" alt="A screenshot of a code editor (VS Code) open to a markdown file titled “Session and Cookie Security Audit Report,” containing audit notes and a todo checklist. The project file explorer and side panels are visible on a dark blue themed interface." width="1920" height="1080" data-path="images/Claude-Code-for-Beginners/Security-Auditing-with-Claude-Code/Cookie-and-Session-Security/session-cookie-security-audit-screenshot.jpg" />
</Frame>

Overview of review scope

* Inspect session configuration: secure flag, HttpOnly, SameSite, session timeout, and session regeneration.
* Validate cookie usage: flags, absence of sensitive data, domain/path scoping, and encryption of sensitive cookies.
* Check for CSRF protections: token-based or double-submit cookie patterns, or origin/header checks.
* Review session storage: avoid default in-memory stores in production; prefer Redis or DB-backed stores and ensure cleanup/expiration.
* Produce a structured findings report: title, severity, CWE mapping, evidence, impact, exploitability, PoC (when safe), and code-level remediation.

Quick scope table

| Area | What to check |
| - | - |
| Session config | `secure`, `httpOnly`, `sameSite`, timeout, regeneration |
| Cookies | flags, no sensitive content, domain/path scoping, encryption |
| CSRF | `csurf` or double-submit, origin/header validation |
| Storage | Not using MemoryStore in production; Redis/DB-backed with cleanup |
| Reporting | Structured findings: evidence, CWE, remediation steps |

Executive summary (condensed)

* Authentication design: JWTs are used via Authorization headers — no traditional cookie-based session store is present.
* Risk score: 8.5 / 10 (High) — main issues are lack of token revocation, absence of refresh token rotation, and limited guidance for secure token storage.
* High-level remediation: introduce token revocation (blacklist), rotate and shorten JWT lifetimes, add refresh tokens stored securely (prefer httpOnly cookies), validate/enforce a strong JWT secret, and add CSRF protections if cookies are introduced.

Critical findings and recommended remediations

1. No session management / token revocation (Critical — CWE-613)

* Evidence: `server.js:1-19`, `routes/auth.js:1-98` — JWTs are issued and there is no blacklist or revocation layer.
* Impact: Stolen tokens remain valid until expiry; no immediate revocation path.
* Exploitability: High — attackers with tokens can access resources until expiry.

Remediation (Redis-backed blacklist example)

```javascript theme={null}
// redis-blacklist.js
const redis = require('redis');
const redisClient = redis.createClient();

(async () => {
  await redisClient.connect();
})();

async function isBlacklisted(token) {
  return Boolean(await redisClient.get(`blacklist:${token}`));
}

async function blacklistToken(token, ttlSeconds) {
  // store a key with TTL equal to remaining token lifetime
  await redisClient.setEx(`blacklist:${token}`, ttlSeconds, '1');
}

module.exports = { isBlacklisted, blacklistToken };
```

Middleware to block blacklisted tokens:

```javascript theme={null}
// middleware/checkBlacklist.js
const { isBlacklisted } = require('./redis-blacklist');

async function checkBlacklist(req, res, next) {
  const authHeader = req.headers.authorization || '';
  const token = authHeader.split(' ')[1];
  if (!token) return next();

  if (await isBlacklisted(token)) {
    return res.status(401).json({ error: 'Token revoked' });
  }
  next();
}

module.exports = checkBlacklist;
```

2. Missing refresh token implementation and long-lived access tokens (High — CWE-613)

* Evidence: `routes/auth.js:48-57` — only access tokens are issued; no refresh tokens are present.
* Impact: Long-lived access tokens increase exposure windows. Refresh tokens allow shorter access token lifetimes and controlled re-authentication.

Server-side sketch for refresh tokens:

```javascript theme={null}
// auth.js (excerpt)
const crypto = require('crypto');
const jwt = require('jsonwebtoken');

// On login:
const accessToken = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '15m' });
const refreshToken = crypto.randomBytes(40).toString('hex');

// Persist refresh token in DB with user id and expiration (and optionally rotate it on use)
await db.saveRefreshToken({ token: refreshToken, userId, expiresAt: Date.now() + 7*24*3600*1000 });

// Send access token in response body, set refresh token as httpOnly, secure cookie:
res.cookie('refresh_token', refreshToken, {
  httpOnly: true,
  secure: process.env.NODE_ENV === 'production',
  sameSite: 'strict',
  maxAge: 7 * 24 * 60 * 60 * 1000
});
res.json({ accessToken });
```

Refresh endpoint (rotate refresh token on use):

```javascript theme={null}
// POST /auth/refresh
router.post('/refresh', async (req, res) => {
  const refreshToken = req.cookies.refresh_token;
  const record = await db.findRefreshToken(refreshToken);
  if (!record) return res.status(401).end();

  // Optionally: revoke/blacklist the old refresh token, issue a new one
  const newRefreshToken = crypto.randomBytes(40).toString('hex');
  await db.replaceRefreshToken(record.id, newRefreshToken, /* new expiry */);

  const newAccessToken = jwt.sign({ userId: record.userId }, process.env.JWT_SECRET, { expiresIn: '15m' });
  res.cookie('refresh_token', newRefreshToken, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict' });
  res.json({ accessToken: newAccessToken });
});
```

3. No CSRF protection for cookie usage (Medium — CWE-352)

* Evidence: `server.js:1-19` — no CSRF middleware detected. Currently, JWTs in Authorization headers are not vulnerable to CSRF, but storing refresh tokens in cookies would require CSRF mitigation.
* Impact: Cookies are automatically sent by browsers; without CSRF protections, state-changing endpoints can be abused.

Remediation example using csurf:

```javascript theme={null}
// server.js (excerpt)
const cookieParser = require('cookie-parser');
const csrf = require('csurf');

app.use(cookieParser());

app.use(csrf({ cookie: { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict' } }));

// Expose CSRF token to client for single-page apps, over a safe endpoint:
app.get('/csrf-token', (req, res) => {
  res.json({ csrfToken: req.csrfToken() });
});
```

4. Insecure or weak JWT secret handling (High)

* Evidence: `.env` or code contains default or short JWT secret.
* Impact: Weak or known JWT secrets let attackers forge tokens.
* Exploitability: High if a default/short secret is used.

Remediation: enforce strong random secrets (≥ 32 chars) and check at startup:

```javascript theme={null}
// startup checks
const crypto = require('crypto');
if (!process.env.JWT_SECRET || process.env.JWT_SECRET === 'your_jwt_secret_key_here') {
  throw new Error('JWT_SECRET must be set to a strong, random value (not the default).');
}
if (process.env.JWT_SECRET.length < 32) {
  throw new Error('JWT_SECRET must be at least 32 characters long.');
}
```

For secret rotation, adopt a key-id (`kid`) strategy so older tokens can be validated during a transition period.

5. No session/store configuration (Medium/High)

* Evidence: no session store configured. If you adopt express-session for other parts, do not use MemoryStore in production.
* Impact: MemoryStore leaks memory and lacks persistence and centralized invalidation.

Remediation (express-session + connect-redis):

```javascript theme={null}
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const redis = require('redis');
const redisClient = redis.createClient();

(async () => { await redisClient.connect(); })();

app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: process.env.NODE_ENV === 'production',
    httpOnly: true,
    sameSite: 'strict',
    maxAge: 24 * 60 * 60 * 1000 // 1 day
  }
}));
```

6. Token storage guidance missing (Medium)

* Evidence: no client-side guidance or documentation. Storing JWTs in `localStorage` or `sessionStorage` is vulnerable to XSS.
* Impact: JavaScript-accessible tokens can be exfiltrated via XSS.
* Recommendation: Prefer storing refresh tokens in `httpOnly` cookies; keep access tokens short-lived and in memory. Provide client guidance for native and SPA clients.

Logout and revocation

* Implement logout endpoints to:
  * Blacklist access tokens (as applicable), and/or
  * Delete/expire refresh tokens in the DB and clear refresh cookie.
* Example logout handler:

```javascript theme={null}
router.post('/logout', async (req, res) => {
  const refreshToken = req.cookies.refresh_token;
  if (refreshToken) {
    await db.deleteRefreshToken(refreshToken);
    res.clearCookie('refresh_token', { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict' });
  }
  // Optionally blacklist current access token for its remaining lifetime
  const authHeader = req.headers.authorization || '';
  const accessToken = authHeader.split(' ')[1];
  if (accessToken) {
    const decoded = jwt.decode(accessToken);
    if (decoded && decoded.exp) {
      const ttlSeconds = decoded.exp - Math.floor(Date.now() / 1000);
      // blacklistToken(accessToken, ttlSeconds);
    }
  }
  res.status(200).json({ message: 'Logged out' });
});
```

Security checklist (condensed)

| Check | Status | Notes |
| - | - | - |
| Session configuration | FAIL | No active server-side session store or regeneration |
| Secure cookie flag | N/A | No cookies used for auth currently |
| HttpOnly flag | N/A | No cookies used for auth currently |
| SameSite attribute | N/A | No cookies used for auth currently |
| Session timeout | FAIL | Single JWT lifetime (24h) with no adaptive timeout |
| Session regeneration | FAIL | No session management |
| CSRF protection | FAIL | No CSRF middleware |
| Token storage guidance | FAIL | No client guidance on secure storage options |
| JWT secret strength | FAIL | Potential default or weak secret detected |

Top priority fixes (recommended)

1. Replace any weak/default JWT secret with a strong, randomly generated secret and enforce minimal length checks.
2. Implement token blacklisting/invalidations (Redis or DB-backed) to support immediate revocation.
3. Adopt a refresh-token architecture with short-lived access tokens and rotated refresh tokens stored as secure httpOnly cookies.
4. Add logout endpoints that invalidate refresh tokens and optionally blacklist access tokens.
5. If cookies are used, enable CSRF protection (e.g., `csurf` or double-submit) and ensure `secure`, `httpOnly`, and `sameSite` flags are set.

Proof-of-concept checks (examples)

* Check for missing logout endpoint:

```bash theme={null}
curl -X POST http://localhost:3000/api/auth/logout \
  -H "Authorization: Bearer eyJ..."
# Expected: 404 Not Found if endpoint is missing.
```

* Confirm token remains valid after intended revocation (demonstrates lack of revocation):

```bash theme={null}
curl -X GET http://localhost:3000/api/protected \
  -H "Authorization: Bearer eyJ..."
# If this succeeds after a user was removed from the system, revocation isn't implemented.
```

Notes on audit methodology

* Break audits into focused checks (cookie flags, CSRF, token management, secret handling) rather than issuing a single large prompt. Focused prompts reduce hallucinations and improve coverage.
* Repeated findings across different, independent checks indicate a higher-confidence issue that needs remediation.

Final recommendations (roadmap)

* Immediate: Replace weak JWT secret, add logout and token blacklist, shorten access token lifetime.
* Week 1: Implement refresh-token rotation, persist refresh tokens in a DB, and set refresh cookie flags (`httpOnly`, `secure`, `sameSite`).
* Week 2: Add CSRF protection for cookie-based flows, add session monitoring and adaptive timeouts.
* Month 1: Harden secrets management, implement auditing and alerting for session anomalies, and map controls to compliance frameworks (OWASP Top 10, PCI-DSS, SOC 2, NIST).

<Callout icon="lightbulb" color="#1CB2FE">
  If you adopt cookies for refresh tokens: always set `httpOnly`, `secure` (in production), and `sameSite: 'strict'`, and pair this with CSRF protections for state-changing endpoints.
</Callout>

Subsequent content will cover file handling and business-logic security issues (file uploads, validation, and authorization checks).

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/claude-code-for-beginners/module/eaf75a67-f28f-4217-a3f9-92d411403129/lesson/de194ae1-791f-4364-9686-c6938882ec80" />
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.