WEBVTT

00:00.110 --> 00:06.230
So finally, with the CSRF protection, it's time that we verify the token.

00:06.230 --> 00:11.300
So we have the logic already inside the contact post route handler.

00:11.330 --> 00:14.690
Just this function is not implemented.

00:14.690 --> 00:18.800
Let's jump to it right now to verify the token.

00:18.800 --> 00:27.710
We're going to use hash function from PHP, which is a safe way to compare tokens, passwords, or any

00:27.710 --> 00:32.930
other hashed strings that need to be compared securely.

00:33.050 --> 00:43.820
Now, it's important to just pass this token that was generated as the first argument, and the second

00:43.820 --> 00:49.010
one should be the token that was sent or provided by the user.

00:49.100 --> 00:54.770
In our case, this means the second argument would be the token sent with the form.

00:54.770 --> 01:02.850
So since we are all about security, it's also important not to use the normal string comparison operator,

01:02.850 --> 01:09.990
but the safe equivalents that are used and created for security purposes.

01:09.990 --> 01:13.140
So let's get started with this function.

01:13.170 --> 01:19.980
Now the first step here is to get the current token and time stored in the session.

01:19.980 --> 01:27.510
Since we already used the token as an argument name, let me call this variable stored token.

01:27.510 --> 01:32.310
And if any of those is not set.

01:32.340 --> 01:38.310
So either stored token is not set or time is not set.

01:38.460 --> 01:42.930
We just return false verification has failed.

01:43.080 --> 01:50.790
Next up, if the token is expired we are using time for that.

01:51.840 --> 01:53.220
We return false.

01:53.220 --> 02:01.020
And it might also be a good idea to remove both values from the session.

02:01.020 --> 02:04.770
So let me use On set for both operations.

02:07.140 --> 02:15.600
So both the CSRF token and CSRF token time should be removed.

02:15.840 --> 02:25.980
Now we can optionally think about using this function for both and accepting the token and time as parameters.

02:26.010 --> 02:32.430
If any of those would be null, then we would be removing from the session instead.

02:32.850 --> 02:36.570
Let's leave it for the end of this video for now.

02:36.600 --> 02:42.090
Let's just make sure we remove those values from the session and return false.

02:42.120 --> 02:49.290
Now at this point we need to validate the token if it's exactly the same as the one we have stored,

02:49.290 --> 02:53.580
because we obviously have it and it is not expired.

02:54.660 --> 02:57.780
So let's see if the token is valid.

02:57.780 --> 03:02.700
We're going to use hash equals.

03:02.700 --> 03:14.080
The first value is the start token and the second one is the user passed token, or just use an empty

03:14.110 --> 03:14.890
string.

03:15.580 --> 03:23.590
But I think that at this point there is no chance that the token no, there actually is a chance that

03:23.590 --> 03:24.520
the token is null.

03:24.520 --> 03:27.610
So yes, let's pass an empty string.

03:27.640 --> 03:34.030
Now those can't be the same because the start token will always contain a value.

03:34.690 --> 03:36.700
So that was the final check.

03:36.700 --> 03:46.060
If the token was valid, we need to generate a new token for the next form submission.

03:46.870 --> 03:53.200
And we just return the value of the valid variable.

03:53.200 --> 03:58.420
So that was the final result okay.

03:58.420 --> 04:00.970
So we just need to try this.

04:00.970 --> 04:08.470
But let's also move this code into a common function because we've got them for a reason.

04:10.420 --> 04:15.910
So here, if the token would be null.

04:18.040 --> 04:22.990
We're just going to unset those values and return.

04:23.950 --> 04:27.700
Otherwise we generate a new token.

04:27.730 --> 04:32.230
This means that the token can be null.

04:32.230 --> 04:39.460
So essentially passing null means that I just want to remove the token altogether from the session.

04:42.070 --> 04:45.880
And now I don't need to worry about unsetting those.

04:45.880 --> 04:54.760
I can just say set CSRF token and time and I just pass null.

04:54.760 --> 04:57.820
This means remove it from the session.

04:58.540 --> 05:03.010
So before we test that, I think I might have an issue here.

05:03.130 --> 05:08.630
So in the template I'm Sending the token using this field name.

05:08.660 --> 05:10.640
CSRF token.

05:11.270 --> 05:17.210
On the other hand, in the root handler I'm using the underscore notation.

05:17.210 --> 05:18.710
Let's fix that.

05:20.000 --> 05:24.020
Close those files and let's refresh the form.

05:24.020 --> 05:31.190
And let me just say this should work because indeed it should work.

05:31.730 --> 05:33.800
There is our message.

05:33.860 --> 05:41.300
Now let me first do a quick test that will generate this token for a short time.

05:41.330 --> 05:42.320
Five seconds.

05:42.320 --> 05:46.130
So we can see how it behaves when it expires.

05:47.090 --> 05:48.620
Jump to a contact form.

05:48.620 --> 05:50.270
We've got the token generated.

05:50.300 --> 05:51.500
No rush here.

05:51.530 --> 05:55.580
I'm slowly gonna fill it out so it takes at least five seconds.

05:57.410 --> 05:59.960
I write this would fail.

06:01.910 --> 06:07.310
And this has failed because I set the token lifetime for five seconds.

06:07.310 --> 06:11.000
So before I manage to send the form, it has expired.

06:11.030 --> 06:19.160
Now, obviously it doesn't make any sense for the token to be that short, but I just wanted to demonstrate

06:19.160 --> 06:26.810
that the expiry time of token is indeed real, and it expires after five seconds.

06:26.840 --> 06:31.670
Let's bring it back to something sensible, which is 30 minutes.

06:31.670 --> 06:42.320
And the ultimate test should be testing our malicious website that previously has sent the form using

06:42.320 --> 06:44.810
the beautiful kitten.

06:44.900 --> 06:46.700
Let's do it next.

06:46.700 --> 06:54.590
So again, I'm just opening this attack file with the kitten, and I think it takes around eight seconds

06:54.590 --> 06:57.560
before it tries to submit the form.

06:57.560 --> 07:08.340
And as you see, we are protected because that website can't have the token because we always generated

07:08.340 --> 07:10.350
on our page.

07:10.950 --> 07:19.620
Now, if you are thinking if this malicious person is able to grab your token somehow and also send

07:19.620 --> 07:27.330
it together with the form to validate the form, well, there is no way because the token, the CSRF

07:27.360 --> 07:34.080
token, is being generated and kept per user inside this user.

07:34.080 --> 07:36.360
Individual session data.

07:36.720 --> 07:41.310
You need to understand that sessions are unique per user.

07:41.460 --> 07:46.560
There is no way to share the session between different people.

07:46.920 --> 07:55.170
Now there are some things that we would have to remember about with this setup, and it is to verify

07:55.170 --> 07:57.990
the CSRF token inside the root.

07:57.990 --> 08:01.440
If you don't do that well, there is no protection.

08:01.470 --> 08:07.710
Additionally, you always need to generate the token inside the form.

08:07.710 --> 08:13.290
So that's another thing and you need to include it inside the form.

08:13.290 --> 08:15.660
This can be solved differently.

08:15.660 --> 08:20.610
You might have some function that will just generate this output.

08:20.610 --> 08:27.600
That may be much easier to use than having to worry about adding this input every single time, and

08:27.600 --> 08:30.660
also that token verification.

08:30.660 --> 08:33.480
Well, this can also be generic.

08:33.480 --> 08:43.200
For example, you might add some generic case in the router that checks if the specific request is of

08:43.200 --> 08:50.670
type post, and then it always asks or verifies the CSRF token.

08:50.670 --> 08:58.410
So this would be most definitely a more generic approach that will by default protect all the routes

08:58.410 --> 09:04.080
which are of type post, which is typically used to send forms.

09:04.080 --> 09:08.850
But I'm leaving that up to you to explore this on your own.
