Use of popup windows when the destination cookie is HTTPOnly

60 views Asked by At

I have a Javascript script that opens a popup window passing a JWT token to a page.

This page uses the JWT token and creates a session, storing its ID in a cookie.

Once this is done, I am authenticated on the page.

It turns out that if I click again on the script that opens the popup, the JWT is passed again and the last cookie is "forgotten", it does not detect the previous session and redoes the entire authentication.

Is there any way for the previous cookie to remain? Is this caused by the cookie being HTTPOnly?

1

There are 1 answers

2
Tomi On

It is probably not related to HttpOnly, which only blocks access from JavaScript (hides it from the document.cookie property).

There is not enough information to solve it, but here are some ideas.

  • Are the original page and the popup on the same domain (origin)? If not, maybe it could be related to the SameSite attribute.

  • Are you sure the session cookie is not sent to the second popup? Can you check HTTP logs? (If you can't see it in Developer Tools because they open too late, either log it server side, or try using chrome://net-export/.)

  • Maybe the session cookie is sent, but the popup page doesn't check or read the cookie, it just unconditionally creates a new session.

  • Does it also happen when it's a normal page load instead of a popup?


To prove HttpOnly is not the problem, here is a tiny demo node.js server in which the popup can tell if you already have a cookie or not.

const http = require("http");

const server = http.createServer((req, res) => {
  if (req.url == "/") {
    res.writeHead(200, { "content-type": "text/html" });
    res.end(`
      <!DOCTYPE html>
      <h1>Hello</h1>
      <p>
      <button id="openpopup" type="button">Open a popup</button>
      <p>
      <button id="sendother" type="button">Send normal request</button>
      <p>
      <button id="showcookie" type="button">Show document.cookie</button>
      <scr`+`ipt>
      function log(msg) {
        const div = document.createElement("div");
        div.append(String(msg));
        document.body.append(div);
      }
      document.getElementById('openpopup').onclick = () => {
        window.open('/popup?jwt=' + encodeURIComponent(JSON.stringify({ "jwt_foo": "jwt_bar" })), '', 'width=500,height=500');
      };
      document.getElementById('sendother').onclick = () => {
        fetch('/normalrequest').then(r => r.json()).then(r => log(JSON.stringify(r)));
      };
      document.getElementById('showcookie').onclick = () => {
        log("document.cookie is: " + document.cookie);
      };
      window.onmessage = (event) => {
        log("Received message from popup: " + event.data);
      }
      </scr`+`ipt>
    `);
    return;
  }

  if (req.url.startsWith("/popup")) {
    if (req.headers.cookie && req.headers.cookie.match(/sessioncookie/)) {
      res.writeHead(200, { "content-type": "text/html" });
      res.end(`
        <p>You're already logged in! No cookie for you!
        <scr`+`ipt>window.opener.postMessage("No cookie for you");</scr`+`ipt>
      `);
    } else {
      const sessionid = String(Math.random());
      res.writeHead(200, { "content-type": "text/html", "set-cookie": "sessioncookie=" + sessionid + "; HttpOnly" });
      res.end(`
        <p>Thanks for the token! Your new session id is ${sessionid}.
        <scr`+`ipt>window.opener.postMessage("Received new session id");</scr`+`ipt>
      `);
    }
    return;
  }

  if (req.url == "/normalrequest") {
    const response = req.headers.cookie && req.headers.cookie.match(/sessioncookie/)
      ? { "status": "ok", "value": "You are logged in! Cookies: " + req.headers.cookie }
      : { "status": "error", "value": "Authentication required!" };
    res.writeHead(200, { "content-type": "application/json" });
    res.end(JSON.stringify(response));
    return;
  }

  res.writeHead(404, { "content-type": "text/html" });
  res.end("404 not found");
});

server.listen(5077);