The argument that “no XSS is possible on my site” is both besides the point and utterly wrong. (I am not attributing this argument to you, just enumerating)
The argument that “it’s all the same once the client is compromised” is also wrong, for the numbered reasons above.
It seems to me that some people are arguing against the claim “Cookies are perfect”, but this is a straw man. The claim is: they are superior.
localStorage is simply not intended to store sensitive information, but cookies are (at least, by comparison). Why not “honor” the intention?
My proximate/initial motive in this thread is regarding the stance taken up top, basically opposing the customer, instead of supporting. I mean, your chances of convincing the auditor are sub-zero, and also, maybe it should give us pause to hear what paid specialists are saying, and do we want Meteor to appeal to serious, corporate customers – or not? Even if the concern didn’t make sense (it does), accommodation seems like a productive response.
A cookie is sent on every HTTP request, so it is available on the server side. To use this pattern for Meteor, the login must be treated different than now, because this is done entirely using web sockets and the login method.
That’s right. What I meant was that Meteor can’t just use a monkey patch and switch to using httpOnly cookies in place of another client readable method (local storage or secure cookies). The client side app needs access to the auth token to send it with requests over DDP.
I wonder if a sort of half-duplex model could be constructed from various parts though. We really only require DDP requests to contain the auth token, not receipts. There are already tools to set up cookies for SSR (staringatlights:fast-render) and others to convert methods to REST. If Meteor was modified a bit, it could probably be set up to receive http requests (method/subscription requests) which would attach httpOnly cookie auth token, and then respond through a DDP connection.
I guess I meant that, you can look up the cookie values. Browser security prevents JavaScript from accessing that information. Otherwise JavaScript loaded from a site can access localStorage or cookies.
Hi all, thank you everyone for some robust discussion.
It would be amazing if we could get a feature request to address this - not sure what or where to request this though… @jkuester - is this something you could setup? (and throw a link to the Feature Request here?)
Is it needed to send the cookie auth on every method/sub calls? It might be possible that only the initial connection that will authenticate the cookie and then succeeding calls will be an “authenticated” ddp. Or are we talking about the same thing?
Possibly - I haven’t looked at the code that controls DDP. I’m not actually certain that the token is even transferred with every request. It actually would make sense not to. But there are recovery scenarios where I think the token would be needed, such as lost and recovered connections, changing IPs, etc.
If it’s not needed for every request, maybe just the handshake (however that works) could be done over HTTP with the httpOnly cookie.
I have had some discussions with ChatGPT and I added the following on my server.
// Sets Content Security Policy to enhance security by restricting resource loading and execution:
// - `default-src 'self'`: Only allows resources (e.g., scripts, styles, images) from the same origin as the page.
// - `script-src 'self' 'unsafe-inline' 'unsafe-eval'`: Allows scripts from the same origin,
// permits inline scripts, and allows the use of eval().
// - Use of 'unsafe-inline' for scripts increases the risk of XSS attacks but may be necessary for compatibility.
// - Use of 'unsafe-eval' allows JavaScript's eval() function and similar methods, which can execute arbitrary strings as code.
// This can significantly increase the risk of security vulnerabilities such as XSS and should be avoided if possible.
// - `style-src 'self' 'unsafe-inline'`: Allows stylesheets from the same origin and permits inline styles.
// - 'Unsafe-inline' for styles is considered less risky than for scripts but should be minimized if possible.
// Note: 'unsafe-inline' and 'unsafe-eval' can expose the site to cross-site scripting (XSS) vulnerabilities by allowing
// the execution of arbitrary code. Consider refactoring to remove inline usage and 'eval()' or implementing
// additional security measures like nonces or hashes to enhance protection against XSS attacks.
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'"
);
This allows the client to only talk to the source (meteor) server and not to any other server. This makes it impossible to send the token stored in LocalStorage to another server.
Since we have an isomorphic app, the server could send secrets to a malicious site. Therefore, we use very strict network policies on the server.
My conclusion is: Using Local Storage can be unsafe if you have malicious code injected via some deeply nested npm dependency, using a good Content-Security-Policy can reduce the risk. You have to also make sure that the server has restricted access to the interned, else it could send secrets to a malicious site…
This is pretty basic, but should work. The Helmet package and the Meteor’s Browser Policy packages should handle this and more.
I highly recommend everyone to study on this and adjust it further for your apps. Lot of it will be trial and error, but if you want more secure app, then this is a must.
The documentation on atmosphere is difficult to read and there is no link to meteor/packages/browser-policy, and therefore I would not have considered the package (a package without source is suspicious to me)