Are your session cookies actually protected? (HttpOnly, Secure, SameSite)
Your session cookie is the thing that says "this is a logged-in user." Steal it and you are that user, no password required. When your server set that cookie on the last login, which flags went out with it? If you can't answer from memory, that's the whole reason to read the header.
Three small attributes decide whether the cookie is locked down or sitting in the open. Most frameworks set sensible defaults now, but "most" and "now" are doing heavy lifting in that sentence, and the cookie you set by hand three releases ago doesn't know about the defaults.
HttpOnly: the difference between a bug and a breach
HttpOnly keeps JavaScript away from the cookie. With it set, document.cookie simply doesn't return the value; the cookie goes up with requests automatically but the page's script can't read it. Without it, any JavaScript running on your page can read the session token and send it anywhere.
That second clause is the whole game, because "any JavaScript running on your page" includes injected JavaScript. The moment you have a cross-site scripting hole, an unescaped comment, a reflected parameter, a compromised third-party script, an attacker's code runs in your page. If the session cookie is readable, that code grabs it and ships it off in a single line, and now the attacker has a logged-in session that never touched your login form.
This is why HttpOnly is the flag that matters most for a session cookie. It doesn't fix XSS, but it downgrades the consequence from "attacker reads your session" to "attacker runs script but can't lift the token." That gap is the difference between an incident and a catastrophe.
Secure and SameSite: the other two
Secure tells the browser to send the cookie only over HTTPS. Without it, the cookie rides along on any plaintext request, including the ones an attacker on a shared network is waiting for. If http:// still serves anything on your domain, a Secure-less session cookie can leak on that first plaintext request before any redirect fires. This is the cookie-level reason that plain HTTP still answering is dangerous.
SameSite controls whether the cookie gets attached to requests coming from other sites, which is your defense against cross-site request forgery. SameSite=Lax is a sane default and stops the cookie riding along with most cross-site requests. SameSite=Strict is tighter. The trap is SameSite=None, which is sometimes genuinely needed for cross-site embeds, but None without Secure is rejected by modern browsers outright, and None set carelessly hands your cookie back into CSRF range. If you set None, you must set Secure, and you should know exactly why you needed it.
How to check
Read the header your server sends when it logs someone in.
curl -sI https://yoursite.com/login-or-whatever-sets-the-cookie | grep -i set-cookie. Look at the attributes after the cookie value.- Or open developer tools, go to Application then Cookies, find your session cookie, and read the HttpOnly, Secure, and SameSite columns. The HttpOnly column being unchecked is the one to fear.
- Check the session cookie specifically. A non-session cookie without flags is usually fine; the session token is the prize.
The good news is you don't have to find the XSS bug to know you're exposed to one. The flags are right there in the Set-Cookie header, which is public, so this is checkable from outside without touching your code. SurfaceCheckr requests a page, reads the response headers the way any browser would, and flags the session-shaped cookies that came back without HttpOnly, Secure, or a sane SameSite. We don't probe your auth or go looking for the injection point that would weaponize it. We tell you the token is readable; you should assume a way to read it will turn up eventually.
The fix
These are attributes on the cookie, set where you create the session. One line, set right once.
// Express: a session cookie wide open
res.cookie("session", token, {
path: "/",
});
// readable from JS, sent over http, attached cross-siteWhatever your stack, the names are the same idea: httpOnly, secure, sameSite. Set them on the session cookie and confirm the rendered Set-Cookie header carries all three. The defense pairs naturally with a CSP that actually blocks injected scripts, because between them you've cut both the ability to run a token-stealing script and the ability to read the token if one runs.
Run the curl line against the response that sets your session and read the attributes. If HttpOnly isn't there, your next XSS bug is an account takeover.
Find it before someone else does.
Paste your domain. The grade and issue count are free, and you'll see in a couple of minutes exactly what's reachable from outside.