Cookie Attributes (HttpOnly, Secure, SameSite)
Cookie Attributes (HttpOnly, Secure, SameSite)
Level 9 — Browser APIs (Storage & State) The flags that make cookies safe for auth.
1. Prerequisites
- Cookies — The header-based browser storage state.
- Session vs Token Authentication — The authentication paradigms utilizing cookies.
2. Term Category
Security (Universal: Configured on backend application server response headers and processed by client-side browser engines.): Cookie Attributes (HttpOnly, Secure, SameSite) is a fundamental concept in this technology stack. Level 9 — Browser APIs (Storage & State)
3. Explanation
(1) Design Motivation — "Why did we design this?"
By default, cookies are simple key-value strings stored in the browser and sent automatically with every matching HTTP request. However, when cookies store session IDs or JWTs, they become high-value targets for attackers:
- XSS Attacks: If an attacker injects malicious JavaScript into your site, they can access
document.cookieto steal authentication tokens. - CSRF Attacks: If a user visits a malicious site, that site can trigger background API calls to your server. The browser automatically appends your site's cookies to the request, executing actions on behalf of the user.
To protect authentication state, servers configure Cookie Attributes inside the Set-Cookie header to restrict when cookies can be accessed and sent:
1. HttpOnly
- Behavior: Prevents client-side JavaScript from reading or writing the cookie (e.g.
document.cookiewill not show it). - Defense: Protects the cookie from theft via XSS (Cross-Site Scripting).
2. Secure
- Behavior: Restricts the browser to sending the cookie only over encrypted connections (HTTPS). If you query the site over plain HTTP, the browser blocks the cookie.
- Defense: Prevents cookie theft via packet sniffing (Man-in-the-Middle attacks) on public networks.
3. SameSite
- Behavior: Restricts when cookies are sent along with cross-site requests (e.g. links from external sites).
- Defense: The primary browser defense against CSRF (Cross-Site Request Forgery).
- Values:
SameSite=Strict: The cookie is never sent on cross-site requests (e.g. even if you click a link from Google to your bank, you will load the page logged out).SameSite=Lax(Default in modern browsers): The cookie is sent on cross-site requests only during safe, top-level navigations (e.g. clicking a regular<a>link), but blocked on background requests (like<img>source loads orfetch()calls from external pages).SameSite=None: The cookie is sent on all cross-site requests. Requires theSecureattribute to be set.
(2) Reality Metaphor
Imagine carrying a physical security badge in your pocket.
- Default Cookie: A plain badge. Anyone looking over your shoulder can read it (XSS), and if a stranger grabs your arm and pushes you into a building gate, the usher sees the badge in your pocket and lets you in (CSRF).
HttpOnly: Placing the badge inside a locked steel box in your pocket. You cannot take it out to read or show it to anyone on the street, but when you stand at the official gate (making a network request), a scanner reads the badge directly through the box steel.Secure: The badge is made of photo-luminescent material that only works under secure blue lighting (HTTPS). If someone tries to inspect it under standard street lights (HTTP), it appears blank.SameSite=Strict: You only show your badge if you walked directly from your own home to the office. If a tour guide from another company led you to the gate, you refuse to show it.
(3) HTTP Configuration Example
The backend server sets these attributes within the Set-Cookie response header:
HTTP/1.1 200 OK
Content-Type: application/json
Set-Cookie: session_id=xyz987654321; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=86400
4. Common Mistakes & Pitfalls
Mistake 1: Assuming HttpOnly protects against CSRF attacks
The mistake: Believing that because a cookie has the HttpOnly attribute enabled, it is safe from Cross-Site Request Forgery.
Why it's wrong: HttpOnly only prevents JavaScript from reading the cookie values. However, if a malicious website triggers a background POST request to your API, the browser still appends the cookie automatically to that request. The server will see the valid session cookie and process the request, completing the CSRF attack.
Fix: You must combine HttpOnly with the SameSite attribute or use anti-CSRF tokens.
Mistake 2: Omitting Secure Flag on Production Authentication Cookies
The mistake: Setting Set-Cookie: session=abc123 without the Secure flag on production HTTPS sites.
Why it's wrong: Without Secure, browsers will send the session cookie over unencrypted HTTP requests if a user visits http://yourdomain.com, exposing session tokens to network sniffing.
Incorrect:
Set-Cookie: sid=abc123; HttpOnly ; ❌ Missing Secure flag on HTTPS site!
Fix:
Set-Cookie: sid=abc123; HttpOnly; Secure; SameSite=Strict
Mistake 3: Setting SameSite=None Without the Secure Flag
The mistake: Writing Set-Cookie: token=xyz; SameSite=None without adding Secure.
Why it's wrong: Modern browsers reject SameSite=None cookies unless accompanied by the Secure attribute flag.
Incorrect:
Set-Cookie: token=xyz; SameSite=None ; ❌ Rejected by modern browsers!
Fix:
Set-Cookie: token=xyz; SameSite=None; Secure
5. Practice Exercises
Exercise 1: Secure HTTP Cookie Header Builder
Scenario: An authentication server constructs RFC 6265 compliant Set-Cookie response headers with strict security flags.
Requirements:
- Write buildSecureCookieHeader(name, value, options).
- Support HttpOnly, Secure, SameSite, Path, Max-Age.
Answer
Implementation
function buildSecureCookieHeader(name, value, options = {}) {
if (!name || value === undefined) throw new Error("Cookie name and value required");
const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];
if (options.maxAgeSeconds !== undefined) {
parts.push(`Max-Age=${options.maxAgeSeconds}`);
}
if (options.domain) {
parts.push(`Domain=${options.domain}`);
}
parts.push(`Path=${options.path || "/"}`);
if (options.sameSite) {
const ss = options.sameSite.toLowerCase();
const formattedSs = ss === "strict" ? "Strict" : ss === "lax" ? "Lax" : "None";
parts.push(`SameSite=${formattedSs}`);
} else {
parts.push("SameSite=Lax");
}
if (options.httpOnly !== false) {
parts.push("HttpOnly");
}
if (options.secure !== false) {
parts.push("Secure");
}
return parts.join("; ");
}
// Verification tests
const header = buildSecureCookieHeader("session_id", "xyz123", {
maxAgeSeconds: 3600,
sameSite: "strict"
});
console.assert(header.includes("session_id=xyz123"), "Test 1 Failed");
console.assert(header.includes("HttpOnly"), "Test 2 Failed");
console.assert(header.includes("Secure"), "Test 3 Failed");
console.assert(header.includes("SameSite=Strict"), "Test 4 Failed");
Technical Explanation
- HttpOnly Attribute: Prevents client-side JavaScript (document.cookie) from accessing the cookie, mitigating XSS attacks.
- Secure Attribute: Ensures cookie is ONLY transmitted over encrypted HTTPS connections.
- SameSite Attribute: Controls cross-site cookie transmission (
Strict,Lax,None) to prevent CSRF attacks.
Exercise 2: SameSite Anti-CSRF Policy Auditor
Scenario: An API security linter evaluates cookie attributes to ensure session cookies are protected against Cross-Site Request Forgery.
Requirements:
- Write auditSameSitePolicy(cookieHeaderStr).
- Check SameSite=Strict/Lax.
- Check HttpOnly and Secure.
Answer
Implementation
function auditSameSitePolicy(cookieHeaderStr) {
if (!cookieHeaderStr || typeof cookieHeaderStr !== "string") {
return { secure: false, risks: ["Missing Set-Cookie header"] };
}
const parts = cookieHeaderStr.split(";").map(p => p.trim().toLowerCase());
const risks = [];
const hasHttpOnly = parts.includes("httponly");
const hasSecure = parts.includes("secure");
let sameSiteValue = "none";
for (const part of parts) {
if (part.startsWith("samesite=")) {
sameSiteValue = part.split("=")[1];
}
}
if (!hasHttpOnly) risks.push("Cookie vulnerable to XSS theft (missing HttpOnly)");
if (!hasSecure) risks.push("Cookie sent over unencrypted HTTP (missing Secure)");
if (sameSiteValue === "none") risks.push("Cookie vulnerable to CSRF attacks (SameSite=None)");
return {
secure: risks.length === 0,
sameSiteValue,
risks
};
}
// Verification tests
const weakCookie = "session=123; Path=/; SameSite=None";
const audit = auditSameSitePolicy(weakCookie);
console.assert(audit.secure === false, "Test 1 Failed");
console.assert(audit.risks.length === 3, "Test 2 Failed: Identifies XSS, HTTPS, and CSRF risks");
Technical Explanation
- SameSite=Strict: Cookie is NEVER sent in cross-site requests (e.g. following external links).
- SameSite=Lax: Default browser policy: cookie sent on top-level GET navigation from external sites, but withheld on cross-site POSTs.
- SameSite=None Requirement: SameSite=None MUST be paired with Secure attribute (
SameSite=None; Secure).
Exercise 3: Cookie Max-Age vs Expires Format Converter
Scenario: Converts modern Max-Age (delta seconds) into UTC Expires header date strings (Wdy, DD-Mon-YYYY HH:MM:SS GMT).
Requirements:
- Write maxAgeToExpiresString(maxAgeSeconds).
- Return formatted UTC HTTP date string.
Answer
Implementation
function maxAgeToExpiresString(maxAgeSeconds) {
if (typeof maxAgeSeconds !== "number" || maxAgeSeconds < 0) {
return "Expires=Thu, 01 Jan 1970 00:00:00 GMT";
}
const expiryDate = new Date(Date.now() + maxAgeSeconds * 1000);
return `Expires=${expiryDate.toUTCString()}`;
}
// Verification tests
const expiresStr = maxAgeToExpiresString(3600);
console.assert(expiresStr.startsWith("Expires="), "Test 1 Failed");
console.assert(expiresStr.includes("GMT"), "Test 2 Failed");
const deleteStr = maxAgeToExpiresString(-1);
console.assert(deleteStr.includes("1970"), "Test 3 Failed: Negative Max-Age sets 1970 deletion date");
Technical Explanation
- Max-Age vs Expires: Max-Age specifies relative lifetime in seconds; Expires specifies absolute UTC date.
- Max-Age Precedence: If both attributes are present, modern browsers prioritize Max-Age over Expires.
- Deleting Cookies: Setting Max-Age=0 or an Expires date in the past immediately purges the cookie from browser storage.
6. Related Terms
- CSRF (Cross-Site Request Forgery) — The cross-origin vulnerability mitigated by SameSite attributes.
- XSS (Cross-Site Scripting) — The injection vulnerability mitigated by HttpOnly.
- Cookies — Related concept: Cookies.
7. Key Takeaways
- Cookie attributes configure security parameters directly inside
Set-Cookieheaders. HttpOnlyblocks JavaScript access, protecting cookies from XSS script theft.Securerestricts cookie transmission to encrypted HTTPS connections.SameSitecontrols cookie sending on cross-site requests, mitigating CSRF attacks.Laxis the browser default, allowing cookie sends only on top-level safe navigations.HttpOnlydoes not defend against CSRF; it must be paired withSameSiteconfigurations.