Chapters
43 Chapters
|
5:00:38
|
This course is archived!
While the concepts of this course are still largely applicable, it's built using an older version of Symfony (4) and React (16).
Login to bookmark this video
-
Course Code
Subscribe to download the code!Compatible PHP versions: ^7.2.0
Subscribe to download the code!Compatible PHP versions: ^7.2.0
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Subtitles
Subscribe to download the subtitles!
Subscribe to download the subtitles!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
42.
CSRF Protection Part 1
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
Subscribe to jump to this part in the video!
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
While the concepts of this course are still largely applicable, it's built using an older version of Symfony (4) and React (16).
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"php": "^7.2.0",
"ext-iconv": "*",
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"doctrine/annotations": "^1.0", // v1.8.0
"doctrine/doctrine-bundle": "^1.6", // 1.9.1
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.3
"doctrine/doctrine-fixtures-bundle": "~3.0", // 3.0.2
"doctrine/doctrine-migrations-bundle": "^1.2", // v1.3.1
"doctrine/orm": "^2.5", // v2.7.2
"friendsofsymfony/jsrouting-bundle": "^2.2", // 2.2.0
"friendsofsymfony/user-bundle": "dev-master#4125505ba6eba82ddf944378a3d636081c06da0c", // dev-master
"phpdocumentor/reflection-docblock": "^3.0|^4.0", // 4.3.0
"sensio/framework-extra-bundle": "^5.1", // v5.2.0
"symfony/asset": "^4.0", // v4.1.4
"symfony/cache": "^3.3|^4.0", // v4.1.4
"symfony/console": "^4.0", // v4.1.4
"symfony/flex": "^1.0", // v1.21.6
"symfony/form": "^4.0", // v4.1.4
"symfony/framework-bundle": "^4.0", // v4.1.4
"symfony/lts": "^4@dev", // dev-master
"symfony/monolog-bundle": "^3.1", // v3.3.0
"symfony/polyfill-apcu": "^1.0", // v1.9.0
"symfony/property-access": "^3.3|^4.0", // v4.1.4
"symfony/property-info": "^3.3|^4.0", // v4.1.4
"symfony/serializer": "^3.3|^4.0", // v4.1.4
"symfony/swiftmailer-bundle": "^3.1", // v3.2.3
"symfony/twig-bundle": "^4.0", // v4.1.4
"symfony/validator": "^4.0", // v4.1.4
"symfony/yaml": "^4.0", // v4.1.4
"twig/twig": "2.10.*" // v2.10.0
},
"require-dev": {
"easycorp/easy-log-handler": "^1.0.7", // v1.0.7
"symfony/debug-bundle": "^3.3|^4.0", // v4.1.4
"symfony/dotenv": "^4.0", // v4.1.4
"symfony/maker-bundle": "^1.5", // v1.5.0
"symfony/phpunit-bridge": "^4.0", // v4.1.4
"symfony/stopwatch": "^3.3|^4.0", // v4.1.4
"symfony/var-dumper": "^3.3|^4.0", // v4.1.4
"symfony/web-profiler-bundle": "^3.3|^4.0", // v4.1.4
"symfony/web-server-bundle": "^4.0" // v4.1.4
}
}
What JavaScript libraries does this tutorial use?
// package.json
{
"dependencies": {
"@babel/plugin-proposal-object-rest-spread": "^7.12.1" // 7.12.1
},
"devDependencies": {
"@babel/preset-react": "^7.0.0", // 7.12.5
"@symfony/webpack-encore": "^0.26.0", // 0.26.0
"babel-plugin-transform-object-rest-spread": "^6.26.0", // 6.26.0
"babel-plugin-transform-react-remove-prop-types": "^0.4.13", // 0.4.13
"bootstrap": "3", // 3.3.7
"copy-webpack-plugin": "^4.4.1", // 4.5.1
"core-js": "2", // 1.2.7
"eslint": "^4.19.1", // 4.19.1
"eslint-plugin-react": "^7.8.2", // 7.8.2
"font-awesome": "4", // 4.7.0
"jquery": "^3.3.1", // 3.3.1
"promise-polyfill": "^8.0.0", // 8.0.0
"prop-types": "^15.6.1", // 15.6.1
"react": "^16.3.2", // 16.4.0
"react-dom": "^16.3.2", // 16.4.0
"sass": "^1.29.0", // 1.29.0
"sass-loader": "^7.0.0", // 7.3.1
"sweetalert2": "^7.11.0", // 7.22.0
"uuid": "^3.2.1", // 3.4.0
"webpack-notifier": "^1.5.1", // 1.6.0
"whatwg-fetch": "^2.0.4" // 2.0.4
}
}
6 Comments
Hi weaverryan am sending a post request from a react app to a sf4 api app, should i delete $event->isMasterRequest() because an option request comes before my post request and sf treated it as a sub request and return 405 error, what should i do?
Hey @adam!
Hmm.
> because an option request comes before my post request and sf treated it as a sub request
This should *not* be the case. Both requests - the POST and the HEAD - are *real* requests into your app and are both *master* requests. I'm 100% sure about that :). So if you're seeing that the HEAD is a sub-request, it must mean that while handling the HEAD "master" request, something else internally is making a sub-request. Basically, my point is: look a bit harder and what's going on... because something isn't right :).
Btw, that HEAD request is likely for CORS. Are you using something like NelmioCorsBundle to return the CORS headers? If so, *it* should be the thing that is responsible for returning the correct response (instead of allowing it to hit your controller, which will return a 405 if it's only set up to respond to POST requests).
Cheers!
Hi Ryan,
I hope you’re excellent, it’s been a long time.
We need to be careful here because I feel the information mentioned here is misguiding:
> First, disallow AJAX requests from all domains except for your domain. Actually, this is just how the Internet works: you can't make AJAX requests across domains.
It seems you are referring to the same-origin policy. However SOP is designed to prevent websites from reading credentialed responses from a third party but not to prevent from posting data. It is possible to mutate state on another website via XHRs without any problem, SOP only makes it impossible to read the response. Yes, we can make cross domain XHR requests, we are just not allowed to read responses (depending on the CORS policy in place). It is also possible to make that XHR credentialed using ‘withCredentials: true’.
So here, with current tuto in place, we are subject to CSRF attacks.
> This is the second rule for CSRF protection: only allow data to be sent to your server as JSON.
The way data is represented is nothing to do with CSRF protection - using json does not protect users further. The risk here comes from the session-based authentication. An authenticated POST is an authenticated POST.. the way the data is represented is irrelevant for a CSRF defense.
Protecting SPA against CSRF and XSS attacks is not easy, but here I believe both points would misguide users in thinking they are protected when they would not be.
Just a heads up!
P.S. I think it would be helpful to a lot of people if you covered a safe login authentication for an SPA which would be immune to CSRF login attacks, this is not easy to do, whether one uses session based authentication OR token based authentication.
Hey Anthony R. !
Just an update on this. Someone else pointed out that these protections ay in fact *not* be enough as browsers have introeduced a feature (https://github.com/pillarjs... that indeed may allow application/json requests to be sent across JavaScript without preflight :/. And more broadly, browsers seem to think that relying on this is not sufficient. We're adding a note about this, and so, indeed, using CSRF tokens is best. We show how in this tutorial and there is also the wonderful bundle you linked to: https://github.com/dunglas/...
Cheers!
Hey Anthony R.!
Thanks for the post - seriously :). This was a tough chapter because it's security-related and I wanted to get it right. Now, I really want to make sure that I've got it right, with your help. So, I have a few questions:
How? I mean, I know one way: via form posts, which could be on the bad domain with an
action=http://thegoodomain.exampleor even with the good domain form hidden in an iframe on the bad domain. But, there is no other way to make a request via XHR, or JSONP (other than a GET request) to another domain, unless that domain has allowed it via CORS, right?Actually, that's not entirely true :). As I mentioned above, there IS certainly an attack vector to make a POST request to another domain using a traditional HTML. But, HTML forms always submit with
Content-Type: application/x-www-form-urlencodedorContent-Type: multipart/form-data. There is no way for the "bad" domain to add a form to their site and change this fact. So, if our API always checks for theContent-Typeto beapplication/json(or really, anything other than the 2 that could be sent by a form), then it eliminates the CSRF attack from an embedded form.Let me know what you think or what I'm not thinking about. From my perspective, the protection is a combination of (A) disallowing cross-domain via XHR... which only leaves GET requests and POST requests initiated by a form. And then (B) checking always for application/json Content-Type, which eliminates the form request. The only thing remaining are GET requests from other domains, which is safe because the attacker can't read the response (assuming, of course, that you haven't done something crazy and "mutated" your server on a GET request). Here's a source I was referencing for this section: https://github.com/pillarjs/understanding-csrf
Cheers!
Hi Ryan,
I have now re-read your post as well as your answer.
> From my perspective, the protection is a combination of (A) disallowing cross-domain via XHR... which only leaves GET requests and POST requests initiated by a form. AND then (B) checking always for application/json Content-Type, which eliminates the form request.
Yes, yes and yes, not sure what was happening inside my head. I forgot preflight requests would fire on cross-origin POST request WHEN Content-Type is 'application/json' (more precisely when it is NOT one of application/x-www-form-urlencoded, multipart/form-data, or text/plain). I totally forgot about preflight... thank you!
So, yes, everything makes a lot of sense... apologies for the confusion and thank you so much for being so awesome.
Based on my very first point, I would still rephrase the following:
> First, disallow AJAX requests from all domains except for your domain. Actually, this is just how the Internet works: you can't make AJAX requests across domains.
into the following:
--> First, disallow AJAX requests from all domains except for your domain. Actually, this is just how the Internet works: you can't make AJAX requests across domains WHEN CONTENT-TYPE IS NOT ONE OF application/x-www-form-urlencoded, multipart/form-data, or text/plain.
Because, technically, it is possible to send cross-origin POST XHR requests with any of these 3 content-types and even if you can't read the response, you can mutate state. I.e. there is no preflight for these as they are Simple Requests:
https://developer.mozilla.o...
P.S. For those that would still would want to submit via `application/x-www-form-urlencoded`, I found the DunglasAngularCsrfBundle after some digging!
https://github.com/dunglas/...
"Houston: no signs of life"
Start the conversation!