I've read an article which used Cors-Anywhere to make an example url request, and it made me think about how easily the Same Origin Policy can be bypassed.
While the browser prevents you from accessing the error directly, and cancels the request altogether when it doesn't pass a preflight request, a simple node server does not need to abide to such rules, and can be used as a proxy.
All there needs to be done is to append 'https://cors-anywhere.herokuapp.com/' to the start of the requested url in the malicious script and Voila, you don't need to pass CORS.
And as sideshowbarker pointed out, it takes a couple of minutes to deploy your own Cors-Anywhere server.
Doesn't it make SOP as a security measure pretty much pointless?
The purpose of the SOP is to segregate data stored in browsers by their origin. If you got a cookie from domain1.tld (or it stored data for you in a browser store), Javascript on domain2.tld will not be able to gain access. This cannot be circumvented by any server-side component, because that component will still not have access in any way. If there was no SOP, a malicious site could just read any data stored by other websites in your browsers.
Now this is also related to CORS, as you somewhat correctly pointed out. Normally, your browser will not receive the response from a javascript request made to a different origin than the page origin it's running on. The purpose of this is that if it worked, you could gain information from sites where the user is logged in. If you send it through Cors-Anywhere though, you will not be able to send the user's session cookie for the other site, because you still don't have access, the request goes to your own server as the proxy.
Where Cors-Anywhere matters is unauthenticated APIs. Some APIs might check the origin header and only respond to their own client domain. In that case, sure, Cors-Anywhere can add or change CORS headers so that you can query it from your own hosted client. But the purpose of SOP is not to prevent this, and even in this case, it would be a lot easier for the API owner to blacklist or throttle your requests, because they are all proxied by your server.
So in short, SOP and CORS are not access control mechanisms in the sense I think you meant. Their purpose is to prevent and/or securely allow cross-origin requests to certain resources, but they are not meant to for example prevent server-side components from making any request, or for example to try and authenticate your client javascript itself (which is not technically possible).
Related
I have an API in Next.js (NextAuth.js) that only the frontend will be using. It uses cookies for authentication. My question is could a malicious website change the user's data using CSRF? Should I implement CSRF tokens or can I prevent malicious websites from changing the data without it?
If authentication is based on something that the browser sends automatically with requests (like cookies), then yes, you most likely need protection against CSRF.
You can try it yourself: set up a server on one origin (eg. localhost:3000), and an attacker page on another (eg. localhost:8080, it's the same as a different domain, controlled by an attacker). Now log in to your app on :3000, and on your attacker origin make a page that will post to :3000 something that changes data. You will see that while :8080 will not receive the response (because of the same origin policy), :3000 will indeed receive and process the request. It will also receive cookies set for :3000, regardless of where the user is making the request from.
For mitigation, you can implement the synchronizer token pattern (csrf tokens), double submit, or you can decide to rely on the SameSite property of cookies, which are not supported by old browsers, but are supported by fairly recent ones, so there is some risk, depending on who your users are.
I'm developing a web app with React and an GraphQL API with Node.js / Express. I would like to make the API more secure so that its harder for API requests that don't come from the web app on the browser to get data. I know how to do it with registered users. But how to make the non-registered user still be able to access some basic data needed for the app?
Is it possible to put some kind of key in the web app - so the API call can't be replicated for others through sniffing the network dev tool in browser and replicating in Postman? Does SSL/TLS also secure requests in that browser tool? Or use like a "standard" user for non-registered visitors?
Its a serverside web app with next.js
I know theres no 100% secure api but maybe its possible to make it harder for unauthorized access.
Edit:
I'm not sure if this is a problem about CSRF because Its not about accessing user data or changing data through malicious websites etc. But its about other people trying to use the website data (all GET requests to API) and can easily build there own web app on top of my api. So no one can easily query my api through simple Postman requests.
The quick answer is no you can't.
If you trying to prevent what can be describe as legit users form accessing your api you can't really do it. they can always fake the same logic and hit your webpage first before abusing the api. if this is what your trying to prevent your best bet is to add rate limiting to the api to prevent a single user from making too many request to your api (I'm the author of ralphi and
express-rate-limit is very popular).
But if you are actually trying to prevent another site form leaching of you and serving content to their users it is actually easier to solve.
Most browsers send Referrer header with the request you can check this header and see that requests are actually coming from users on your own site (this technique is called Leech Protection).
Leaching site can try and proxy request to your api but since they all going to come from the same IP they will hit your rate limiting and he can only serve a few users before being blocked.
One thing the Leecher site can do is try to cache your api so he wont have to make so many requests. if this is a possible case you are back to square one and you might need to manually block his IP once you notice such abuse. I would also check if it's legal cause he might be breaking the law.
Another option similar to Referrer is to use samesite cookies. they will only sent if the request is coming directly from your site. they are probably more reliable than the Referrer but not all browsers actually respect them.
I would like in Liferay to allow only logged in users to do post requests, and at the same time deny other Post request sources, like from Postman, for example.
With the caveat that I am not familiar with Liferay itself, I can tell you that in a general Web application what you are asking is impossible.
Let's consider the problem in its simplest form:
A Web application makes POST requests to a server
The server should allow requests only from a logged-in user using the Web application
The server is stateless - that is, each request must be considered atomically. There is no persistent connection and no state is preserved at the server.
So - let's consider what happens when the browser makes a POST:
An HTTP connection is opened to the server
The HTTP headers are sent, including any site cookies that have previously been set by the server, and special headers like the User Agent and referrer
The form data is posted to the server
The server processes the request and returns a response
How does the server know that the user is logged in? In most cases, this is done by checking a cookie that is sent with the request and verifying that it is correct - cryptographically signed, for instance.
Now let's consider a Postman request. Exactly what is the difference between a request submitted through Postman and one submitted through the browser? None. There is no difference. It is trivially simple to examine and retrieve the cookies sent on a legitimate request from the browser, and include those headers in a faked Postman request.
Let's consider what you might do to prevent this.
1. Set and verify extra cookies - won't work because we can still retrieve those cookies just like we did with the login session
2. Encrypt the connection so the cookies can't be captured over the wire - won't work because I can capture the cookies from the browser
3. Check the User Agent to ensure that it is sent by a browser - won't work because I can spoof the headers to any value I want
4. Check the Referrer to ensure the request came from a valid page on my site (this is part of a Cross-Site Request Forgery mitigation) - won't work because I can always spoof the Referrer to any value I want
5. Add logic (JavaScript) into the page to compute some validity token - won't work because I can still read the JavaScript (it's client-side) and fake my own token
By the very nature of the Web system, this problem is insoluble. Because you (the server/application writer) do not have complete control over both sides of the communication, it is always possible to spoof requests from the client. The best you can do is prevent arbitrary requests from arbitrary users who do not have valid credentials. However, any request that includes the correct security tokens must be considered valid, whether it is generated from a browser/web page or crafted by hand or through some other application. At best, you will needlessly complicate your application for no significant improvement in security. You can prevent CSRF attacks and some other injection-type attacks, but because you as the client can always read whatever is sent from the server and can always craft your own requests, you can always provide a valid request.
Clarification
Can you please explain exactly what you are trying to accomplish? Are you trying to disable guest access completely, even through "valid" referrers (a user actually submitting a form) or are you trying to prevent post requests coming from other referrers?
If you are just worried about referrer forgeries you can set the following property in your portal-ext.properties file.
auth.token.check.enabled = true
If you want to remove all permissions for the guest role you can simply go into the portal's control panel, go into Configuration and then into the permissions table. Unchecked the entire row associated with guest.
That should do it. If you can't find those permissions post your exact Liferay version.
Is CSRF possible with PUT or DELETE methods? Or does the use of PUT or DELETE prevent CSRF?
Great question!
In a perfect world, I can't think of a way to perform a CSRF attack.
You cannot make PUT or DELETE requests using HTML forms.
Images, Script tags, CSS Links etc all send GET requests to the server.
XmlHttpRequest and browser plugins such as Flash/Silverlight/Applets will block cross-domain requests.
So, in general, it shouldn't be possible to make a CSRF attack to a resource that supports PUT/DELETE verbs.
That said, the world isn't perfect. There may be several ways in which such an attack can be made possible :
Web Frameworks such as Rails have support for "pseudo method". If you put a hidden field called _method, set its value to PUT or DELETE, and then submit a GET or POST request, it will override the HTTP Verb. This is a way to support PUT or DELETE from browser forms. If you are using such a framework, you will have to protect yourself from CSRF using standard techniques
You may accidentally setup a lax response headers for CORS on your server. This would allow arbitrary websites to make PUT and DELETE requests.
At some point, HTML5 had planned to include support for PUT and DELETE in HTML Forms. But later, they removed that support. There is no guarantee that it won't be added later. Some browsers may actually have support for these verbs, and that can work against you.
There may just be a bug in some browser plugin that could allow the attacker to make PUT/DELETE requests.
In short, I would recommend protecting your resources even if they only support PUT and DELETE methods.
Yes, CSRF is possible with the PUT and DELETE methods, but only with CORS enabled with an unrestrictive policy.
I disagree with Sripathi Krishnan's answer:
XmlHttpRequest and browser plugins such as Flash/Silverlight/Applets
will block cross-domain requests
Nothing stops the browser from making a cross-domain request. The Same Origin Policy does not prevent a request from being made - all it does is prevent the request from being read by the browser.
If the server is not opting into CORS, this will cause a preflight request to be made. This is the mechanism that will prevent a PUT or DELETE from being used, because it is not a simple request (the method needs to be HEAD, GET or POST). Assuming a properly locked down CORS policy of course (or none at all which is secure by default).
No. Relying on an HTTP verb is not a way to prevent a CSRF attack. It's all in how your site is created. You can use PUTs as POSTs and DELETEs as GETs - it doesn't really matter.
To prevent CSRF, take some of the steps outlined here:
Web sites have various CSRF countermeasures available:
Requiring a secret, user-specific token in all form submissions and side-effect URLs prevents CSRF; the attacker's site cannot put the
right token in its submissions1
Requiring the client to provide authentication data in the same HTTP Request used to perform any operation with security
implications (money transfer, etc.)
Limiting the lifetime of session cookies Checking the HTTP Referer header or(and)
Checking the HTTP Origin header[16]
Ensuring that there is no clientaccesspolicy.xml file granting unintended access to Silverlight controls[17]
Ensuring that there is no crossdomain.xml file granting unintended access to Flash movies[18]
Verifying that the request's header contains a X-Requested-With. Used by Ruby on Rails (before v2.0) and Django (before v1.2.5).
This protection has been proven unsecure[19] under a combination of
browser plugins and redirects which can allow an attacker to
provide custom HTTP headers on a request to any website, hence
allow a forged request.
In theory it should not be possible as there is no way to initiate a cross-domain PUT or DELETE request (except for CORS, but that needs a preflight request and thus the target site's cooperation). In practice I would not rely on that - many systems have been bitten by e.g. assuming that a CSRF file upload attack was not possible (it should not be, but certain browser bugs made it possible).
CSRF is indeed possible with PUT and DELETE depending on the configuration of your server.
The easiest way to think about CSRF is to think of having two tabs open in your browser, one open to your application with your user authenticated, and the other tab open to a malicious website.
If the malicious website makes a javascript request to your application, the browser will send the standard cookies with the request, thus allowing the malicious website to 'forge' the request using the already authenticated session. That website can do any type of request that it wants to, including GET, PUT, POST, DELETE, etc.
The standard way to defend against CSFR is to send something along with the request that the malicious website cannot know. This can be as simple as the contents of one of the cookies. While the request from the malicious site will have the cookies sent with it, it cannot actually access the cookies because it is being served by a different domain and browser security prevents it from accessing the cookies for another domain.
Call the cookie content a 'token'. You can send the token along with requests, and on the server, make sure the 'token' has been correctly provided before proceeding with the request.
The next question is how do you send that value with all the different requests, with DELETE specifically difficult since it is not designed to have any kind of payload. In my opinion, the cleanest way is to specify a request header with the token. Something like this x-security-token = token. That way, you can look at the headers of incoming requests, and reject any that are missing the token.
In the past, standard ajax security restricted what could be done via ajax on the malicious server, however, now-a-days, the vulnerability depends on how you have your server set up with regards to accees-control configurations. Some people open up their server to make it easier to make cross domain calls or for users to make their own RESTful clients or the like, but that also makes it easier for a malicious site to take advantage unless CSRF prevention methods like the ones above are put in place.
What changes to the HTTP protocol spec, and to browser behaviour, would be required to prevent dangerous cases of cross-site request forgery?
I am not looking for suggestions as to how to patch my own web app. There are millions of vulnerable web apps and forms. It would be easier to change HTTP and/or the browsers.
If you agree to my premise, please tell me what changes to the HTTP and/or browser behaviour are needed. This is not a competition to find the best single answer, I want to collect all the good answers.
Please also read and comment on the points in my 'answer' below.
Roy Fielding, author of the HTTP specification, disagrees with your opinion, that CSRF is a flaw in HTTP and would need to be fixed there. As he wrote in a reply in a thread named The HTTP Origin Header:
CSRF is not a security issue for the Web. A well-designed Web
service should be capable of receiving requests directed by any host,
by design, with appropriate authentication where needed. If browsers
create a security issue because they allow scripts to automatically
direct requests with stored security credentials onto third-party
sites, without any user intervention/configuration, then the obvious
fix is within the browser.
And in fact, CSRF attacks were possible right from the beginning using plain HTML. The introduction of nowadays technologies like JavaScript and CSS did only introduce further attack vectors and techniques that made request forging easier and more efficient.
But it didn’t change the fact that a legitimate and authentic request from a client is not necessarily based on the user’s intention. Because browsers do send requests automatically all the time (e. g. images, style sheets, etc.) and send any authentication credentials along.
Again, CSRF attacks happen inside the browser, so the only possible fix would need to be to fix it there, inside the browser.
But as that is not entirely possible (see above), it’s the application’s duty to implement a scheme that allows to distinguish between authentic and forged requests. The always propagated CSRF token is such a technique. And it works well when implemented properly and protected against other attacks (many of them, again, only possible due to the introduction of modern technologies).
I agree with the other two; this could be done on the browser-side, but would make impossible to perform authorized cross-site requests.
Anyways, a CSRF protection layer could be added quite easily on the application side (and, maybe, even on the webserver-side, in order to avoid making changes to pre-existing applications) using something like this:
A cookie is set to a random value, known only by server (and, of course, the client receiving it, but not a 3rd party server)
Each POST form must contain a hidden field whose value must be the same of the cookie. If not, form submission must be prevented and a 403 page returned to the user.
Enforce the Same Origin Policy for form submission locations. Only allow a form to be submitted back to the place it came from.
This, of course, would break all sorts of other things.
If you look at the CSRF prevention cheat sheet you can see that there are ways of preventing CSRF by relying upon the HTTP protocol. A good example is checking the HTTP referer which is commonly used on embedded devices because it doesn't require additional memory.
However, this is weak form of protection. A vulnerability like HTTP response splitting on the client side could be used to influence the referer value, and this has happened.
cookies should be declared 'local' (default) or 'remote'
the browser must not send 'local' cookies with a cross-site request
the browser must never send http-auth headers with a cross-site request
the browser must not send a cross-site POST or GET ?query without permission
the browser must not send LAN address requests from a remote page without permission
the browser must report and control attacks, where many cross-site requests are made
the browser should send 'Origin: (local|remote)', even if 'Referer' is disabled
other common web security issues such as XSHM should be addressed in the HTTP spec
a new HTTP protocol version 1.2 is needed, to show that a browser is conforming
browsers should update automatically to meet new security requirements, or warn the user
It can already be done:
Referer header
This is a weaker form of protection. Some users may disable referer for privacy purposes, meaning that they won't be able to submit such forms on your site. Also this can be tricky to implement in code. Some systems allow a URL such as http://example.com?q=example.org to pass the referrer check for example.org. Finally, any open redirect vulnerabilities on your site may allow an attacker to send their CSRF attack through the open redirect in order to get the correct referer header.
Origin header
This is a new header. Unfortunately you will get inconsistencies between browsers that support it and do not support it. See this answer.
Other headers
For AJAX requests only, adding a header that is not allowed cross domain such as X-Requested-With can be used as a CSRF prevention method. Old browsers will not send XHR cross domain and new browsers will send a CORS preflight instead and then refuse to make the main request if it is explicitly not allowed by the target domain. The server-side code will need to ensure that the header is still present when the request is received. As HTML forms cannot have custom headers added, this method is incompatible with them. However, this also means that it protects against attackers using an HTML form in their CSRF attack.
Browsers
Browsers such as Chrome allow third party cookies to be blocked. Although the explanation says that it'll stop cookies from being set by a third party domain, it also prevent any existing cookies from being sent for the request. This will block "background" CSRF attacks. However, those that open full page or in a popup will succeed, but will be more visible to the user.