Hello Kyle, in my understanding CSRF exploits have nothing to do with CORS. Instead they are related to the "SameSite" policy of your cookies. In your example server you've set the SameSite policy of your cookie to "none", doing so you're vulnerable to csrf unless you implement a csrf token as you showed us. BUT if you hadn't set samesite policy in your server, Chrome (and I think all the other major browsers) would have automatically set that cookie to have a "Lax" policy which would have prevent csrf attack even without a csrf token. That's my understanding from what I was able to experiment in the past. Please correct me if I'm wrong. I usually don't write comments, but I'll take the opportunity to thank you for all you contents. They have been incredibly valuable for me :)
Checking "CanIUse" LAX is only the default in Chromium based browsers. Firefox, Safari, and even the now deprecated IE11 all support the "SameSite = Lax" header though, if you've manually specified it.
CORS does not increase security. CORS, specifically, is an opt-out of security policy. The *default* policy of modern browsers is a same origin policy, and if you want to allow origins beyond that, you need to configure your server with a CORS whitelist policy that says "Oh, these places are allowed too." As others have stated, this vulnerability is entirely due to the cookie's SameSite setting, where "None" was the old default (meaning other sites could send requests to your origin and have it pass along any relevant cookies). The new default is Lax which only passes such cookies on a navigation action (meaning it doesn't return data to the malicious site), and you could even specify the SameSite policy as Strict to only ever be sent when you're already on the cookie's origin site. Now, you can still do weird stuff and end up with a CSRF vulnerability. For instance, you can have an endpoint that allows a GET request but performs actual actions on behalf of the user, allowing malicious sites to attack this way even on a navigation action. But the more appropriate response is using the correct HTTP Verb bindings instead of going all-out with CSRF tokens, I think. As to why your attack worked-- localhost is the same site as localhost, even if it's a different port. If you had 2 or 3 different computers and hosted the login site and the malicious site on two different computers, I don't think this would work.
Great video! Auth is so important to get right and has so many pitfalls that you can fall into. Love to see content like this helping up everyone's security game!
If you don't use subdomains or multiple sites on the same domain, same-site:strict cookies are enough. It's still good to use a CSRF Token if you're not sure where the client's going to deploy your web app - they might have a statistics site or some other tool on a subdomain with an XSS.
for many sites a same-site strict session cookie is bad user experience though, as your session gets reset whenever you click a link to your site. Imagine getting logged out of Twitter/RUclips whenever someone sends you a link to a tweet/video. CSRF Tokens (or additional same-site strict cookies to detect 3rd party requests) really are the better solution here. But technically you're correct.
@@RoterFruchtZwerg Clicking a link triggers navigation first, for example to the Twitter/RUclips domain and then your existing cookie is used for the rest of the requests, it's not the same as one site requesting data from another. You wouldn't be logged out.
@@TanelM same-site "strict" prevents sending Cookies with navigation request. Clicking a link on Twitter that points to RUclips would log you out from RUclips. What you describe is same-site "lax", which still includes the cookie on navigation.
The drawback of this approach is that you need to implicitly include the csrf token in every form/http post and validate it in backend, which can easily be forgotten. A better approach is to use refresh tokens and access tokens. Refresh tokens are stored in http only cookie which can only retrieve access token (and refresh themselfs), and access token are stored in browsers memory (like you do with the csrf token) and used to call the API's.
@@aniketbhat9840 because you have to manually attach the access token in the Authorization header of your ajax calls, if you keep the access token as an httpCookie, the client can't access it, thus making them useless, on the otherhand clients don't have to worry about refresh tokens, as their sole purpose is to fetch a new access token, and they can be sent in any way even as a cookie.
SameSite policy on cookies and checking origin/referrer headers is a cleaner solution that csrf tokens in my opinion, unless you make an API with some ridiculously bad GET requests.
8:30 but if the token is sent only when the user logs in and you don't save it in local storage, he will lose it if he refreshes the page after being logged in
It is assumed that the client would preserve the token if it's a SPA or your server will keep sending/generating tokens if it's SSR as he explains at 9:10.
Most popular practice is sending a new csrf token for each form. If user opens the "add new user" page, while loading that page's data your client receives a csrf token that is only valid for that form. Which makes it unnecessary to store any csrf tokens. Because when user needs a csrf token, they will receive a new one when the form is loading.
But someone could just easily check the dev tools and look for the header and replicate it in the request no? there is the same type of vulnerability with CSRF tokens, because they don't expire when another page with the same cookie is loaded. Someone could just make a request using a library like axios, fetch the CSRF token, and then use that in their request.
@@wearr_ Dev Tools have nothing to do with csrf. When you get the access to the dev tools on the computer of another person, you could make anything without csrf. And you cannot do a request with axios to another site. Specialy with a custom header. Even when CORS is enabled, the custom header have to be enabled for CORS requests. Fot this you have to list them in the CORS response header, wich you should not do.
@@wearr_ the csrf token is generated per request (more secure less usable) or per user-session (less secure more usable), so even if some one can request "our api" to get a csrf he wouldn't affect other users with his malicious intents and his "evil" axios skills 😄
@@Venistro if you are appending a header to a request for something like -my-secure-code or whatever tf you want to do and then someone looks in the dev tools of the app and sees that each request has that header appended to it, they will just add it to their request. The only way to do it securely is have a crypto key that generates a unique header name, and then the server has to know the results of that to check against it.
@@yaci8330 if you use a templating language like EJS, to use csrf tokens you should just embed it into the html form that is submitted, that is quite literally how a library like csurf recommends doing it for ejs. However if someone makes a web request to the page using axios (http client, not "evil") then they can get the html content, which will come with a freshly generated, valid csrf token in the request. You can then use cheerio to get the token from the html content, and now you might be asking yourself "but that is just purely hypothetical, right?" no. That very same kind of attack was something that I've personally leveraged to get a refresh token from an API from a *different origin* *cough* cross site request forgery *cough*
This is the note taken from MDN. Maybe we don't need implement it on our own in modern browsers. Note: Lax replaced None as the default value in order to ensure that users have reasonably robust defense against some classes of cross-site request forgery (CSRF) attacks.
Part of the best practice to "never trust the client" is to not trust that the browsers they're using are modern, uncompromised, and using secure settings. Because you cannot guarantee these things, implementing CSRF protection yourself is critical.
Why do you think so, they literally say that it will prevent "Some classes of CSRF attacks", but not all. It is possible to bypass both Lax and Strict flags, depending on how the application is built. Implementation of CSRF tokens is still vital and without it, the application is pwnable.
Thank you, I've been researching CSRF from a developers POV, this really cleared things up. Just one thing to point out that CORS is actually to impose relaxations on Same Origin Policy. We can do this using Access-Control headers. Subscribed for more such content.
It's a very nice explanation but I still have several doubts. 1. If the server checks the CSRF token, why does it need to check also the session if we are sure that the request is done by a logged-in user from their browser? 2. Isn't setting SameSite option in the cookie enough? If not, why? 3. If the CSRF token is stored in js, it's lost after refreshing the site, right? Can we prevent it? Thanks!
1. CSRF token needs to be tied to the user's session. Because otherwise, anyone can request an action and get the csrf token from his/her request by himself and then use that token to forge the malicious request to other users. If the token is not tied to the user's session csrf tokens from other users will be valid and csrf protection is then broken. 2. SameSite is not enough against sophisticated attacker. SameSite Lax will attach the cookies to the cross-origin requests if the method is GET. Many applications usually accept form parameters from the GET line. For example PHP ($_REQUEST) and Java are very common to do that. Attacker can forge the form action and specify parameters from GET line, which will bypass the Lax flag. SameSite Strict cookie can also be bypassed in couple of more complicated ways. 3.CSRF tokens should be random and unique in ideal case for each form submitted. Upon refreshing the page, the token should be changed in the server's response, so there is usually no need to store it anywhere. Upon a page refresh, it is usually embedded in the hidden input parameter from the form. When the form is submitted the csrf token is there and the server checks it and knows that the request is not forged.
@@mach1ne722 Regarding number 3. tho, lets use the follwing example. Lets say a user sends a succesful login request and receives back a session cookie and a csrf token "1", then through a redirect he sends a request for the main page, with the session id and csrf token "1" , the server expects to receive "1" so it validates his request and sends back the new csrf token to be saved locally: "2". Now when the user sends requests for any page, the server will expect to receive "2" as a csrf token, which is fine since the user has the new value, "2", saved locally. However lets say the user refreshes the page, the value "2" would be lost and the browser, at best. would send the same request (with token "1") again, which would be rejected. This can be solved by not generating a new token everytime and using the original token always while the user is logged in, but then the issue would arise when the user opens a new fresh tab with the address of your site, in which case there would be no token sent, just the session id, so the user would be rejected in this case as well. The only solutions to this (as far as i can see) are either asking the user to login everytime he wants to get a fresh csrf token, which invalidates the use of cookies entirely, or to have a certain endpoint of the application (like the mainpage) give out a csrf token with just a valid session cookie, without the need of a valid cstf token, which invalidates the security benefit of the csrf token
Thanks Kyle. I think modern browsers by default secures your web app against CSRF by using strict CORS policy. So no need to implement CSRF unless you have enabled CORS. In this case, CSRF becomes mandatory.
CORS policy only works for ajax calls, if an attacker uses a form action to make the request (the one which refreshes the page), CORS policy won't stop it. Similar is true for using the src attribute of an img tag
@@thatsalot3577could you please clarify me that how exactly CORS policy can prevent ajax requests because as far as I know CORS allows all the requests but just won't show the response
@@akshaykumar-wd8jc that is not how cors work On browser, before every ajax call, a preflight call is made to the server where method is OPTIONS, if the response headers allow your domain to make a post request only then your ajax call would be able to send anything to the server
sounds the like the refresh-token method. although they both do not eliminate the risk of a malicious attack they just reduce it. also whether you know that there is a risk of being "hacked" or not, you always assume that it's the case. so not a single app on the earth is secure 100%. it's only a matter of making harder.
0:20 - "Recrest" you say...? I do brush my teeth twice, but I use Colgate toothpaste. So, am I good? 2:29 - "Crewcrest"!? Oh no! I have never brushed with a whole crew! I really need to fix these vulnerabilities.
I think if you ask the user to send back his username or password with the request to delete a ressource, in this case you're protected against CSRF, as the evil site or page doesn't know them even if the cookie is auto sent by the browser, in other words the username or password will act as a CSRF token, because at the end a csrf token is just another secret that you're posting and keeping in a variable in memory and sending it back throught the clients request.
There are so many ways to leak the username and figure somebody's password. Those cannot be considered as enough for the endpoint to be secured against CSRF attacks. That "another secret" is actually random and unpredictable in most cases and username and password are not! In this example, the request to delete a resource usually will not have username and password information in them, but on the other hand some sort of ID that respresents the resource that is being deleted. More often than not the resource's identifier is publicly known.
Very clear, and fun. So in summary the main issue being other sites can use the targeted sites cookie. And by storing a token as a variable it's not accessible from other sites. However in the current example if the user refreshes there page they would lose the token and need to re login. Or should that token be stored in the browser?
usually, in SPAs even if you have already stored session-cookies you need to make a login request on start to gather all the side information about user from server
That's a great point. I also posted a comment related to having to re-login when page is refreshed. Storing it in browser is bad as it can be retrieved my hacker and set a input value.
I would no use a token on cookie. But maybe local storage since that is not sent automatically with the request. The request requires to add that param from local storage manually... That would solve this issue
Great, one way is that server can pass a JWT token which the logged in client can save and use it in the future requests. Hence using secure tokens such as JWT, prevents CSRF attacks.
this worked is because you are using a simple post request if you had body in your request then a preflight request will be sent to precent this from happening so another solution would be to not allow get requests or simple post requests to modify your backend resources or I am missing something??
If you recreate your token on every request but just store one current token in session, you will run into problems with multiple tabs... as soon as you open a second tab, the token of the first tab isn't valid snymore
I just started learning NestJS (uses ExpressJS under the hood) and this is a super important thing to note when designing an api. Thanks for the video. 🙏
With NestJS you are most likely using JWT Tokens (at least if you follow their authentication docs). With JWT Tokens, you don't have to worry about CSRF.
So what would happen if after you login, you refreshed the page? Wouldn't your CSRF token get lost? You wouldn't be able to run any action unless you logged out and then logged in again. right? Any way of solving this without storing it anywhere visible/stealable?
When you saved the tokrn locally if you refresh the page the tokne will gone meaing if you make another request you will not have the crsf so what can i d about it
But, if you login to get session and then simply refresh page you will lose that csrf token making page unusable. Unless it is fine to force user via login process each time page is refreshed.
I have listed steps. Pls correct if i'm wrong. 1. send csrf token on login both response body and set response cookie 2. save csrf token on locally. 3. send the saved csrf token on each requests. and i have one doubt also. do we need to send and save this csrf token on all api requests ?
Really great video Kyle... What I don't understand is, how cookie from localhost:5500 is able to accessed by localhost:5501? Is browser don't enforce, each domain have their cookies seperated?
The attack vector in csrf isn't explained here. A user needs to be logged into say a bank site, then goes to another site say email in a new tab. In the email they click a link that goes to the bank app and uses the cookie from the bank app . Session storage is tab scoped for jwt.
@@peterhorton9063 when the person clicks the link it isn't CSRF since the user actively navigates to the tab of the bank and the malicious page doesn't send the request without the user noticing. See the CSRF definition on MDN.
Hello Kyle, What will happen when the user will refresh the browser window? Will the CSRF token be deleted. If so user needs to login every time the user refresh the browser?
I don't understand how CORS doesn't prevent that...An origin consists of protocol, host and port number. If the port number differs, the cors policy should block the response! Actually, the post is not even triggered, a preflight request with OPTIONS method checks if the post can be done. Am I wrong?
I don't understand the part where the in evil page, the cookie is automatically sent. I thought, we have to explicitly pass the data in api call body to process it. Main thing u don't understand is, where the normal page is sending the cookie? If the cookie is sent automatically then it means from that site the cookie will be sent with every api call??
So someone can do a request from the original site, copy the CSRF token, put it inside there own process. And it would still work for that session...? Not seeing the 'security' here ?
I am using something else, client id and client secret which needs to be passed every time you send an request to the api, I have created a file with default clients (each one has client id and client secret) which has a pc client, a phone client and a web client. But I think that this form thing will still be a problem
but if everything is intercepted by middleman via his backend, those information can be still be obtained right? provided he knows where you keep your csrf token (whether in html input form/local storage/cookie)
Okay But how this goes with my authentication with JWT. I am storing short lived accessToken in some variable/state which is sent with Authorization Header. RefreshToken in some httpOnly Cookie. Do I still need to worry about CSRF?
4:16 Ok i'm not getting this at all. If the "evil" site is a completely separate domain, how could it get the cookie to begin with? I can't make sense of it. Like, from my website, i could hit a youtube endpoint to delete one of your vids and somehow magically i already have your authentication/authorization cookie?
I think it's more like, you logged in your own session, and while it's active you clicked a malicious link which deletes the vids from the current session, in this case your vids.
As mentioned above, the point is to make a user logged somewhere to click from its browser on a malicious link by himself. Because as long as you click yourself on a link, all cookies and limited-access routes are available, no matter from which tab/domain you're trying to access.
the 3rd party site never gets your session cookie, but it uses the fact that your browser sends it along with the form request. This only works with same-site "none" Cookies though, which were default until one or two years ago. now the default is "lax" and you need to explicitly set it to "none".
Would I be correct that requiring a custom header with the POST of `foo=bar`, or whatever you wish, would be enough if you expect all authenticated requests to be done with JavaScript ajax requests and not HTML form posts? From what I understand, there is no way to add a custom header on an HTML form post. So all this managing of extra tokens is unnecessary.
Couldn’t this also be prevent by checking origin. Even if it’s a micro service setup, with man instance of the came backend. Or even move that check of origin to the first layer check like an api gateway?
Yes, and no. In that case you are open to XSS attacks, because a potentially malicious JavaScript code can steal data from your local storage, and thus make a combined attack by leveraging XSS + CSRF.
Hi Kyle. I'm using Spring Security in my server side app. I want to use fetch api in a pure Javascript client call. I receive the JSESSIONID and XSRF-TOKEN cookies in client browser but I cannot access through Javascript to the CSRF token value. How can I do it? Many thanks.
CSRF does not stop most attacks. All you need to do is a fetch request to what ever end point has the token then pass that along with the request. For real security use JWT or client ssl certs. Obviously as others have pointed out that correct CORS setup would have stopped this attack.
a 3rd party site can't do a fetch request on the endpoint that delivers the csrf token. Also CORS is not relevant. CORS does not apply to GET/POST requests sent via link or form. CSRF Tokens are the right fit here.
I'm a beginner dev and after this video I dove deeper into the subject and it's all pretty complicated. I just decided to settle with my express server using csurf, each time the react frontend is refreshed it gets a new csrf token from the /csrf-token endpoint and stores it app-wide for post/delete/put requests with zustand, this is enough for decent security right?` I read that samesite strict-policy is enough but since my backend and frontend are on different domains it doesn't seem like a sensible solution for me
jwt are a solution for another problem and when transmitted in a cookie actually be the concern in this context. Jwt are used to store server data (e.g. user information) on the client side. The server has signed a jwt and can be sure that the payload of the jwt (e.g. user login) was 100% created by the server and the content can be trusted. Often a jwt is transmitted automatically by the browser in a cookie, and this "automatically" is in my opinion the critical point here. However in my opinion the content of this video is outdated and creates confusion, because the problem here is solved with the samesite property of the cookie itself (which is funnily disabled in this video and makes me wonder why try to explain a solution for a problem that is created in the first place with no reason). Actually no code is required anymore to check on the server side for a random session token, because modern browsers do the "automatic" transmission of cookies differently now (search for same site options strict or lax). Even though I respect Kyle for his knowldege, but on this one, it looks to me like solving a problem that does not exist anymore with technology of 4 years ago.
I just wanna to know that you write condition to not match crsf token when we request to auth api but there is one bug on there is we can send random uuid to api request and server will not check that uuid is generated from server side or evil side.
From the evil page, we made a request to port 3000 right? Then how did it reach 5500? Can someone explain pls? Also how was evil page created under same domain?
The server with the API is running on port 3000. The malicious page is simply HTML; it doesn't need to run under the same domain. It exploits automatically sent cookies to make requests to the API on port 3000, resulting in the deletion of the user. The client, running on port 5500, fetches data from the same API. After the attack, it can't retrieve the user because the user no longer exists.
so even if i have jwt auth by cookie httpOnly should i use csrf in every request that changes app state? i other words after login for jwt token should i give csrf token for api client? i force this client to send it back in body?
Hello Kyle, Really Nice and clear explanation, Thanks a lot. Can you please help on, How to handle scenarios where the user refreshes a page? As CSRF is stored in a local variable, the page reload will vanish its value...
What is the link to what Kyle recommends we "check out first" at 1:03? Kyle is speaking so quickly that auto caption translates what he saying to that he's putting the link in the "cards". ?????
You can capture the url that sent (referred) the request, right? Also, any chance you feel like teaching us a thing or two about the VirtualKeyboard API?? I was just about to toy around with it, those touchscreen keyboards always try and ruin a UI!
Yep! Although I'm not a security expert, I don't see any way JWT could guard you against CSRF attacks. CSRF attacks exploit the fact that a user's browser automatically sends certain cookies. If you store your JWT token in a cookie, there will be no difference at all. That's just my understanding of the topic. As I said, I'm not an expert, so you can investigate it yourself.
Is mandatory do login? I see many sites that for example have a cart in home page without login 🤔. How to do secure home page where all people can edit ?
I'm not sure how to store firebase authentication tokens for for Sveltekit & SSR. Probably I should store it in the memory but I guess that means I should send the token with every request. I guess unless there is another way.
why you dont try that evil page out from an sandbox VM to let us see what happens? and big thx to your videos! they are amazing and clear to understand
Hello Kyle, in my understanding CSRF exploits have nothing to do with CORS.
Instead they are related to the "SameSite" policy of your cookies.
In your example server you've set the SameSite policy of your cookie to "none", doing so you're vulnerable to csrf unless you implement a csrf token as you showed us.
BUT if you hadn't set samesite policy in your server, Chrome (and I think all the other major browsers) would have automatically set that cookie to have a "Lax" policy which would have prevent csrf attack even without a csrf token.
That's my understanding from what I was able to experiment in the past. Please correct me if I'm wrong.
I usually don't write comments, but I'll take the opportunity to thank you for all you contents. They have been incredibly valuable for me :)
Yup, but same-site means same main domain. It doesn't protect from an attack between sub-domains.
Correct. Protecting from CSRF is THE reason why the same-site attribute for Cookies was introduced and defaults to "lax" since 1-2 years.
You're the correct here
@@TanelM same-origin protects against same site different subdomains too
Checking "CanIUse" LAX is only the default in Chromium based browsers. Firefox, Safari, and even the now deprecated IE11 all support the "SameSite = Lax" header though, if you've manually specified it.
CORS does not increase security. CORS, specifically, is an opt-out of security policy. The *default* policy of modern browsers is a same origin policy, and if you want to allow origins beyond that, you need to configure your server with a CORS whitelist policy that says "Oh, these places are allowed too." As others have stated, this vulnerability is entirely due to the cookie's SameSite setting, where "None" was the old default (meaning other sites could send requests to your origin and have it pass along any relevant cookies). The new default is Lax which only passes such cookies on a navigation action (meaning it doesn't return data to the malicious site), and you could even specify the SameSite policy as Strict to only ever be sent when you're already on the cookie's origin site.
Now, you can still do weird stuff and end up with a CSRF vulnerability. For instance, you can have an endpoint that allows a GET request but performs actual actions on behalf of the user, allowing malicious sites to attack this way even on a navigation action. But the more appropriate response is using the correct HTTP Verb bindings instead of going all-out with CSRF tokens, I think.
As to why your attack worked-- localhost is the same site as localhost, even if it's a different port. If you had 2 or 3 different computers and hosted the login site and the malicious site on two different computers, I don't think this would work.
Thanks @andrewshirley9240. I thought something was off in the video but couldnt point out what.
Great video! Auth is so important to get right and has so many pitfalls that you can fall into. Love to see content like this helping up everyone's security game!
If you don't use subdomains or multiple sites on the same domain, same-site:strict cookies are enough. It's still good to use a CSRF Token if you're not sure where the client's going to deploy your web app - they might have a statistics site or some other tool on a subdomain with an XSS.
for many sites a same-site strict session cookie is bad user experience though, as your session gets reset whenever you click a link to your site. Imagine getting logged out of Twitter/RUclips whenever someone sends you a link to a tweet/video. CSRF Tokens (or additional same-site strict cookies to detect 3rd party requests) really are the better solution here. But technically you're correct.
@@RoterFruchtZwerg Clicking a link triggers navigation first, for example to the Twitter/RUclips domain and then your existing cookie is used for the rest of the requests, it's not the same as one site requesting data from another. You wouldn't be logged out.
@@TanelM same-site "strict" prevents sending Cookies with navigation request. Clicking a link on Twitter that points to RUclips would log you out from RUclips.
What you describe is same-site "lax", which still includes the cookie on navigation.
@@RoterFruchtZwerg Oh, didn't know that! Haven't worked on sites where this would be an issue. Thanks for the clarification!
@@RoterFruchtZwerg same site lax is more than enough
The drawback of this approach is that you need to implicitly include the csrf token in every form/http post and validate it in backend, which can easily be forgotten. A better approach is to use refresh tokens and access tokens. Refresh tokens are stored in http only cookie which can only retrieve access token (and refresh themselfs), and access token are stored in browsers memory (like you do with the csrf token) and used to call the API's.
Is there a reason why you chose to store access tokens in memory and not in httpOnly cookie?
@@aniketbhat9840 because you have to manually attach the access token in the Authorization header of your ajax calls, if you keep the access token as an httpCookie, the client can't access it, thus making them useless, on the otherhand clients don't have to worry about refresh tokens, as their sole purpose is to fetch a new access token, and they can be sent in any way even as a cookie.
SameSite policy on cookies and checking origin/referrer headers is a cleaner solution that csrf tokens in my opinion, unless you make an API with some ridiculously bad GET requests.
8:30 but if the token is sent only when the user logs in and you don't save it in local storage, he will lose it if he refreshes the page after being logged in
It is assumed that the client would preserve the token if it's a SPA or your server will keep sending/generating tokens if it's SSR as he explains at 9:10.
Most popular practice is sending a new csrf token for each form. If user opens the "add new user" page, while loading that page's data your client receives a csrf token that is only valid for that form. Which makes it unnecessary to store any csrf tokens. Because when user needs a csrf token, they will receive a new one when the form is loading.
When you have a SPA and do all requests with ajax, you can just send a custom header and check for this custom header to prevent CSRF.
But someone could just easily check the dev tools and look for the header and replicate it in the request no?
there is the same type of vulnerability with CSRF tokens, because they don't expire when another page with the same cookie is loaded. Someone could just make a request using a library like axios, fetch the CSRF token, and then use that in their request.
@@wearr_ Dev Tools have nothing to do with csrf. When you get the access to the dev tools on the computer of another person, you could make anything without csrf. And you cannot do a request with axios to another site. Specialy with a custom header. Even when CORS is enabled, the custom header have to be enabled for CORS requests. Fot this you have to list them in the CORS response header, wich you should not do.
@@wearr_ the csrf token is generated per request (more secure less usable) or per user-session (less secure more usable), so even if some one can request "our api" to get a csrf he wouldn't affect other users with his malicious intents and his "evil" axios skills 😄
@@Venistro if you are appending a header to a request for something like
-my-secure-code or whatever tf you want to do
and then someone looks in the dev tools of the app and sees that each request has that header appended to it, they will just add it to their request. The only way to do it securely is have a crypto key that generates a unique header name, and then the server has to know the results of that to check against it.
@@yaci8330 if you use a templating language like EJS, to use csrf tokens you should just embed it into the html form that is submitted, that is quite literally how a library like csurf recommends doing it for ejs. However if someone makes a web request to the page using axios (http client, not "evil") then they can get the html content, which will come with a freshly generated, valid csrf token in the request. You can then use cheerio to get the token from the html content, and now you might be asking yourself "but that is just purely hypothetical, right?" no. That very same kind of attack was something that I've personally leveraged to get a refresh token from an API from a *different origin*
*cough* cross site request forgery *cough*
This is the note taken from MDN. Maybe we don't need implement it on our own in modern browsers.
Note: Lax replaced None as the default value in order to ensure that users have reasonably robust defense against some classes of cross-site request forgery (CSRF) attacks.
Part of the best practice to "never trust the client" is to not trust that the browsers they're using are modern, uncompromised, and using secure settings. Because you cannot guarantee these things, implementing CSRF protection yourself is critical.
Why do you think so, they literally say that it will prevent "Some classes of CSRF attacks", but not all. It is possible to bypass both Lax and Strict flags, depending on how the application is built. Implementation of CSRF tokens is still vital and without it, the application is pwnable.
@@mach1ne722 you’re right. But I said maybe, up to your business
Thank you, I've been researching CSRF from a developers POV, this really cleared things up. Just one thing to point out that CORS is actually to impose relaxations on Same Origin Policy. We can do this using Access-Control headers. Subscribed for more such content.
It's a very nice explanation but I still have several doubts.
1. If the server checks the CSRF token, why does it need to check also the session if we are sure that the request is done by a logged-in user from their browser?
2. Isn't setting SameSite option in the cookie enough? If not, why?
3. If the CSRF token is stored in js, it's lost after refreshing the site, right? Can we prevent it?
Thanks!
1. CSRF token needs to be tied to the user's session. Because otherwise, anyone can request an action and get the csrf token from his/her request by himself and then use that token to forge the malicious request to other users. If the token is not tied to the user's session csrf tokens from other users will be valid and csrf protection is then broken.
2. SameSite is not enough against sophisticated attacker. SameSite Lax will attach the cookies to the cross-origin requests if the method is GET. Many applications usually accept form parameters from the GET line. For example PHP ($_REQUEST) and Java are very common to do that. Attacker can forge the form action and specify parameters from GET line, which will bypass the Lax flag. SameSite Strict cookie can also be bypassed in couple of more complicated ways.
3.CSRF tokens should be random and unique in ideal case for each form submitted. Upon refreshing the page, the token should be changed in the server's response, so there is usually no need to store it anywhere. Upon a page refresh, it is usually embedded in the hidden input parameter from the form. When the form is submitted the csrf token is there and the server checks it and knows that the request is not forged.
@@mach1ne722 Regarding number 3. tho, lets use the follwing example. Lets say a user sends a succesful login request and receives back a session cookie and a csrf token "1", then through a redirect he sends a request for the main page, with the session id and csrf token "1" , the server expects to receive "1" so it validates his request and sends back the new csrf token to be saved locally: "2". Now when the user sends requests for any page, the server will expect to receive "2" as a csrf token, which is fine since the user has the new value, "2", saved locally.
However lets say the user refreshes the page, the value "2" would be lost and the browser, at best. would send the same request (with token "1") again, which would be rejected. This can be solved by not generating a new token everytime and using the original token always while the user is logged in, but then the issue would arise when the user opens a new fresh tab with the address of your site, in which case there would be no token sent, just the session id, so the user would be rejected in this case as well.
The only solutions to this (as far as i can see) are either asking the user to login everytime he wants to get a fresh csrf token, which invalidates the use of cookies entirely, or to have a certain endpoint of the application (like the mainpage) give out a csrf token with just a valid session cookie, without the need of a valid cstf token, which invalidates the security benefit of the csrf token
Thanks Kyle.
I think modern browsers by default secures your web app against CSRF by using strict CORS policy.
So no need to implement CSRF unless you have enabled CORS. In this case, CSRF becomes mandatory.
CORS policy only works for ajax calls, if an attacker uses a form action to make the request (the one which refreshes the page), CORS policy won't stop it.
Similar is true for using the src attribute of an img tag
@@thatsalot3577could you please clarify me that how exactly CORS policy can prevent ajax requests because as far as I know CORS allows all the requests but just won't show the response
@@akshaykumar-wd8jc that is not how cors work
On browser, before every ajax call, a preflight call is made to the server where method is OPTIONS, if the response headers allow your domain to make a post request only then your ajax call would be able to send anything to the server
sounds the like the refresh-token method. although they both do not eliminate the risk of a malicious attack they just reduce it.
also whether you know that there is a risk of being "hacked" or not, you always assume that it's the case. so not a single app on the earth is secure 100%. it's only a matter of making harder.
0:20 - "Recrest" you say...? I do brush my teeth twice, but I use Colgate toothpaste. So, am I good?
2:29 - "Crewcrest"!? Oh no! I have never brushed with a whole crew! I really need to fix these vulnerabilities.
I think if you ask the user to send back his username or password with the request to delete a ressource, in this case you're protected against CSRF, as the evil site or page doesn't know them even if the cookie is auto sent by the browser, in other words the username or password will act as a CSRF token, because at the end a csrf token is just another secret that you're posting and keeping in a variable in memory and sending it back throught the clients request.
There are so many ways to leak the username and figure somebody's password. Those cannot be considered as enough for the endpoint to be secured against CSRF attacks. That "another secret" is actually random and unpredictable in most cases and username and password are not! In this example, the request to delete a resource usually will not have username and password information in them, but on the other hand some sort of ID that respresents the resource that is being deleted. More often than not the resource's identifier is publicly known.
Its almost as if i try something last night. You get a message then make a video on it. Great video
Very clear, and fun. So in summary the main issue being other sites can use the targeted sites cookie. And by storing a token as a variable it's not accessible from other sites.
However in the current example if the user refreshes there page they would lose the token and need to re login. Or should that token be stored in the browser?
usually, in SPAs even if you have already stored session-cookies you need to make a login request on start to gather all the side information about user from server
@@real-kirillov that would be a very poor user experience. SPAs use refresh/access tokens for maintaining authentication state.
That's a great point. I also posted a comment related to having to re-login when page is refreshed. Storing it in browser is bad as it can be retrieved my hacker and set a input value.
I would no use a token on cookie. But maybe local storage since that is not sent automatically with the request. The request requires to add that param from local storage manually... That would solve this issue
thanks for the video, in my client applications, i send the tokens over headers for each request, so that works for me
But keep in mind there are XSS attacks
@@lukewalker4841 yes i use xss clean on my server for that
Hello Kyle, thank you for this tutorial, I think another alternative to prevent the evil site to access is to set cookie sameSite to lake
i had a lot of question about csrf and you answered all with that one video
Great, one way is that server can pass a JWT token which the logged in client can save and use it in the future requests. Hence using secure tokens such as JWT, prevents CSRF attacks.
great and clear explaination of CSRF! always been using but just getting clear of how it is is awesome, thanks!
Don't use cookies. Try to implement REFRESH and ACCESS tokens.
But always remember that there is an attack called XSS ;)
the real question is why the evil page can get access to your sessionID and send with the POST. it should not.
this worked is because you are using a simple post request
if you had body in your request then a preflight request will be sent to precent this from happening
so another solution would be to not allow get requests or simple post requests to modify your backend resources
or I am missing something??
there is no preflight or CORS with requests generated by forms or links
@@RoterFruchtZwerg ok I will make sure to test this.. thank you
If you recreate your token on every request but just store one current token in session, you will run into problems with multiple tabs... as soon as you open a second tab, the token of the first tab isn't valid snymore
I just started learning NestJS (uses ExpressJS under the hood) and this is a super important thing to note when designing an api. Thanks for the video. 🙏
With NestJS you are most likely using JWT Tokens (at least if you follow their authentication docs).
With JWT Tokens, you don't have to worry about CSRF.
@@dotnetapp7503 honestly not a fan of JWT…
@@liam.brewer is there a reason?
JWT is better useable for apps it has less attack scenarios and in nestJS you get em right out of the box.
@@dotnetapp7503 haven’t had a good DX with them and i’ve honestly heard bad things about their security for production grade applications.
@@dotnetapp7503 of course you have to worry, its not about jwt or sessions, its about whether or not you store your token in a cookie
Your content is dope. I've learned a lot of stuff from you. Thank you
So what would happen if after you login, you refreshed the page? Wouldn't your CSRF token get lost? You wouldn't be able to run any action unless you logged out and then logged in again. right? Any way of solving this without storing it anywhere visible/stealable?
When you saved the tokrn locally if you refresh the page the tokne will gone meaing if you make another request you will not have the crsf so what can i d about it
Great video. Shared it with my dev buddies in college. Thanks!
But, if you login to get session and then simply refresh page you will lose that csrf token making page unusable. Unless it is fine to force user via login process each time page is refreshed.
You can use jwt, and keep it alive for certain amount of time
I have listed steps. Pls correct if i'm wrong.
1. send csrf token on login both response body and set response cookie
2. save csrf token on locally.
3. send the saved csrf token on each requests.
and i have one doubt also. do we need to send and save this csrf token on all api requests ?
Really great video Kyle...
What I don't understand is, how cookie from localhost:5500 is able to accessed by localhost:5501?
Is browser don't enforce, each domain have their cookies seperated?
cookies shared across ports, localhost only counts as domain
Cookies are shared across tabs.
Thanks for sharing Kyle. Can we prevent CSRF attacks by using JWT?
Yes, as long as you're using JWT over headers, not cookies
@@Urizieli owh, so i don't need to use CSRF token as long I send the JWT through the header right?
@@ganesdipa6855 Correct. You can consider JWT as a replacement for CSRF token.
The attack vector in csrf isn't explained here. A user needs to be logged into say a bank site, then goes to another site say email in a new tab. In the email they click a link that goes to the bank app and uses the cookie from the bank app . Session storage is tab scoped for jwt.
@@peterhorton9063 when the person clicks the link it isn't CSRF since the user actively navigates to the tab of the bank and the malicious page doesn't send the request without the user noticing. See the CSRF definition on MDN.
Dude please take a breath in between so I can think😢
How is that his problem that you can't listen and think at the same time?
Super thank you man, I was always wondering without the time to search 😅
I'm glad I could help!
I was waiting for csrf because I was unable to answer this question in my interview.
Hello Kyle, What will happen when the user will refresh the browser window? Will the CSRF token be deleted. If so user needs to login every time the user refresh the browser?
But if you reload the page, the client csrf variable will be gone, right? So, for futher api requests you have to relogin?
3:19 if SOP is enforced by default in modern browsers, why do you need CORS? Isn't CORS to relax the default SOP policy?
I don't understand how CORS doesn't prevent that...An origin consists of protocol, host and port number. If the port number differs, the cors policy should block the response! Actually, the post is not even triggered, a preflight request with OPTIONS method checks if the post can be done. Am I wrong?
Guy is forcing the same site to none and wonder why his app is csrf vulnerable 😐
Why using cookies, why not JWT with authorization header? (since in this video, however you are attaching csrf token with every request)
I don't understand the part where the in evil page, the cookie is automatically sent. I thought, we have to explicitly pass the data in api call body to process it.
Main thing u don't understand is, where the normal page is sending the cookie? If the cookie is sent automatically then it means from that site the cookie will be sent with every api call??
sorry, help me understand,
what if someone created their own page to retrieve the session and make it a req.body to send to /delete-user?
So someone can do a request from the original site, copy the CSRF token, put it inside there own process. And it would still work for that session...? Not seeing the 'security' here ?
I am using something else, client id and client secret which needs to be passed every time you send an request to the api, I have created a file with default clients (each one has client id and client secret) which has a pc client, a phone client and a web client.
But I think that this form thing will still be a problem
but if everything is intercepted by middleman via his backend, those information can be still be obtained right? provided he knows where you keep your csrf token (whether in html input form/local storage/cookie)
Why not just store the jwt in memory and send it with requests if cookies are the main culprit here?
Okay But how this goes with my authentication with JWT.
I am storing short lived accessToken in some variable/state which is sent with Authorization Header.
RefreshToken in some httpOnly Cookie.
Do I still need to worry about CSRF?
did you find the answer?
@@atbt6343 not really. I didn't care about csrf attacks 😐
4:16 Ok i'm not getting this at all. If the "evil" site is a completely separate domain, how could it get the cookie to begin with? I can't make sense of it. Like, from my website, i could hit a youtube endpoint to delete one of your vids and somehow magically i already have your authentication/authorization cookie?
I think it's more like, you logged in your own session, and while it's active you clicked a malicious link which deletes the vids from the current session, in this case your vids.
As mentioned above, the point is to make a user logged somewhere to click from its browser on a malicious link by himself. Because as long as you click yourself on a link, all cookies and limited-access routes are available, no matter from which tab/domain you're trying to access.
the 3rd party site never gets your session cookie, but it uses the fact that your browser sends it along with the form request. This only works with same-site "none" Cookies though, which were default until one or two years ago. now the default is "lax" and you need to explicitly set it to "none".
kek, i've been searching about CSRF for about 2 days and then this video get published.
KEKW
I'm using PKI to protect my endpoint, where the token (the message) is never exposed and the signature is both verifiable and unforgable.
Would I be correct that requiring a custom header with the POST of `foo=bar`, or whatever you wish, would be enough if you expect all authenticated requests to be done with JavaScript ajax requests and not HTML form posts? From what I understand, there is no way to add a custom header on an HTML form post. So all this managing of extra tokens is unnecessary.
Hello Kyle, can you do a web search engine tutorial? I've been trying to find one that doesn't include React or something like that.
Thankyou . Crystal and clear explanation
Couldn’t this also be prevent by checking origin. Even if it’s a micro service setup, with man instance of the came backend.
Or even move that check of origin to the first layer check like an api gateway?
I'm confused, my application is using jwt for authentication so do I still need CSRF Tokens?
Hello Kyle, If I store authentication token in local storage (instead of cookie), would it save my site from CSRF ?
Yes, and no. In that case you are open to XSS attacks, because a potentially malicious JavaScript code can steal data from your local storage, and thus make a combined attack by leveraging XSS + CSRF.
it sounds like use jwt baerer token instead because he did the same job but not need cookies. Any objections?
Hi Kyle. I'm using Spring Security in my server side app. I want to use fetch api in a pure Javascript client call. I receive the JSESSIONID and XSRF-TOKEN cookies in client browser but I cannot access through Javascript to the CSRF token value. How can I do it?
Many thanks.
CSRF does not stop most attacks. All you need to do is a fetch request to what ever end point has the token then pass that along with the request. For real security use JWT or client ssl certs. Obviously as others have pointed out that correct CORS setup would have stopped this attack.
a 3rd party site can't do a fetch request on the endpoint that delivers the csrf token. Also CORS is not relevant. CORS does not apply to GET/POST requests sent via link or form. CSRF Tokens are the right fit here.
I'm a beginner dev and after this video I dove deeper into the subject and it's all pretty complicated. I just decided to settle with my express server using csurf, each time the react frontend is refreshed it gets a new csrf token from the /csrf-token endpoint and stores it app-wide for post/delete/put requests with zustand, this is enough for decent security right?`
I read that samesite strict-policy is enough but since my backend and frontend are on different domains it doesn't seem like a sensible solution for me
Csurf has been deprecated for around a year.
if user refresh the page, the token is lost. Should I store it in local storage?
In client if we only store token in the variable, does that mean if we refresh client page, user will be logged out ? How to fix that
What is the difference between this and conventional JWT. I searched and found only two discussions about it, and it didn't show the difference
jwt are a solution for another problem and when transmitted in a cookie actually be the concern in this context.
Jwt are used to store server data (e.g. user information) on the client side. The server has signed a jwt and can be sure that the payload of the jwt (e.g. user login) was 100% created by the server and the content can be trusted. Often a jwt is transmitted automatically by the browser in a cookie, and this "automatically" is in my opinion the critical point here.
However in my opinion the content of this video is outdated and creates confusion, because the problem here is solved with the samesite property of the cookie itself (which is funnily disabled in this video and makes me wonder why try to explain a solution for a problem that is created in the first place with no reason). Actually no code is required anymore to check on the server side for a random session token, because modern browsers do the "automatic" transmission of cookies differently now (search for same site options strict or lax). Even though I respect Kyle for his knowldege, but on this one, it looks to me like solving a problem that does not exist anymore with technology of 4 years ago.
I just wanna to know that you write condition to not match crsf token when we request to auth api but there is one bug on there is we can send random uuid to api request and server will not check that uuid is generated from server side or evil side.
Here is My Question What if the Authorised Person Spam By Doing Multiple Form Submission By Doing Through Bots How to Prevent This Too ?
You can use rate limiters
What about when u refresh? The js memory is cleared it won't work
From the evil page, we made a request to port 3000 right? Then how did it reach 5500? Can someone explain pls? Also how was evil page created under same domain?
The server with the API is running on port 3000. The malicious page is simply HTML; it doesn't need to run under the same domain. It exploits automatically sent cookies to make requests to the API on port 3000, resulting in the deletion of the user. The client, running on port 5500, fetches data from the same API. After the attack, it can't retrieve the user because the user no longer exists.
How clone is ruuning at 5500 and express at 3000 when in same directory.
so even if i have jwt auth by cookie httpOnly should i use csrf in every request that changes app state?
i other words after login for jwt token should i give csrf token for api client? i force this client to send it back in body?
Hello Kyle,
Really Nice and clear explanation, Thanks a lot.
Can you please help on, How to handle scenarios where the user refreshes a page?
As CSRF is stored in a local variable, the page reload will vanish its value...
Store the Csrf token on the DOM itself as a hidden element.
Since csurf is deprecated, what are some of the alternative for express js web applications?
Very interesting. Thanks Kyle!
Excellent!! One of the best Explanation for CSRF
Thanks!
No problem!
can you just store session key in localStorage and include in request body instead of cookie?
yes, but then your website is vulnerable to XSS attacks (Cross-Site Scripting)
@@lukewalker4841how does using localStorage make website vulnerable to XSS attacks?
What is the link to what Kyle recommends we "check out first" at 1:03?
Kyle is speaking so quickly that auto caption translates what he saying to that he's putting the link in the "cards". ?????
Does sending an api-key in header for all api requests work as well?
You can capture the url that sent (referred) the request, right? Also, any chance you feel like teaching us a thing or two about the VirtualKeyboard API?? I was just about to toy around with it, those touchscreen keyboards always try and ruin a UI!
Awesome!
Could you not store cookie against specific domain?
Can you do a video about implementing a content security policy please?
the code is better than the talking.
Funny observation: You started by saying CSRF, then you said CSFR a few times and then by the end you said CSFRF 🤣🤣
Can someone please explain to me whether I still need CSRF protection if I use the JWT authorization token?
Yep! Although I'm not a security expert, I don't see any way JWT could guard you against CSRF attacks. CSRF attacks exploit the fact that a user's browser automatically sends certain cookies. If you store your JWT token in a cookie, there will be no difference at all. That's just my understanding of the topic. As I said, I'm not an expert, so you can investigate it yourself.
Is mandatory do login? I see many sites that for example have a cart in home page without login 🤔. How to do secure home page where all people can edit ?
one question, adding jwt would solve that also?
I'm not sure how to store firebase authentication tokens for for Sveltekit & SSR. Probably I should store it in the memory but I guess that means I should send the token with every request. I guess unless there is another way.
secret: your website is never "secure" regardless of what you do,
Thank you!
and why do you still need the cookie with this?
in this demo case you don't, but session cookies have use-cases and are widely used. So it's important to learn about this.
why you dont try that evil page out from an sandbox VM to let us see what happens? and big thx to your videos! they are amazing and clear to understand
How does this work with the refresh/access tokens jwt flow?
Thank you
0:50 CSFR eh Kyle? 😉
Rails does this for you.
laravel too