This vulnerability occurs when an application sets a sensitive cookie (especially session identifiers or authentication tokens) without enabling the HttpOnly attribute. The HttpOnly flag instructs the browser not to allow client-side scripts (like JavaScript) to access the cookie via document.cookie. If this flag is missing, and an attacker successfully executes a Cross-Site Scripting (XSS) payload on the site, they can easily steal the sensitive cookie, leading to session hijacking. 🍪🚫📜➡️👤
Missing the HttpOnly flag on sensitive cookies significantly increases the impact of XSS vulnerabilities:
Session Hijacking: An XSS attack can steal the session cookie, allowing the attacker to completely impersonate the user without needing their password.
Token Theft: Other sensitive tokens stored in cookies (e.g., anti-CSRF tokens, API access tokens if stored this way) can be stolen and potentially misused.
Setting HttpOnly is a crucial defense-in-depth measure against the consequences of XSS.
Reference Details
CWE ID:CWE-1004OWASP Top 10 (2021): A05:2021 - Security Misconfiguration
Severity: Medium (Increases the impact of XSS)
This is a configuration issue related to how cookies are set. Most modern web frameworks either enable HttpOnly by default for their session cookies or provide a clear configuration option to do so. The vulnerability typically arises when:
The framework default is insecure and not overridden.
Developers manually set sensitive cookies without including the HttpOnly flag.
Remediation:
Configure Framework Defaults: Ensure the framework’s session cookie configuration explicitly sets HttpOnly to true.
Set Flag on Custom Cookies: When creating any cookie that contains sensitive data or is used for session management/authentication, always include the HttpOnly attribute. Cookies needed purely for client-side JavaScript manipulation should not contain sensitive data.
# settings.py (Production)# DANGEROUS: Session cookie accessible via document.cookie in JavaScript.# If an XSS vulnerability exists, the session ID can be stolen easily.SESSION_COOKIE_HTTPONLY = False
Django: Ensure SESSION_COOKIE_HTTPONLY = True (this is the default and should generally not be changed). Consider CSRF_COOKIE_HTTPONLY = False (default) if your frontend JavaScript needs to read the CSRF token from the cookie, but ensure your session cookie is HttpOnly.
Flask: Always add httponly=True when calling response.set_cookie for any sensitive cookie. Configure session extensions to use HttpOnly.
# settings.py (Django Production - Secure Default)# SECURE: HttpOnly is True by default for session cookies.# SESSION_COOKIE_HTTPONLY = True # Explicitly setting is fine too.# CSRF token often needs to be readable by JS for AJAXCSRF_COOKIE_HTTPONLY = False # Default and often necessary
Access your site over HTTPS and log in. Use browser developer tools (Application/Storage tab) to inspect the session cookie (sessionid in Django, connect.sid often in Flask/Node) and any custom authentication cookies. Verify that the “HttpOnly” column/flag is checked/true. Try accessing document.cookie in the browser’s JavaScript console; sensitive cookies should not appear in the list.
Vulnerable Scenario 1: cookie.http-only=false in Properties
# application.properties (Production)# DANGEROUS: JSESSIONID cookie will be accessible via document.cookie.server.servlet.session.cookie.http-only=false
# application-production.properties (Secure - Default)# SECURE: HttpOnly is true by default. Explicitly setting is also fine.server.servlet.session.cookie.http-only=true# Also recommended:server.servlet.session.cookie.secure=trueserver.servlet.session.cookie.same-site=Strict
// controller/AuthController.java (Secure Custom Cookie)@PostMapping("/issue-auth-cookie-secure")public String issueAuthCookieSecure(HttpServletResponse response, HttpServletRequest request) { String token = generateSensitiveToken(); Cookie authCookie = new Cookie("authToken", token); // SECURE: Added setHttpOnly(true). authCookie.setHttpOnly(true); authCookie.setSecure(request.isSecure()); // Also set Secure flag authCookie.setPath("/"); // Consider SameSite=Strict as well // authCookie.setAttribute("SameSite", "Strict"); // Via setHeader or specific servlet API version response.addCookie(authCookie); return "tokenIssued";}
Access the site over HTTPS. Use browser developer tools to inspect the JSESSIONID cookie and any custom sensitive cookies. Verify the “HttpOnly” flag is set. Try reading document.cookie via the browser console; the sensitive cookies should not be listed.
Vulnerable Scenario 2: Manual Cookie Creation without HttpOnly = true
// Controllers/TrackingController.cspublic IActionResult SetTrackingId(){ string trackingId = Guid.NewGuid().ToString(); // DANGEROUS: HttpOnly flag is missing or false. // If trackingId is sensitive or guessable, XSS could reveal it. Response.Cookies.Append("trackingId", trackingId, new CookieOptions { Secure = true, SameSite = SameSiteMode.Strict, Expires = DateTimeOffset.UtcNow.AddYears(1) // HttpOnly = true, // This is missing }); return Ok();}
Ensure Cookie.HttpOnly is set to true (the default) for session and authentication cookies in Startup.cs. When manually appending cookies via Response.Cookies.Append, set CookieOptions.HttpOnly = true for any cookie containing sensitive data or used for security purposes.
Access the site over HTTPS. Use browser developer tools to inspect session (.MyApp.Session) and authentication (.AspNetCore.Identity.Application) cookies. Verify the “HttpOnly” flag is set. Check any custom cookies. Try reading document.cookie via the browser console; HttpOnly cookies should not be visible.
Controlled by session.cookie_httponly directive in php.ini or ini_set(), and the httponly parameter in setcookie() / session_set_cookie_params(). Laravel controls this via config/session.php.
Access the site over HTTPS. Use browser developer tools to inspect the PHPSESSID, laravel_session, and any custom sensitive cookies. Verify the “HttpOnly” flag is set. Try reading document.cookie via the browser console; HttpOnly cookies should not appear. Use phpinfo() (if accessible) to check session.cookie_httponly.
Access the site over HTTPS. Inspect the session cookie (connect.sid or custom name) and other sensitive cookies using browser developer tools. Verify the “HttpOnly” flag is set. Attempt to read document.cookie in the console; HttpOnly cookies should be absent.
Controlled by the :httponly option in config/initializers/session_store.rb or when setting cookies manually via the cookies helper. Rails defaults generally include httponly: true for session cookies.
Access the site over HTTPS. Use browser developer tools to inspect the session cookie (_my_app_session) and any custom sensitive cookies. Verify the “HttpOnly” flag is set. Try reading document.cookie in the console; HttpOnly cookies should not be visible.