Search code examples
phpcookiessession-cookiessetcookiewebsecurity

Secure cookies available via HTTP


I noticed something earlier today while inspecting cookies on some of my subdomains that surprised me a little. Although I have set PHP to use only secure cookies, they are nonetheless available using HTTP.

My root domain and most of my subdomains are HTTPS. In fact, they are essentially HTTPS only. I haven't enabled HSTS because I have subdomains that are HTTP only and do not support HTTPS. (Essentially, my domains are either HTTPS only or HTTP only, not both.) While navigating to these subdomains, I noticed the cookies set by the root domain show up in the browser developer tools, and are seemingly sent on requests to the server.

I'd prefer this not happen because a) it's insecure and b) the server handling these subdomains does squat with these cookies so sending them at all is an unnecessary risk that could compromise the main applications.

The weird thing is my cookies are already secure. All cookies are set like so:

setCookie("my_cookie", $cookieValue, $expires, '/', $domain, true, true);

At the top of my session manager, I also have:

session_set_cookie_params(3600, '/', '.example.com', true, true);

The last trues are secure and httpOnly. One would think this makes them HTTP-only:

HTTPS: Cookie with "Secure" will be returned only on HTTPS connections Reading cookies via HTTPS that were set using HTTP

To ensure that the session cookie is sent only on HTTPS connections, you can use the function session_set_cookie_params() before starting the session: https://stackoverflow.com/a/6531754/6110631

A cookie with the Secure attribute is sent to the server only with an encrypted request over the HTTPS protocol, never with unsecured HTTP, and therefore can't easily be accessed by a man-in-the-middle attacker. Insecure sites (with http: in the URL) can't set cookies with the Secure attribute. However, do not assume that Secure prevents all access to sensitive information in cookies; for example, it can be read by someone with access to the client's hard disk. https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Secure_and_HttpOnly_cookies

But lo and behold, all cookies that I set on the root domain continue to be available on my HTTP-only subdomains. Using the developer tools, any changes with cookies in the root domain continue to be reflected on HTTP-only subdomains!

I intentionally set the cookie as .domain to make it available on all subdomains, since they all share session information and enable SSO (the HTTPS domains, that is). However, I would think that with the secure flag, this would still prevent the cookies from being available on HTTP-only subdomains. Does one of these parameters take precedence over another? (I would think secure would).

Why is this not working as intended? It seems that because the cookies are available, even though I have secure and httpOnly, the cookies could be stolen from an unencrypted HTTP connection. Is it that the cookies are not actually sent, but the browser (erroneously) displays them as present anyways, or is there a real security risk here?


Solution

  • The browser developer tools seems to make a distinction between displaying cookies that may be available on a domain and those that are actually sent in the request - that is to say, the developer tools will show cookies for a subdomain even if they are never sent on requests to the server.

    Here's an example of a request to the root domain:

    :authority: example.com
    :method: GET
    :path: /account
    :scheme: https
    accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
    accept-encoding: gzip, deflate, br
    accept-language: en-US,en;q=0.9
    cache-control: max-age=0
    cookie: [cookies redacted]
    dnt: 1
    referer: [redacted]
    upgrade-insecure-requests: 1
    user-agent: [redacted]
    

    Here's an example of a request to an HTTP-only subdomain:

    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
    Accept-Encoding: gzip, deflate
    Accept-Language: en-US,en;q=0.9
    Cache-Control: max-age=0
    Connection: keep-alive
    DNT: 1
    Host: subdomain.example.com
    If-Modified-Since: Wed, 27 May 2020 20:51:35 GMT
    If-None-Match: "6c5-5a6a760afe782-gzip"
    Upgrade-Insecure-Requests: 1
    User-Agent: [redacted]
    

    As one can see, cookies are not sent with the second request. However, if you examine the cookies on both domains in the browser, you can see the browser displays them regardless:

    enter image description here

    enter image description here

    This can be a source of confusion, but examining the request headers shows that the browser does indeed refrain from sending the secure cookies on insecure requests.