# Cookie Security: HttpOnly Secure SameSite

# Cookie Security: HttpOnly, Secure, SameSite

## 1. HttpOnly Flag

### Problem
JavaScript can access cookies via `document.cookie`, making them vulnerable to XSS attacks. Malicious scripts can steal session tokens and authentication credentials.

### Solution
Set the `HttpOnly` flag to prevent JavaScript from accessing cookies. Only the server can read these cookies via HTTP headers.

### Code

**Node.js/Express:**
```javascript
app.use(session({
  secret: 'your-secret-key',
  resave: false,
  saveUninitialized: true,
  cookie: {
    httpOnly: true,      // Prevents JS access
    maxAge: 3600000      // 1 hour
  }
}));
```

**Python/Flask:**
```python
@app.route('/login', methods=['POST'])
def login():
    response = make_response(redirect('/dashboard'))
    response.set_cookie(
        'session_token',
        value=generate_token(),
        httponly=True,      # Prevents JS access
        max_age=3600
    )
    return response
```

**PHP:**
```php
setcookie(
    'session_id',
    $sessionId,
    [
        'httponly' => true,  // Prevents JS access
        'expires' => time() + 3600,
        'path' => '/',
        'samesite' => 'Strict'
    ]
);
```

### Tips
- ✅ Always use `HttpOnly` for authentication cookies
- ✅ Combine with other flags for defense-in-depth
- ✅ Monitor for XSS vulnerabilities in your application
- ⚠️ Non-sensitive cookies (tracking, preferences) can remain accessible to JS

---

## 2. Secure Flag

### Problem
Cookies transmitted over unencrypted HTTP connections can be intercepted by man-in-the-middle (MITM) attacks. Attackers can capture session tokens in transit.

### Solution
Set the `Secure` flag to ensure cookies are only transmitted over HTTPS connections, encrypting data in transit.

### Code

**Node.js/Express:**
```javascript
app.use(session({
  secret: 'your-secret-key',
  cookie: {
    secure: true,        // HTTPS only
    httpOnly: true,
    sameSite: 'Strict',
    maxAge: 3600000
  }
}));

// Enforce HTTPS in production
if (process.env.NODE_ENV === 'production') {
  app.use((req, res, next) => {
    if (req.header('x-forwarded-proto') !== 'https') {
      res.redirect(`https://${req.header('host')}${req.url}`);
    } else {
      next();
    }
  });
}
```

**Python/Flask:**
```python
app.config['SESSION_COOKIE_SECURE'] = True  # HTTPS only
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'Strict'

@app.before_request
def enforce_https():
    if not request.is_secure and app.env == 'production':
        url = request.url.replace('http://', 'https://', 1)
        return redirect(url, code=301)
```

**Nginx Configuration:**
```nginx
server {
    listen 443 ssl http2;
    server_name example.com;
    
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    
    # Redirect HTTP to HTTPS
    error_page 497 =301 https://$host$request_uri;
    
    location / {
        proxy_pass http://backend;
        proxy_cookie_flags ~ secure httponly samesite=strict;
    }
}
```

### Tips
- ✅ Always use HTTPS in production
- ✅ Use HSTS headers to enforce HTTPS
- ✅ Obtain valid SSL/TLS certificates (Let's Encrypt is free)
- ✅ Test with `secure: false` only in development
- ⚠️ Localhost development can bypass Secure flag requirement

---

## 3. SameSite Attribute

### Problem
Cross-Site Request Forgery (CSRF) attacks trick users into making unwanted requests to other sites. Cookies are automatically sent with cross-origin requests, allowing attackers to perform actions on behalf of users.

### Solution
Set `SameSite` to control when cookies are sent with cross-origin requests. Three levels: `Strict`, `Lax`, and `None`.

### Code

**Node.js/Express:**
```javascript
// Strict: Only same-site requests
app.use(session({
  cookie: {
    sameSite: 'Strict',  // Most restrictive
    secure: true,
    httpOnly: true
  }
}));

// Lax: Same-site + top-level navigation (default in modern browsers)
app.use(session({
  cookie: {
    sameSite: 'Lax',     // Balanced approach
    secure: true,
    httpOnly: true
  }
}));

// None: All requests (requires Secure flag)
app.use(session({
  cookie: {
    sameSite: 'None',    // Cross-site allowed
    secure: true,        // REQUIRED with None
    httpOnly: true
  }
}));
```

**Python/Flask:**
```python
# Strict
app.config['SESSION_COOKIE_SAMESITE'] = 'Strict'

# Lax (recommended for most apps)
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'

# None (for cross-origin scenarios)
app.config['SESSION_COOKIE_SAMESITE'] = 'None'
app.config['SESSION_COOKIE_SECURE'] = True  # Required
```

**PHP:**
```php
// Strict
setcookie('session_id', $value, [
    'samesite' => 'Strict',
    'secure' => true,
    'httponly' => true
]);

// Lax
setcookie('session_id', $value, [
    'samesite' => 'Lax',
    'secure' => true,
    'httponly' => true
]);

// None
setcookie('session_id', $value, [
    'samesite' => 'None',
    'secure' => true,  // REQUIRED
    'httponly' => true
]);
```

**SameSite Behavior Table:**

| Mode | Same-Site Requests | Cross-Site GET | Cross-Site POST |
|------|-------------------|-----------------|-----------------|
| **Strict** | ✅ Sent | ❌ Not sent | ❌ Not sent |
| **Lax** | ✅ Sent | ✅ Sent | ❌ Not sent |
| **None** | ✅ Sent | ✅ Sent | ✅ Sent |

### Tips
- ✅ Use `Strict` for sensitive operations (banking, admin panels)
- ✅ Use `Lax` for general applications (default in modern browsers)
- ✅ Use `None` only when cross-origin cookies are necessary
- ✅ `None` requires `Secure` flag (HTTPS only)
- ⚠️ Older browsers don't support SameSite; use CSRF tokens as fallback

---

## 4. Complete Secure Cookie Implementation

### Code

**Node.js/Express (Production-Ready):**
```javascript
const express = require('express');
const session = require('express-session');
const helmet = require('helmet');

const app = express();

// Security headers
app.use(helmet());

// CSRF protection
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: false });

// Session configuration
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    httpOnly: true,           // Prevent XSS access
    secure: process.env.NODE_ENV === 'production',  // HTTPS only
    sameSite: 'Strict',       // CSRF protection
    maxAge: 1000 * 60 * 60,   // 1 hour
    domain: process.env.COOKIE_DOMAIN
  }
}));

// Login route
app.post('/login', csrfProtection, (req, res) => {
  // Authenticate user
  req.session.userId = user.id;
  req.session.regenerate((err) => {
    if (err) return res.status(500).send('Session error');
    res.redirect('/dashboard');
  });
});

// Logout route
app.post('/logout', (req, res) => {
  req.session.destroy((err) => {
    if (err) return res.status(500).send('Logout error');
    res.clearCookie('connect.sid');
    res.redirect('/');
  });
});

// HTTPS enforcement
if (process.env.NODE_ENV === 'production') {
  app.use((req, res, next) => {
    if (req.header('x-forwarded-proto') !== 'https') {
      res.redirect(`https://${req.header('host')}${req.url}`);
    } else {
      next();
    }
  });
}

app.listen(3000);
```

**Python/Flask (Production-Ready):**
```python
from flask import Flask, session, redirect, request
from flask_session import Session
from flask_talisman import Talisman
import os

app = Flask(__name__)

# Security headers
Talisman(app, force_https=True)

# Session configuration
app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'Strict'
app.config['PERMANENT_SESSION_LIFETIME'] = 3600
app.config['SESSION_TYPE'] = 'filesystem'

Session(app)

@app.route('/login', methods=['POST'])
def login():
    # Authenticate user
    session['user_id'] = user.id
    session.permanent = True
    return redirect('/dashboard')

@app.route('/logout', methods=['POST'])
def logout():
    session.clear()
    return redirect('/')

if __name__ == '__main__':
    app.run(ssl_context='adhoc', debug=False)
```

---

## 5. Testing & Verification

### Browser DevTools
```javascript
// Check cookie flags in DevTools → Application → Cookies
// Look for:
// - HttpOnly: ✅ (no JS access)
// - Secure: ✅ (HTTPS only)
// - SameSite: Strict/Lax/None

// Test XSS protection
document.cookie  // Should NOT show HttpOnly cookies
```

### cURL Testing
```bash
# Check Set-Cookie headers
curl -i https://example.com/login

# Expected output:
# Set-Cookie: session_id=abc123; Path=/; HttpOnly; Secure; SameSite=Strict
```

### OWASP ZAP / Burp Suite
- Scan for missing cookie flags
- Test CSRF vulnerabilities
- Verify HTTPS enforcement

---

## 6. Security Checklist

- ✅ **HttpOnly**: Enabled for all authentication cookies
- ✅ **Secure**: Enabled in production (HTTPS only)
- ✅ **SameSite**: Set to `Strict` or `Lax`
- ✅ **HTTPS**: Enforced site-wide
- ✅ **HSTS**: Enabled with `Strict-Transport-Security` header
- ✅ **CSRF Tokens**: Implemented as secondary protection
- ✅ **Session Regeneration**: After login/privilege changes
- ✅ **Cookie Expiration**: Set reasonable timeouts
- ✅ **Secure Domain**: Cookies scoped to specific domain
- ✅ **Monitoring**: Log suspicious cookie access attempts

---

## 7. Common Pitfalls

| ❌ Wrong | ✅ Correct |
|---------|-----------|
| `secure: false` in production | `secure: true` with HTTPS |
| Missing `httpOnly` flag | `httpOnly: true` always |
| `sameSite: 'None'` without `secure` | `sameSite: 'None', secure: true` |
| No session regeneration | Regenerate after login |
| Storing sensitive data in cookies | Store only session ID |
| No CSRF protection | Implement CSRF tokens + SameSite |
| HTTP in production | Enforce HTTPS everywhere |

---

## Summary

**HttpOnly** → Prevents XSS attacks  
**Secure** → Prevents MITM attacks  
**SameSite** → Prevents CSRF attacks  

Use all three together for comprehensive cookie security.
