I am running WordPress 5.3.2 on Apache/2.4.29 (Ubuntu) 18.04 on a Digital Ocean droplet.
My client requested the following:
All cookies transferred over an encrypted session, in particular session cookies, should be marked as 'Secure' and all session information should be transmitted over HTTPS.
The HttpOnly flag should also be set within the cookie
So, I defined the following in the virtual host:
Header edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure;SameSite=Strict
I then tested the header response and could see my Set-Cookie defined.
The problem is, I now can't login to WordPress. WordPress says:
ERROR: cookies are blocked or not supported by your browser. You must
enable cookies to use WordPress.
What am I doing wrong?
Strict is probably more restrictive than you want, as this will prevent cookies from being sent on initial cross-site navigations, e.g. if I emailed you a link to a page on your blog, when you first followed that link, the SameSite=Strict cookies would not be sent and it might appear as if you were not logged in.
SameSite=Lax is a better default here. Then I would explicitly look at setting SameSite=Strict or SameSite=None on individual cookies where you know the level of access required.
The HttpOnly attribute is also blanket preventing all of your server-side set cookies from being read by JavaScript. You may well have functionality on your page that requires this.
Finally, a blanket approach here is probably overkill - as it looks as if you will be appending this snippet to every outgoing cookie header, even the ones that already include those attributes. This is likely to cause some unpredictable behaviour. I would either do this on a specific allow-list basis by checking for explicit cookie names or I would alter the regex to only set this if those attributes are missing.
A late answer. But if it helps someone:
Put these values in php.ini
session.cookie_httponly = 1
session.cookie_secure = 1
Of course you should have a valid https certificate.
Related
I'm trying to figure out a way to make cookie-based authentication work, when the frontend and the backend are two different subdomains, without exposing myself to the CSRF threat.
Currently, i'm resorting to setting the cookie attribute sameSite to "None". Problem is, that this totally exposes the site to CSRF attacks.
For example,this is my cookie with the sameSite set to "lax":
cookieName=cookieValue; expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/; httponly; SameSite=Lax; Secure; Domain=some-sub-domain.localhost;
If my React app runs on locahost:3000, which is the same domain but different subdomain(and port..), this works only with sameSite=None. If sameSite is changed to "lax" or "strict", i get the following error, in the network tab, next to the set-cookie header:
This attempt to set a cookie via Set-Cookie header was blocked because it had the "SameSite=Lax" attribute but came from a cross-site response which was not the response to a top-level navigation.
I tried every possible configuration(including ".localhost"), and the only thing that seems to work is setting the SameSite to "None", but this would totally expose my site to CSRF attacks(assuming i do not incorporate some CSRF token, which is a different story).
My idea is to simply allow cookies to travel between subdomains, while blocking them on different domains. I mean, any subdomain would eventually be under my control.
Can it be done?
If anybody is interested, i found the "problem":
It was "localhost". It's not a "normal" domain. It's not localhost.com, or localhost.net- it's just plain localhost.
Therefore, setting up "subdomains" like frontend.localhost and backend.localhost doesn't work(unless you use sameSite=none), because those are actually totally different domains, not subdomains
To overcome this issue locally, you should set up virtual hosts in your operating system's hosts file. Something like frontend.local.xyz and backend.local.xyz. "local.xyz" is the actual domain in this case, and the others are subdomains. This solves the "problem", and allows a "strict" cookie to travel between them.
Note that this would still NOT work, if your domain is in this list:
https://publicsuffix.org/list/public_suffix_list.dat
I have a web server which returns 200 OK with a bunch of set-cookies, and an HTML page which loads a bunch of scripts from the same server.
However, the subsequent loads that was spawned from that HTML page submits a different cookie on their HTTP request headers.
What could be causing that? Surely there's some policy I'm missing out on, but I don't see why it works on some pages and not others?
I'm using chrome as the browser, but this behavior also happened from iOS, so I'm guessing it's not browser specific.
So after a lot more reading and troubleshooting, it turns out that when you don't set a cookie path, it'll default to whatever path the original request set-cookie was sent to. And because my resource paths had a different path, the cookie was not sent.
Adding Path=/ fixed it for my issue. Of course, if you don't want your cookie to be accessible to all pages this is bad, but my web-server requires requests to come with cookies because they are sensitive data (for security reasons).
I have a company website that's hosted as https://foo.bar.com.
However, it was incorrectly conveyed to a lot of users that the URL would be www.foo.bar.com. Until this can be rectified, we are putting through an interim solution by setting up a proxy site www.foo.bar.com that will redirect any users coming to it to https://foo.bar.com.
This works... but only the first time the user navigates to the page. The next time I try to access www.foo.bar.com, due to caching, the browser takes me to https://www.foo.bar.com. We don't have a certificate set up for https://www.foo.bar.com and as a result are given a NET::ERR_CERT_COMMON_NAME_INVALID error.
Is there a way to work around this without needing a certificate?
To test, I've even tried returning a webpage when the I navigate to www.foo.bar.com with a link that navigates to https://foo.bar.com. However, the same issue happens even in this case. I'm guessing HSTS is at play here but not sure how to go about it.
I'd appreciate any insight into this matter, thank you in advance.
I belive the only solution to your problem is to obtain a valid certificate for www.foo.bar.com. Due to the certificate error the browsers will not attempt to communicate with your server so there's no way for you to issue a redirect away from wrong domain to the correct domain.
Why only the second time?
You mention HSTS so I am assuming https://foo.bar.com is sending a Strict-Transport-Security header as part of it's response. This header likely is being sent with the includeSubDomains option which instructs the browser to not only enforce HTTPS on foo.bar.com but also all subdomains of that main domain. As a result, when trying to request www.foo.bar.com the browser matches that HSTS rule and automatically re-writes it to use HTTPS.
Once this HSTS rule has been set in the browser it cannot be removed except by expiring, either by exceeing the original max-age time or by issuing another Strict-transport-security header with max-age=0 on https://foo.bar.com
If evil.example.com sets a cookie with a domain attribute set to .example.com, a browser will include this cookie in requests to foo.example.com.
The Tangled Web notes that for foo.example.com such cookie is largely indistinguishable from cookies set by foo.example.com. But according to the RFC, the domain attribute of a cookie should be sent to the server, which would make it possible for foo.example.com to distinguish and reject a cookie that was set by evil.example.com.
What is the state of current browsers implementations? Is domain sent back with cookies?
RFC 2109 and RFC 2965 were historical attempts to standardise the handling of cookies. Unfortunately they bore no resemblance to what browsers actually do, and should be completely ignored.
Real-world behaviour was primarily defined by the original Netscape cookie_spec, but this was highly deficient as a specification, which has resulting in a range of browser differences, around -
what date formats are accepted;
how cookies with the same name are handled when more than one match;
how non-ASCII characters work (or don't work);
quoting/escapes;
how domain matching is done.
RFC 6265 is an attempt to clean up this mess and definitively codify what browsers should aim to do. It doesn't say browsers should send domain or path, because no browser in history has ever done that.
Because you can't detect that a cookie comes from a parent domain(*), you have to take care with your hostnames to avoid overlapping domains if you want to keep your cookies separate - in particular for IE, where even if you don't set domain, a cookie set on example.com will always inherit into foo.example.com.
So: don't use a 'no-www' hostname for your site if you think you might ever want a subdomain with separate cookies in the future (that shouldn't be able to read sensitive cookies from its parent); and if you really need a completely separate cookie context, to prevent evil.example.com injecting cookie values into other example.com sites, then you have no choice but to use completely separate domain names.
An alternative that might be effective against some attack models would be to sign every cookie value you produce, for example using an HMAC.
*: there is kind of a way. Try deleting the cookie with the same domain and path settings as the cookie you want. If the cookie disappears when you do so, then it must have had those domain and path settings, so the original cookie was OK. If there is still a cookie there, it comes from somewhere else and you can ignore it. This is inconvenient, impractical to do without JavaScript, and not watertight because in principle the attacker could be deleting their injected cookies at the same time.
The current standard for cookies is RFC 6265. This version has simplified the Cookie: header. The browser just sends cookie name=value pairs, not attributes like the domain. See section 4.2 of the RFC for the specification of this header. Also, see section 8.6, Weak Integrity, where it discusses the fact that foo.example.com can set a cookie for .example.com, and bar.example.com won't be able to tell that it's not a cookie it set itself.
I would believe Zalewski's book and his https://code.google.com/p/browsersec/wiki/Main over any RFCs. Browsers' implementations of a number of HTTP features are notoriously messy and non-standard.
I've run into a problem where login on my site is not working consistently in IE8. It works fine in Firefox/Chrome/Safari but not IE8.
On first login everything is fine. After logging out and trying to log back in it usually fails for a while. It will end up redirecting to the login page against after authentication.
So the authentication is successful, it returns true, but it seems IE8 is not accepting the new value for the session id which we set by returning:
Set-Cookie SESSIONID=........; Path=/
in the response header. But obviously this works with a clear cache and I can get in fine. But after its already there it fails to reset, so after authenticating and attempting to go to a new page it sees this is an old session id being sent from the browser and redirects to the login page.
I haven't found anything here or elsewhere that really solves this (besides clearing the cache). Most references to IE8 cookie problems are language/framework specific and don't answer this problem.
Is there something special I might need to do with the set-cookie to make this work?
UPDATE:
I've set IE8 to prompt before accepting any cookie. When the login works fine it prompts as expected. When it does not work there isn't even a prompt to except the cookie..
UPDATE 2:
I should have mentioned that the cookie is expected to be set after an ajax call:
$.get(authenticate_url, ....)
The url it requests a response from returns the header that sets the session id, then in the callback function the user is redirected to the main page -- assuming login is successful.
Is it a first party cookie or a 3rd party cookie. If the latter, ensure you're sending a P3P header.
Are you setting the HTTPOnly attribute?
Are you sure that the domain for the cookie is always the same? E.g. if you set the cookie when visiting "example.com" and tried to change it from "www.example.com" then you'll encounter problems.
I've hit similar symptoms when I have cookies set for both www.example.com and example.com. If there's no domain explicitly set, then session cookies get set for both.
However, higher-level domain cookies take precedence over the lower domian. So, if www.example.com tries to set a cookie, but example.com already has, then the example.com cookie stays in place and continues to apply for www.example.com.
There are two ways to approach this problem. One is not to allow access both with and without the www subdomain. Redirect one to the other. The second is to explicitly set the cookie domain, so that there aren't two versions lying around.
Of course, that might not actually be your issue. Experiment and find out :)