Technology Sep 22, 2026 · 9 min read

Your Session Cookie Is Basically a Temporary Password - Part 2

In Part 1, we looked at the basics: How sessions work, why cookies matter, and why attributes like HttpOnly, Secure, and SameSite are important. But secure cookie settings are only one part of session security. A session has a lifecycle. It is created, authenticated, used, refreshed, expired, an...

DE
DEV Community
by khg5293
Your Session Cookie Is Basically a Temporary Password - Part 2

In Part 1, we looked at the basics:

How sessions work, why cookies matter, and why attributes like HttpOnly, Secure, and SameSite are important.

But secure cookie settings are only one part of session security.

A session has a lifecycle.

It is created, authenticated, used, refreshed, expired, and eventually destroyed.

Problems can happen at any point in that lifecycle.

In this post, we'll look at some of the less obvious session security issues developers should understand.

CSRF: When the Browser Helps the Attacker

One of the strange things about cookies is that browsers send them automatically.

That is convenient for users.

It is also exactly why CSRF exists.

Imagine a user is logged into:

bank.example

Their browser has:

Cookie: session_id=a8f19d27c4e91b72

Now the user visits:

evil.example

That page contains something like:

<form action="https://bank.example/transfer" method="POST">
  <input type="hidden" name="amount" value="500">
  <input type="hidden" name="to" value="attacker">
</form>

<script>
  document.forms[0].submit();
</script>

The attacker does not need to know the victim's session cookie.

The browser may attach it automatically when sending the request to bank.example.

From the server's perspective, the request could arrive with a valid authenticated session.

That is the core idea behind Cross-Site Request Forgery.

The attacker is not stealing the session.

They are abusing the browser's ability to use it.

CSRF Tokens

One common defense is a CSRF token.

The application generates a random value associated with the user's session.

For example:

<form method="POST" action="/settings/email">
  <input
    type="hidden"
    name="csrf_token"
    value="9bf231ce89f5d10a"
  >

  <input
    type="email"
    name="email"
  >

  <button type="submit">
    Change email
  </button>
</form>

The server expects both:

A valid authenticated session

and

A valid CSRF token

If the attacker can cause the browser to submit a request but cannot obtain the correct CSRF token, the server can reject it.

Conceptually:

Session cookie
      +
CSRF token
      ↓
Request accepted

A missing or incorrect token should cause the request to fail.

SameSite cookies can also reduce CSRF risk, but it is useful to understand CSRF as its own problem rather than thinking one cookie flag solves everything.

Session Fixation

Another session attack is less intuitive.

It is called session fixation.

Normally, we think of an attacker trying to steal a session after login.

Session fixation reverses that idea.

The attacker tries to make the victim use a session ID the attacker already knows.

Imagine the attacker knows:

session_id=XYZ123

The victim somehow begins using that session.

Then the victim logs in.

If the application keeps the same session identifier after authentication:

Before login:
session_id=XYZ123

After login:
session_id=XYZ123

the attacker already knows the identifier of the authenticated session.

The attack flow looks like this:

Attacker knows session XYZ123
          ↓
Victim begins using XYZ123
          ↓
Victim logs in
          ↓
Server authenticates XYZ123
          ↓
Attacker still knows XYZ123

The password was never stolen.

The session was never guessed.

The problem was that the session identifier was not changed when authentication happened.

Regenerate the Session ID After Login

The defense is straightforward:

Generate a new session identifier when authentication state changes.

For example:

app.post("/login", async (req, res) => {
  const user = await authenticate(
    req.body.username,
    req.body.password
  );

  if (!user) {
    return res.status(401).send("Invalid credentials");
  }

  req.session.regenerate((error) => {
    if (error) {
      return res.sendStatus(500);
    }

    req.session.userId = user.id;
    req.session.username = "khg5293";

    res.redirect("/dashboard");
  });
});

The important transition is:

Unauthenticated session ID
          ↓
User logs in
          ↓
New session ID
          ↓
Authenticated session

The old identifier should not simply become the authenticated session.

Session rotation can also make sense after important privilege changes.

For example:

User
  ↓
Admin role granted
  ↓
Rotate session ID

A session should not quietly carry sensitive state transitions forever.

Session Expiration

Sessions should not remain valid indefinitely.

If a session ID is stolen today and still works six months later, the attacker gets a very large window of opportunity.

Two common expiration strategies are:

Idle Timeout

The session expires after a period of inactivity.

For example:

Last activity: 10:00 AM

No activity for 30 minutes

Session expires at 10:30 AM

If the user makes another legitimate request before the timeout:

10:00 AM request
10:20 AM request
10:40 AM request

the idle timer may be refreshed.

This protects against sessions that are abandoned but never explicitly logged out.

Absolute Timeout

An absolute timeout limits the total lifetime of the session.

For example:

Login: 9:00 AM

Absolute lifetime: 8 hours

Session expires: 5:00 PM

Even if the user remains active, the session eventually ends.

This creates an upper bound on how long the same authenticated session can exist.

A system may use both:

Idle timeout:
30 minutes

Absolute timeout:
8 hours

The correct values depend on the application.

A low-risk community forum and an online banking system should not necessarily use the same session lifetime.

Remember Me Changes the Risk

Many applications offer a feature like:

Remember me

That can be useful, but developers should understand what it means from a security perspective.

A normal session might last:

30 minutes

while a persistent login might last:

30 days

That dramatically increases the usefulness of a stolen token.

If long-lived authentication is required, it is often better to treat it as a separate mechanism rather than simply making the normal session live forever.

For example:

Short-lived session
       +
Long-lived reauthentication token

The persistent token can be separately rotated, revoked, and monitored.

Longer lifetime should always be treated as increased exposure.

Logout Should Destroy the Session

A common logout implementation is:

res.clearCookie("session_id");
res.redirect("/");

That removes the cookie from the user's browser.

But ask an important question:

What happened to the session on the server?

Possibly nothing.

Imagine the server still contains:

sessions["a8f19d27c4e91b72"] = {
  userId: "khg5293",
  authenticated: true
};

The original browser may no longer have the cookie.

But if an attacker already copied the session identifier, that copy might still work.

Logout should normally invalidate the server-side session too.

For example:

app.post("/logout", (req, res) => {
  req.session.destroy((error) => {
    if (error) {
      return res.sendStatus(500);
    }

    res.clearCookie("session_id");

    res.redirect("/");
  });
});

The goal is:

Delete browser cookie
        +
Destroy server session

Both matter.

Password Changes Should Affect Existing Sessions

Another important lifecycle question is:

What happens to existing sessions when the password changes?

Imagine this situation:

Laptop session
Phone session
Tablet session
Unknown attacker session

The user changes their password.

If all existing sessions remain valid, the attacker's session may continue working.

Depending on the application, a password reset or other major account security event may justify invalidating other sessions.

For example:

Password reset
      ↓
Invalidate old sessions
      ↓
Require new authentication

This is especially important after suspected account compromise.

Session Revocation Matters

Expiration answers:

When should this session naturally stop working?

Revocation answers:

How can we make this session stop working right now?

Useful scenarios include:

User clicks "log out everywhere"

Password reset

Account locked

Admin disables account

Suspicious login detected

User removes a trusted device

A server-side session store makes this relatively straightforward.

If the session no longer exists:

session_id=a8f19d27c4e91b72

the server should reject it.

For example:

HTTP/1.1 401 Unauthorized

A session is only useful while the server still considers it valid.

Don't Put Session IDs in URLs

This is worth stating directly.

Do not design authenticated URLs like:

https://example.com/account?session_id=a8f19d27c4e91b72

URLs leak into many places.

For example:

Browser history

Server logs

Proxy logs

Analytics systems

Screenshots

Copied links

Bookmarks

Referer headers

A URL is a terrible place for a bearer credential.

Authentication tokens belong in mechanisms designed to transport them safely.

For traditional browser sessions, that generally means cookies.

Avoid Logging Session Tokens

The same idea applies to application logging.

This is useful:

2026-09-15T21:42:11Z
event=login_success
user=khg5293
source_ip=192.0.2.18

This is dangerous:

2026-09-15T21:42:11Z
event=login_success
user=khg5293
session_id=a8f19d27c4e91b72

Logs are often copied, indexed, retained, exported, and accessed by many systems.

A live session identifier appearing in logs can become a credential leak.

Application logs should avoid exposing secrets unnecessarily.

Keep Cookie Scope Narrow

Cookie attributes can control where a cookie is sent.

For example:

Set-Cookie: session_id=abc123; Path=/

sends the cookie more broadly than:

Set-Cookie: session_id=abc123; Path=/account

The correct scope depends on the application.

The same idea applies to domains.

An overly broad cookie configuration can cause a credential to be sent to places that do not actually need it.

A useful security principle is:

Give credentials the smallest scope they actually require.

That applies to cookies too.

Avoid Unnecessary Session Data in the Browser

In a traditional server-side session architecture, the browser does not need to know everything about the session.

This is usually enough:

Cookie: session_id=a8f19d27c4e91b72

The server can associate that identifier with:

{
  userId: "khg5293",
  role: "user",
  permissions: ["profile:read"],
  authenticated: true
}

There is usually no benefit in exposing sensitive server-side session state to the browser unnecessarily.

Keep the client-side value minimal.

Treat Session Security as a Lifecycle

It is easy to focus entirely on login.

But a secure session has many stages.

Session created
      ↓
User authenticates
      ↓
Session ID rotated
      ↓
Session used
      ↓
Session monitored
      ↓
Session expires or is revoked
      ↓
Session destroyed

Security decisions exist at every step.

If any one step is poorly designed, the session can become easier to abuse.

A More Complete Session Security Checklist

For a traditional web application:

[ ] Generate unpredictable session IDs

[ ] Rotate session IDs after authentication

[ ] Rotate again after major privilege changes where appropriate

[ ] Use HttpOnly

[ ] Use Secure

[ ] Choose an appropriate SameSite policy

[ ] Protect state-changing requests against CSRF

[ ] Implement idle expiration

[ ] Consider an absolute session lifetime

[ ] Invalidate sessions during logout

[ ] Revoke sessions after important account security events

[ ] Support session revocation where appropriate

[ ] Never place session IDs in URLs

[ ] Avoid logging authentication tokens

[ ] Keep cookie scope as narrow as practical

[ ] Avoid exposing unnecessary session state to the browser

Final Thought

Session security is not just about protecting one cookie.

It is about controlling the entire lifecycle of an authenticated identity.

The session has to be created securely.

It has to change when authentication changes.

It has to expire.

It has to be revocable.

And when the user logs out, it should actually stop working.

The safest way to think about a session ID is still the simplest one:

If someone possesses this value,
what can they do?

If the answer is:

Act as the authenticated user

then that session deserves the same care as any other credential.

Because once the password has already done its job, the session becomes the thing standing between the user and everyone else.

DE
Source

This article was originally published by DEV Community and written by khg5293.

Read original article on DEV Community
Back to Discover

Reading List