> ## 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.

# Demo Cookie and Session Security

> An audit of an Express app’s cookie and session security focusing on JWT risks, missing revocation, CSRF and remediation patterns with concrete code fixes and prioritization.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/XqMRTrEG2GqQdgEv/images/Claude-Code-For-Beginners/Security-Auditing-with-Claude-Code/Demo-Cookie-and-Session-Security/cookie-session-security-demo.jpg?fit=max&auto=format&n=XqMRTrEG2GqQdgEv&q=85&s=894ebbfc5c2c9abfbf382e76f69da031" alt="A presentation slide titled &#x22;Cookie and Session Security&#x22; with a large dark-blue curved shape on the right containing the word &#x22;Demo.&#x22; The bottom-left shows a small &#x22;© Copyright KodeKloud&#x22; text." width="1920" height="1080" data-path="images/Claude-Code-For-Beginners/Security-Auditing-with-Claude-Code/Demo-Cookie-and-Session-Security/cookie-session-security-demo.jpg" />
</Frame>

This lesson demonstrates how to audit an Express application for cookie- and session-related weaknesses and provides concrete remediation patterns. The example focuses on JWT-based authentication (no server-side cookie sessions), highlights the security implications, and maps findings to prioritized fixes you can implement quickly.

We ran an interactive analysis session (local assistant/REPL) to explore the repository and guide the audit. The session output looked like this:

```bash 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
```

A shortened sample of the structured audit prompt used to direct the analysis:

```text theme={null}
• 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)
```

<Callout icon="lightbulb" color="#1CB2FE">
  Large or reusable prompts used for these audits are stored in a repository to avoid retyping long instructions each time.
</Callout>

***

## What the audit looked for

The audit checks focused on session and cookie hygiene, CSRF controls, and session storage practices. Key control areas included:

* Session configuration (secure/httpOnly/sameSite, regeneration, timeouts)
* Cookie security (flags, scope, no sensitive data in cookies, encryption)
* CSRF protection (tokens, double-submit cookies, origin/referer checks)
* Session storage (avoid default memory store, prefer Redis/DB, cleanup)

Detailed checklist mapping:

| Control Area          | Specific Checks                                              | Why it matters                                             |
| --------------------- | ------------------------------------------------------------ | ---------------------------------------------------------- |
| Session configuration | secure, httpOnly, sameSite, maxAge, resave/saveUninitialized | Prevents token theft and session fixation                  |
| Cookie security       | Flags, domain/path scoping, encryption for sensitive values  | Limits client-side access and cross-site leakage           |
| CSRF protection       | csrf middleware, double-submit cookie, header validation     | Mitigates cross-site request forgery when cookies are used |
| Session storage       | No in-memory store in prod, use Redis or DB, TTL cleanup     | Durable and scalable revocation/invalidations              |
| JWT-specific          | Refresh tokens, blacklisting, secret strength                | JWTs can be valid even after logout without revocation     |

The audit also produced a structured findings report with fields like Title, Severity, CWE, Evidence (file/line), Exploitability notes, and remediation snippets.

***

## High-level summary of findings

* The app uses JWT-based authentication (no server-side cookie sessions), so many cookie-specific checks were N/A. However, JWTs introduce other risks that require operational controls.
* Key issues discovered:
  * No server-side token revocation (no blacklisting).
  * No refresh token pattern — access tokens are long-lived or not rotated safely.
  * JWT secret management is weak or not validated (risk of brute-force or leaked secrets).
  * No CSRF middleware detected (relevant if cookies are introduced later).
  * No explicit guidance for secure client-side token storage (storing JWTs in localStorage is risky).

Example diagnostic notes:

* No cookie usage found — Secure/HttpOnly/SameSite checks were marked N/A.
* No CSRF middleware in server.js (server.js:1-19).
* No refresh token implementation and no logout endpoint that invalidates tokens.
* JWTs remain valid until expiry — no revocation mechanism in place.

Final (example) risk score: 8.5 / 10 (high). Immediate priorities: rotate JWT secret and add token blacklisting.

***

## Representative audit excerpts

The report explicitly noted that many cookie checks were skipped due to JWT-only usage, but it stressed that any future introduction of cookies must include Secure, HttpOnly, and SameSite flags and CSRF protections. It also flagged missing logout/revocation endpoints and insecure secret handling.

***

## Remediation snippets and patterns

Below are concrete, copy-paste-friendly code patterns you can adapt to your codebase. Keep your app structure and error handling in mind when integrating these.

1. Server-side session (cookie) configuration (only if you switch to cookie sessions)

```javascript theme={null}
const session = require('express-session');

app.use(session({
  secret: process.env.SESSION_SECRET,
  cookie: {
    secure: process.env.NODE_ENV === 'production', // HTTPS-only in prod
    httpOnly: true, // prevent JS access
    sameSite: 'strict', // mitigate CSRF for sensitive ops
    maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days
  },
  resave: false,
  saveUninitialized: false
}));
```

2. CSRF protection (when cookies are used)

```javascript theme={null}
const csrf = require('csurf');

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

// Example: expose token in responses where needed
app.get('/form', (req, res) => {
  res.json({ csrfToken: req.csrfToken() });
});
```

3. Strong JWT secret validation (startup-time checks)

```javascript theme={null}
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');
}

if (process.env.JWT_SECRET.length < 32) {
  throw new Error('JWT_SECRET must be at least 32 characters long');
}
```

4. Implement refresh token pattern (access + refresh token)

```javascript theme={null}
const crypto = require('crypto');
const jwt = require('jsonwebtoken');

// Example login handler
router.post('/login', async (req, res) => {
  // Authenticate user here...
  const payload = { sub: user.id };
  const accessToken = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '15m' });
  const refreshToken = crypto.randomBytes(40).toString('hex');

  // Store refreshToken in DB with user id and expiration
  await db.insertRefreshToken({ token: refreshToken, userId: user.id, expiresAt });

  // Return access token and set refresh token in an HttpOnly, Secure cookie if desired
  res.cookie('refreshToken', refreshToken, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'strict',
    maxAge: 1000 * 60 * 60 * 24 * 30 // 30 days
  });

  res.json({ accessToken });
});
```

5. Token blacklisting (Redis-backed revocation) — middleware + logout

```javascript theme={null}
// Redis client (example using node-redis v4)
const redis = require('redis');
const redisClient = redis.createClient();
redisClient.connect().catch(console.error);
const jwt = require('jsonwebtoken');

// Middleware to check blacklisted tokens and verify token validity
const checkBlacklist = async (req, res, next) => {
  const auth = req.headers.authorization;
  if (!auth || !auth.startsWith('Bearer ')) return res.status(401).json({ error: 'Missing token' });

  const token = auth.split(' ')[1];

  // Verify token signature and expiry before trusting claims
  try {
    jwt.verify(token, process.env.JWT_SECRET);
  } catch (err) {
    return res.status(401).json({ error: 'Invalid token' });
  }

  const blacklisted = await redisClient.get(`blacklist:${token}`);
  if (blacklisted) return res.status(401).json({ error: 'Token revoked' });

  next();
};
```

Logout handler to add token to blacklist with TTL equal to the remaining lifetime:

```javascript theme={null}
// Example logout handler
router.post('/logout', async (req, res) => {
  const auth = req.headers.authorization;
  if (!auth || !auth.startsWith('Bearer ')) return res.status(400).end();

  const token = auth.split(' ')[1];

  // Verify token to get expiry (verify throws if invalid)
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    const now = Math.floor(Date.now() / 1000);
    const ttl = (decoded.exp || now) - now;

    if (ttl > 0) {
      await redisClient.set(`blacklist:${token}`, '1', { EX: ttl });
    }
  } catch (err) {
    // If token is invalid, we can still respond success — it is effectively logged out
  }

  res.status(200).json({ success: true });
});
```

6. Sliding session timeout / short-lived access tokens example

```javascript theme={null}
const jwt = require('jsonwebtoken');

const checkTokenFreshness = (req, res, next) => {
  const auth = req.headers.authorization;
  if (!auth || !auth.startsWith('Bearer ')) return res.status(401).end();

  const token = auth.split(' ')[1];

  // Verify token to ensure claims (iat/exp) are trustworthy
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    const tokenAge = Math.floor(Date.now() / 1000) - (decoded.iat || 0);

    // If token is older than 1 hour, require re-authentication
    if (tokenAge > 3600) return res.status(401).json({ error: 'Session expired, re-authenticate' });

    next();
  } catch (err) {
    return res.status(401).json({ error: 'Invalid token' });
  }
};
```

<Callout icon="warning" color="#FF6B6B">
  Avoid storing access or refresh tokens in localStorage or any storage accessible to JavaScript. Prefer HttpOnly cookies for refresh tokens and keep access tokens short-lived and in memory. Storing tokens in localStorage increases your attack surface for XSS.
</Callout>

7. Guidance summary — secure token handling

* Use HttpOnly, Secure, SameSite cookies for refresh tokens.
* Keep access tokens short-lived (minutes) and refresh them via a secure refresh flow.
* Store refresh tokens server-side (DB/Redis) or as HttpOnly cookies with rotation.
* Validate JWT\_SECRET at startup and rotate secrets on a regular schedule.

***

## Proof-of-concept checks (curl examples)

Use these commands to reproduce the lack of revocation or logout behavior during testing:

```bash theme={null}
# 1. Login and capture JWT
curl -s -X POST http://localhost:3000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"password123"}'

# 2. Attempt logout (if no endpoint exists)
curl -i -X POST http://localhost:3000/api/auth/logout \
  -H "Authorization: Bearer eyJ..."
# 3. Verify token remains valid until expiry
curl -i -X GET http://localhost:3000/api/protected \
  -H "Authorization: Bearer eyJ..."
# If valid, protected route responds 200 OK — indicates no server-side invalidation
```

***

## Prioritization & remediation roadmap

Top fixes to reduce risk fast:

| Priority | Action                                                                                 | Duration  |
| -------- | -------------------------------------------------------------------------------------- | --------- |
| P0       | Replace weak/hard-coded JWT secrets with a strong random secret and enforce min length | Immediate |
| P0       | Implement token blacklisting (Redis) for revocation                                    | Immediate |
| P1       | Add refresh token pattern with HttpOnly secure cookies                                 | Week 1    |
| P1       | Add logout that revokes access and refresh tokens                                      | Week 1    |
| P2       | Add CSRF protection if cookies are used                                                | Week 2    |
| P2       | Move any session state to Redis or a DB-backed store (no memory store in prod)         | Week 2–4  |

Suggested timeline:

* Immediate: JWT secret rotation and token blacklisting.
* Week 1: Refresh tokens + secure cookie patterns and logout.
* Week 2: CSRF controls and session store adoption.
* Month 1: Session monitoring, adaptive timeouts, and device tracking.

***

## Compliance impact

The audit mapped findings to common frameworks:

* OWASP Top 10: A07:2021 (Identification and Authentication Failures) — applicable due to lack of revocation and long-lived tokens.
* PCI DSS / SOC 2 / NIST: Insufficient session invalidation and access management controls may impact compliance posture.

***

## Closing notes

* JWT-only architectures reduce some cookie risks but require operational controls: revocation, refresh, and secure storage.
* If you introduce cookies later, ensure Secure, HttpOnly, and SameSite flags and add CSRF protection.
* Break audits into focused checks (sessions, cookies, CSRF, storage) to produce actionable, prioritized findings and reduce missed items.

Upcoming topics will cover file handling and business logic audits.

## Links and references

* [OWASP Top 10 — Authentication Failures](https://owasp.org/Top10/)
* [Express Session Documentation](https://www.npmjs.com/package/express-session)
* [csurf middleware](https://www.npmjs.com/package/csurf)
* [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken)
* [node-redis client](https://www.npmjs.com/package/redis)

<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>
