phphttpcookiessame-origin-policy

Same cookie gets set at different localhost origins


I've two PHP apps one on localhost:8080 (vuln) and the other on localhost:42069 (attacker).

8080 has a login page, which on login saves a cookie.

42069 sets no cookies (has no PHP code) and only has an HTML form that logs the user out of 8080 using a logout POST request.

When I log document.cookie at 42069 after logging into 8080 I get to see the same cookie as 8080.

What's going on here?

App sources repo. Run the app with start.sh.


Solution

  • This is because both sites have the same domain localhost. Your Browser stores cookies for each domain which is determined by the part after the protocol(http, https) and before the first /. In your case localhost:8080 and localhost:42069 are considered to be the same domain. Cookies set by on page will be available to the other page as well.

    This behavior is defined in RFC6265

    1.Introduction

    [...]

    For historical reasons, cookies contain a number of security and privacy infelicities. For example, a server can indicate that a given cookie is intended for "secure" connections, but the Secure attribute does not provide integrity in the presence of an active network attacker. Similarly, cookies for a given host are shared across all the ports on that host, even though the usual "same-origin policy" used by web browsers isolates content retrieved via different ports.

    And

    8.5. Weak Confidentiality

    Cookies do not provide isolation by port. If a cookie is readable by a service running on one port, the cookie is also readable by a service running on another port of the same server.

    If you want the two apps to have separate cookies you could try to use subdomains or different top-level domains: E.g. app1.localhost:8080 and app2.localhost:42069. This may cause the browser to store the cookies separatly.

    Note this is not guaranteed to work see here

    8.6. Weak Integrity

    Cookies do not provide integrity guarantees for sibling domains (and their subdomains). For example, consider foo.example.com and bar.example.com. The foo.example.com server can set a cookie with a Domain attribute of "example.com" (possibly overwriting an existing "example.com" cookie set by bar.example.com), and the user agent will include that cookie in HTTP requests to bar.example.com. In the worst case, bar.example.com will be unable to distinguish this cookie from a cookie it set itself. The foo.example.com server might be able to leverage this ability to mount an attack against bar.example.com.

    Guaranteed Solution

    You can run one page on localhost and the other on 127.0.0.1 or 127.0.0.2, etc. Because these are two different domains. Means a cookie on localhost cannot be shared with 127.0.0.x and vice versa.

    #!/bin/bash
    if ! ( which php > /dev/null ); then
        echo 'error: php not installed'
        exit 1
    fi
    
    php -S localhost:8080 --docroot=website-vuln/public/ 2>> vuln.log >> vuln.log &
    php -S 127.0.0.1:42069 --docroot=website-evil/public/ 2>> evil.log >> evil.log &
    
    echo vuln server @ http://localhost:8080
    echo evil server @ http://127.0.0.1:42069
    

    On Linux /etc/hosts can be used to create more cookie domains for 127.0.0.1 than localhost. And as many meaningfull names for 127.0.0.x

    E.g. /etc/hosts

    127.0.0.1    app1
    127.0.0.1    app2
    

    Then you can modify your script to:

    #!/bin/bash
    if ! ( which php > /dev/null ); then
        echo 'error: php not installed'
        exit 1
    fi
    
    php -S app1:8080 --docroot=website-vuln/public/ 2>> vuln.log >> vuln.log &
    php -S app2:42069 --docroot=website-evil/public/ 2>> evil.log >> evil.log &
    
    echo vuln server @ http://app1:8080
    echo evil server @ http://app2:42069
    

    Now you have to change the link in your form:

    <form method="POST" action="http://app1:8080/">
    

    In Windows 10 the hosts file is located at c:\Windows\System32\Drivers\etc\hosts.