Cookie Security: HttpOnly Secure SameSite
Learn: Cookie Security: HttpOnly Secure SameSite
Welcome to TopperBlog! 👋
I'm a tech content creator passionate about helping developers level up their careers and master cutting-edge technologies.
🎯 What I Write About:
• AI/ML Engineering & LLMs
• Web3 & Blockchain Development
• System Design & Architecture
• Interview Preparation (FAANG)
• Freelancing & Remote Work
• Modern Tech Stacks (Next.js, React, Rust, TypeScript)
• Performance Optimization & Best Practices
💼 Mission: Sharing practical, actionable insights that accelerate your tech career and maximize your earning potential.
📚 15+ In-Depth Guides covering everything from earning $10k/month as a freelancer to cracking FAANG interviews.
🌐 Let's connect and grow together in this amazing tech journey!
#TechBlogger #SoftwareEngineering #CareerGrowth #WebDevelopment #AIEngineering
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:
app.use(session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true, // Prevents JS access
maxAge: 3600000 // 1 hour
}
}));
Python/Flask:
@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:
setcookie(
'session_id',
$sessionId,
[
'httponly' => true, // Prevents JS access
'expires' => time() + 3600,
'path' => '/',
'samesite' => 'Strict'
]
);
Tips
- ✅ Always use
HttpOnlyfor 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:
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:
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:
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: falseonly 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:
// 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:
# 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:
// 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
Strictfor sensitive operations (banking, admin panels) - ✅ Use
Laxfor general applications (default in modern browsers) - ✅ Use
Noneonly when cross-origin cookies are necessary - ✅
NonerequiresSecureflag (HTTPS only) - ⚠️ Older browsers don't support SameSite; use CSRF tokens as fallback
4. Complete Secure Cookie Implementation
Code
Node.js/Express (Production-Ready):
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):
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
// 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
# 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
StrictorLax - ✅ HTTPS: Enforced site-wide
- ✅ HSTS: Enabled with
Strict-Transport-Securityheader - ✅ 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.