Say I login through an opendID connect provider and am redirected to my callback www.mysite.com/auth/callback. I then create an httponly cookie, which contains an id referencing to my received tokens, that is passed to the browser at wwww.mysite.com/. How would another site submit a request that contains that same session cookie? Does the browser not pass only the cookies of the requesting domain. So if www.evil.com tries to make a request to www.mysite.com/api/endpoint, won't the session cookie not be passed, making the forged request invalid?
Am I missing something basic here??
When web browsers send a request to a different domain, they're nice enough to first check if they already have cookies for that domain, and if they do, then they send them along with the request. So, if you're on a web application trying to send a request to your app, it'll send that request along with your cookies. The idea behind the anti-forgery token is that even when the web browser sends all that information, if the token doesn't match with the one that you create on a legit request submitted from your application, it'll fail.
If you don't want your cookies being sent via cross-site requests, you can use the samesite flag for your cookies. Here you can decide between Strict and Lax mode. In Strict mode, you'll never send your site cookies via cross-site requests, so you don't need to care about session cookies being sent. The problem here is that if you're redirected from a different site, from example, if you're here, and try to go to facebook (if facebook used strict mode), your cookies will not be sent, and you'll need to authenticate again (it can be an annoying or a good feature, depending of your application and your user-base).
The Lax mode is pretty similar, but in this mode, you'll only send your cookies via safe HTTP verbs (GET, HEAD, OPTIONS and TRACE), so you still get your protection against POST/PUT XSRF attacks, and you don't have an annoying behaviour for the GET requests. It's up to you to decide which one will be the better option for your app.
More info about XSRF and samesite cookies: http://arnoldcer.com/2017/03/14/cross-site-request-forgery-what-it-is-how-to-exploit-it-and-how-to-defend-against-it/
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.
Now authorization sheme looks like this:
If the user input the correct data, the server generates a unique sessionKey, inserts it into the session table with FK for this user. In response to a JSON request, I send this sessionKey. The web client sets this key it in a cookie.
But the problem is that if the web client stores this cookie, JS will have access to them, and it is not safe.
The alternative is to set the HTTP-Only cookie. But it is not clear whether it is necessary to use CSRF middleware in this case. Does the HTTPOnly attribute solve the problem of XSS / CSRF attacks? If it does not decide and you need a CSRF middleware, then the csrf cookie must be a session cookie.
The problem is that all the csrf middlewares for my framework do not allow to use of the session csrf cookie. Alternatively, write my own middleware.
Do I understand correctly that the csrf middleware stores the token that I gave to the client in RAM and verifies on every request? But then what's the point of this token if it can be intercepted in the same way as an authorization cookie?
Let's start with stating that Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF) are two different animals.
XSS is about embedding malicious code into a site to have it executed on the client machine. No HTTPOnly flag can mitigate this.
CSRF is about embedding malicious code on some third party site and sending you the link to the third party site. The malicious code can try to fire GET/POST request (which can bypass browsers Same Origin Policy) and execute some unwanted actions on the site the user is logged to. It's easier to understand this with an example:
You are logged into your site on https://example.com. You are authenticated with a cookie.
Someone sends you a link to https://malicious.net. You open the link in a separate browser tab.
Malicious code is being executed and fires a request to https://example.com/deleteAccount=1. Cookie will be attached, request will be authenticated and executed.
The answer is no - the HTTPOnly flag will not mitigate any of this. But let us concentrate on solving CSRF issue. What options do you have?
In fact you have many: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html
IMO the easiest could be passing the sessionKey not over a cookie, but over an Authorization header. This can not be done automatically by the browser so you would be safe from CSRF attacks.
Most CSRF solutions seem to insist that the CSRF token is sent as part of the POST data.
In my situation the data being sent is json, and I don't control what is sent (and I don't want to start messing with the json). So, I'm thinking of sending the CSRF token as a header. However, there are legacy parts of my application that would still need to be able to send the token in the body (e.g. submits from html forms).
So my CSRF protection would have to allow the request if a valid CSRF token appeared in the body OR a header. Is this a security risk, compared with insisting that the token is in the body?
CSRF is about make a unsuspicious user post data to a server where the attacker believes the user is logged in.
The idea behind the protection, is that the server associate a token to your session, and sends it to you as a cookie and as payload requirement. Then when posting something you send the token in the payload, and as cookie. Therefore the attacker cannot guess what token is in the cookie or the session. If the server receives a post with two different tokens, it will be rejected.
I think it would be fine to put the payload token in a header, as long it is not "Cookie" or any other header that is "remembered" and sent automatically by the browser.
There won't be any security risk if you send a CSRF token in header. Just make sure that the value of this header changes everytime the client requests a page i.e it should be a random number. Also, your web application on client side should send this header back to the server, so that the server can match the value of header sent to the client with the value of the same header received from the client's response.
Sending CSRF in request header is more secure.
CORS doesn't check same-origin policy for the form tag requests, which means if somebody managed to get the CSRF token then he can send the post request by using form tag from different domain (origin)
but in case of sending the CSRF in request header, the form tag cannot send request header, he has to use javascript (fetch() or XmlHttpRequest()), in this case the CORS will prevent him because he is sending from different domain (origin).
This defense relies on the same-origin policy (SOP) restriction that only JavaScript can be used to add a custom header, and only within its origin. By default, browsers do not allow JavaScript to make cross origin requests with custom headers.
below, is quoted from https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#use-of-custom-request-headers
If this is the case for your system, you can simply verify the presence of this header and value on all your server side AJAX endpoints in order to protect against CSRF attacks. This approach has the double advantage of usually requiring no UI changes and not introducing any server side state, which is particularly attractive to REST services. You can always add your own custom header and value if that is preferred.
This technique obviously works for AJAX calls, but you still need to protect form tags with approaches described in this document such as tokens. Also, CORS configuration should also be robust to make this solution work effectively (as custom headers for requests coming from other domains trigger a pre-flight CORS check).
I'm loving using ServiceStack, but one question has come up from a colleague that I cannot answer. The Authentication mechanism sets a ss-pid and an ss-id cookie, which is explained here: https://github.com/ServiceStack/ServiceStack/wiki/Sessions
When trying to access a restricted resource, these cookies need to be provided, otherwise you'll receive a 401 Not Authorized result.
My question is this. Why use a cookie rather than a custom HTTP header value that includes the sessionId or equivalent cookie values? Is it because the cookie inherently has its own mechanism to maintain expiration? What were the design decisions undelying the use of cookies over HTTP headers?
HTTP Cookies are inherently sticky and is the most appropriate way for maintaining sessions over HTTP. After the server instructs the client to add a Cookie, every subsequent request that the client makes back to the same server will also retain that cookie - this what enables the Client/Server session.
I am reading the tutorial at
http://code.google.com/p/google-web-toolkit-incubator/wiki/LoginSecurityFAQ
It states
Remember - you must never rely on the
sessionID sent to your server in the
cookie header ; look only at the
sessionID that your GWT app sends
explicitly in the payload of messages
to your server.
Is it use to prevent http://en.wikipedia.org/wiki/Cross-site_request_forgery#Example_and_characteristics
With this mythology, is it sufficient enough to prevent to above attack?
Yes, it is sufficient to prevent Cross Site Request Forgery.
The sessionid in the cookie cannot be trusted. Say a user is logged on to mysite.com and session id is in the cookie. Now the user clicks on a link to evilsite.com. evilsite.com has code like this
<img src="http://mysite.com/transfermoney.jsp?amount=1000.." />
The browser will make a request to mysite.com, and it will also send along the cookie with the session id. The thing to understand here is that evilsite.com cannot read the cookie, but it can still get its job done.
Browser same-origin policy prevents evilsite.com from reading the session identifier whether its in the cookie or embedded in html page. But because browser automatically sends the cookie to your server even if the resource was requested from the html code in another domain, you have XSRF.
To prevent this, it is recommended to put the session identifier as a request parameter. If its added as a request parameter, evilsite.com cannot access the identifier, and hence cannot put it in the img src attribute.
However, do remember that if your site has XSS vulnerabilities, nothing can prevent you from XSRF. Put it another way, if you have XSS vulnerabilities, an attacker wouldn't even care about doing XSRF.