Today, I will configure a protected application
against the CSRF attack. I will configure both my Sprint boot backend and my Angular frontend
to see how they interact together. To protect my application against the CSRF attack, I
must configure both the frontend and the backend. Because the CSRF protection ensures
the synchronization between the frontend and the backend. The CSRF protection ensures that no
attacker steps in the middle. But let's see how the CSRF attack works. Let's pick a frontend
application. Every time the frontend makes a request to the backend, the browser sends all the
cookies related to this backend. Those cookies contains the authentication information, the
session information. The frontend has nothing to do, because it's the default behavior
of the browser to avoid the JavaScript to access and manipulate the cookies. Let's say now
I receive an email with a link. The link will open a hacker website and make a request to my bank
website. As the browser stores all the cookies, I'm already connected to my personal account.
And now the script sends some automatic request to withdraw €200 from my bank account. This is a
very simplified workflow on how the CSRF attack works. And now, how do I prevent this attack?
As the browser sends automatically the cookies, I must have a second token stored in a different
system. My frontend must request a token, a CSRF token, and store it in a different storage
system, a variable or the local storage. The backend will associate each generated CSRF token
with a user session. And when calling the backend, the frontend must include this new token. I can
send it in a header. When the backend receives the request, it receives both the cookies and the
CSRF token. With the cookies, it associates the request with a session, then check if the CSRF
token belongs to this session. As the CSRF token is not in the cookies, the browser won't send it.
So an attacker won't be able to read the token and the backend will reject any request without
this token. As said, to be protected against the CSRF attack, I must configure both my frontend
and my backend. So let's create both projects. Angular for the frontend and Sprint Boot for the
backend. But before going remember to subscribe to my channel. I just ask you as simple click. I will
start creating a Sprint Boot project with spring initializer. I change the group name, sergio,
CSRF. The artifact, backend. The name. Ensure it's a Maven project. With Java. The latest
version of Spring Boot. A jar packaging. I will still with Java 17. And add dependencies. Web
to create some endpoints. Security to protect my application. And Lombok for
the code generation. That's all. Let's download the project and open it with IntelliJ. Let's start by creating
two endpoints. One to get all the available messages and another to
append new messages. But first, I will comment the Security dependency. I want
to first test my endpoints. Then I will add the authentication. I reload the Maven
project. And let's go creating the endpoints. I will create a package first,
controllers, and the controller content. Controller. It's a rest
controller the first one get all messages. It returns a ResponseEntity with
ContentDto. A dto that I will create later. messages, ResponseEntity, ok. From a service. I will inject the service. And add the Lombok annotation. Let's continue creating
the second endpoint. I will create the service later. This time is a post to create messages. It returns a
ContentDto, with a request body. And returns the result of the service. But
when reading, in fact it returns a list of messages of content. Let's start by creating
the Dto. It will be in another package named dtos. And inside ContentDto as a record.
Only one field, message. Now I can import the Dto. Okay. Let's create the service
in another package. Oh let's move this package. And create the services package. Let's add the service annotation. And the two methods. I can import it
now. I create the method get messages, which returns a list. And let's create the create messages method. I will create a list in
the service where I save all the messages. That's what I return. And when creating, I just append and return the created message. That's
all. Okay. Let's try this first to validate it. I have an error, I must import the list. Let's start
this now. I run my application, I run my backend. Started correctly. And from a terminal. curl localhost 8080 messages. It returns
nothing as my list is empty by default. Let's create a message. Let's put the header first.
content type application Json. And now the data, messages, message hi. Okay. It returns
me the created message. And now if I fetch all the messages, I have this one. This
works fine from my terminal. But it won't work if I request it from my frontend.
It won't work because of the CORS. So let's add the CORS configuration. Let's
create a config package where we put all the configurations. And I will put the
CORS configurations in a class named web config. Add the configuration
annotation. And the bean web MVC configurer. I will just
implement the CORS mapping. This mapping will be applied to all the routes. I allow origins, just my frontend. My frontend will be running on the port for 4200. I allow some headers. Like content type, and X CSRF token. This will be the header used by the
CSRF token. I also allow some methods, get and post. Finally, I allow the credentials.
This is to accept receiving the cookies with the credentials. I have two end
points and the CORS configuration in my backend. Let's create the frontend
project and wire all together. As said, I will create an Angular project. NG new frontend.
I will leave the default values, no routing, CSS, and it will download the default dependencies.
Now I will just add one dependency, material. Yes, let's download the latest version. And I will pick the default theme. I want to use the typography
styles. Okay, I continue accepting default values. That's it. Before starting, I
will switch to a dark mode. Now let's start the project. Okay. That's the default
content of an Angular project. I will delete all and start by creating
a simple header with a title and a logo. I will start by downloading
an angular logo. I will put it in the assets. Here it is. Now I will
go to the app component and delete all. And just leave my header. Okay, the header is there. Let's modify it. Let's start by a header tag. And put my logo. assets logo PNG. I will put
some style on it. And alternative, logo. Let's see it. Okay, yes. I will also add a title, app title, my CSRF app. Here
it is. But as said, I will apply some style. From app logo, and to app header. First of all, in the logo, I will set a height. Better. And app title, I will set the font size for the title. And in the header, I
will just add some background color. Okay, a fixed height. Some padding, some color for the
text, and align all of this in the center. Let's see the result. Yes. Let's continue
with another component to display all the messages. NG generate component
content display. Let's name it. I go back to the app component and
add this new component. I will wrap it into another div. I will add some style to this. div content display. Here it is. I will
add some style to this main component, just to center it. Some padding. Some marging. And the width. Okay. it's in the
middle now. Let's go to the content display component. I will go to the materials
website to download a card where to display the content. Let's go to the components.
Go to the cards. Let's see some examples. Yes, this one. View the code. I will pick the first one. Past. I delete the buttons. Let's
see the result. Okay, yes. I must add the imports in the typescript file. Standalone and import the
buttons and the card. As it's a standalone component, I must add it in the module. Okay, better now. Let's go to the
typescript and read the content from the backend. I will read it on the onInit method. I must inject the HTTP module,
constructor private HTTP client. I must import it, import htttp client from angular
common HTTP. HTTP get. I will get a content type from the URL. HTTP LocalHost 8800 messages.
And subscribe to the result. It will be a data of content. And I will store the results into a
variable messages. And read all the content, the field message. Let's create this variable
messages which is a string and initialized empty. Content.ts export class content with only one field. Constructor, a public message, which is
a string. But to use the HTTP client, I must go to the modules and import the HTTP module. From angular common http. And add it in the imports. Better. Now, I
will edit the HTML to be dynamic with the content. A dynamic title and the subtitle. I we use paragraph ngFor let message
of messages. And print a single message. Okay, I have the title
and if I inspect the network, I can see the messages endpoint which is
requested to my backend. But it answers an empty list. Now I have the frontend
which communicates with the backend. But I have an empty response. I need
to create a form to submit messages. I will generate another component.
NG generate component content input. I will add this input in the app component content input. Here it is. I
will go to the materials website to pick a form. Let's pick the form fields, simple form fields. Okay.
I go to the HTML, copy all of this, and replace by a form. Copy the typescript imports. Now it's a standalone component. Import with all of
this. Select the input and the form field. There is also some CSS. Let's pick it. Okay, let's see the result. I
must update the modules, as now it's a standalone. Here it is. I will edit
the HTML to have only the needed fields. Add message. And that's all. I will link the form fields message
form, which is a NG form, and on the submit action, call the method onSubmit.
This is linked to the model. And now I need a button. Let's go to materials
website. Pick some buttons. This one seems great. Basic. Add the imports. Okay, I need to add some more imports. As now
I am using the form modules, I need the common module from angular. common. And I need the NG form also. The forms module angular forms. Now I need to create the onSubmit method. submit. Okay I have my content displayed. I
will start by creating the variable where to store the form values. ViewChild,
named messages. Form message. Form which is a NG form.
Let's import the ViewChild Okay, now on the submit even, I must call
the end point in the backend. So I need the HTTP client in my component.
The constructor, I inject the HTTP client. Add the import. Angular, common HTTP. And now add the method, the request. HTTP post to my backend, LocalHost 8080 messages,
with the content message form. All values. And I will add an option, with credentials true. I
need this option to tell the browser to send all the cookies with the request. This is necessary
because this is an AJAX request. And now subscribe to the result. Data which is of type content.
And the result, I will call an upper method, a method from the parent. So I have an event,
and then emit the data. Let's create this event output. And event emitter. Let's add the content import. Oh it's not square brackets but curly brackets. And I will just add any. Okay, but now
I have to bind this event. Let's go to the root component. When the input is submitted I call
this method, on message created, with the event data. But I need this method to
be inside the content display, because the messages variable is inside
the content display. So let's move it inside this content display HTML. And I will
put it on the top. And now let's create this method with an input as message of type string, and returns void. When received, I
push the message into the messages list. And now add the content input in the
imports. Okay, it works. Let's try this now. Okay, I post the messages with the form, and
I read the messages with the display component. Okay. I have my frontend which can now request
my backend. And my backend returns data. I will now add the Spring Security to implement the CSRF
workflow. Still, when I add the Spring Security, I need to implement an authentication system.
So I will implement a simple authentication system. I start by uncommenting the Spring
Security dependency. Go to the pom XML, and uncomment it. And reload the Maven project.
And now, let's create the authentication controller. controller. Go to the packages, into
the controllers package. I create a new class, auth controller. I start by adding the
rest controller annotation. And the required args annotation from Lombok.
My endpoint will be a post mapping, to sign in. It returns a response entity of a user
Dto. And receives a request body of type sign in Dto. Let's create those objects, as records,
into the dto package. The user will have an ID, the login and a name. And the signInDto
will be a record, with the login and the password. Now, I will call a service, an authentication service, to sign in
with the dto. It will return a user dto. I inject it. I will create
it later. With authenticated user, I will inject it in the Security Context. I
will start by getting the Security Context, Security Context Holder, get context. In
the context, I set the authentication. A new user password authentication token.
With the user, no password and no roles. This context, I must inject it into a
Security Repository, Security Context Repository. Repository, save
context. And now, I save it. It needs the request and the response.
Let's add those input items. HTTP servlet request. HTTP servlet Response. And
finally return the response entity. Okay with the user. And I initialize
the repository as an HTTP session security context repository. Let's go
to the service now. Into the services package. I add the service authentication.
And as before, the required args annotation. The method returned a user dto, sign in, and
accept a sign in dto. What does this method? It will check the incoming password with an
accepted password. For that I need a password encoder. I get the incoming password
from the char array and compare it with a hashed password. I will
hardcode this hashed password for simplicity. If it's correct I will
return a user dto that I will create by myself. Otherwise, I throw an exception, a runtime exception. This hashed password will be a constant. With what value? With this hash.
This hash corresponds to the word password. I should create an aspect to manage all the
exceptions. But I've already done that in another videos. Check the link in the corner
if you want to know more about how to handle the exceptions in a Spring Boot application.
And now let's configure the Spring Security. Let's start now with the password encoder. I go
to the config package. And create a new class, password config. Will be a component
and inject this bean, password encoder. I will use a bcrypt
password encoder. And now, the Spring Security filter chain.
I create a new class, security config. Add the configuration annotation. And the enable web security. Declare the
bean. Returns a security filter chain. Let's start with the CSRF. For now I disabled the CSRF.
Let's go step by step. The session management. I use the session creation
policy always, because to avoid complexity, I will use a stateful application.
With a cookie for the session. This way all the authentication is
managed by Spring. And now the routes. I accept a post on the endpoint sign in, permit all. I also allow the options method
to all the end points. And the rest of the requests must be authenticated. Return the
HTTP build. And catch the exceptions. Okay, my backend is now protected with a simple
authentication system. With a hardcoded password, but it's enough for today. If you want to see a
more advanced authentication system check the link in the corner. Let's go now to the frontend
to create my login form. Let's create a new component. NG generate component
login form. Now I will go again to the materials website to pick a login form. Form Fields examples. This simple form. Copy all the code. Let's go to the new component. All will be in inside a form. Also copy the imports. I paste them in the typescript file. Imports and standalone. I also need to import forms module, from angular forms. Let's start displaying this form. Let's go
to the root component. On the top, I add the login form. Okay, here it is my login form on the top.
And the messages below. But I want to display the login form only if the user is not authenticated.
So I will create a variable in the root component, authenticated, which starts
to be false. And in the HTML, I will display the login form if authenticated is false. And display the content
if authenticated is true. Okay, it's better now. Let's come back to the
login form and edit this. Login. Remove the rest. And another field as password.
The input goes to a login variable, from a NG model. And this input goes
to a variable named password to the NG model. Let's declare the model login form
which is an NG form. And I will put some style. And the fields, login full width. Let's add some style in the CSS
file. The login form and on the login full width. min width, max width. And center in the middle. Now take the full width for each field. Let's come back to
the HTML and add the button, the submit button. I will
take the same as the content input login. And copy the imports. Here it is. Let's go to the form again, and
bind the submit action to a method onsubmit. I call a method named on submit, no
it's NG submit. Let's create this method. Returns void. And will call the backend to authenticate the user. So I need
the HTTP client. I inject it in the constructor. I add the import. Call the post method, on HTTP LocalHost
8800 sign in, with the values of the login form. Let's create this variable. It will be a
view child login form, NG form. Add the import. Add the view child import. And subscribe to the
response. This will call an event emitter on login event. And emit
this will. Just change the value of the authenticated variable on the root
component. Let's create this event emitter. Add the imports. And now bind this output. I must go to the root component. Bind this output with
a method called on signed in, on Signed in, because I'm already signed, I'm
already authenticated. And now create this method. It's just changes the value of authenticated. Let's test
the login. I put any login and the password must be the hardcoded value. password. Okay it changed the flag authentication
from false to true. And now it displays the message the content display. Now I have my
frontend ready with the login. Next step, add the CSRF protection. But the CSRF protection
in the frontend requires that each request contains an additional parameter. So I need
to modify the way each request is sent. Let's create my own HTTP client. All what I will do is
just wrap the existing HTTP client into my own HTTP client. This way I can modify the request
before sending it. NG generate a service HTTP client. I will start by injecting
the HTTP client, the real HTTP client. Add the import from angular
common HTTP. And now create two methods, the get with a URL as a string, which
returns for Simplicity I will use any. And a post method with
a URL and data. It returns any. Let's start implementing
the get. Returns this HTTP get, with the URL of my backend, LocalHost 8080.
Now I concat the URL and add the options, with credentials. That's it. true. Let's
go to the Post. Return this HTTP post, with the URL of the backend, I concat the URL I want, add the data and the
options with credentials. I return the promise, this way I subscribe to the
response when calling those methods. And now I inject this service into the components. Let's start by the content display. from HTTP client service, in content input. And in the login. And now use this in the constructor. Let's go to the input. I replace in the
constructor. And in the login form, I replace in the constructor. Now I have
to adapt the methods. Let's start by the display get messages. And subscribe. In
the input, post messages. I don't need the options and the data. And in the login
form, post sign and the data. And as said, I subscribe to the response. Now I have
a fullstack application protected with authentication. It's a stateful application.
What I will do now is protect my backend with the CSRF protection. I will start by enabling
the CSRF protection in the Spring Security. config customizer with defaults.
But now I have to add a new matcher. Get on the CSRF endpoint.
I will create now the CSRF endpoint, but it must be public. The options request are
the ones used with the CORS configuration. They are sent automatically by the browser. And I need
them to be unprotected because I don't want to apply the CSRF protection on those requests. As
those requests are made by the browser, I can't wrap them to add an additional parameter. Let's
create now another controller to create the CSRF token. CSRF controller. It's a rest controller
and just one endpoint, a get on csrf token. public csrf token. csrf token. Those values will automatically be
injected by Spring because I've enabled the CSRF protection. That's all on the
backend side. Now on the frontend, I need to request the CSRF token at
the beginning. Then include the CSRF on each request. A solution can be to
request the CSRF token per session, per user session. But a stronger solution
is to request the CSRF token per request. This solution requires more implementation, so I
choose the easy one. Request the CSRF token at the beginning means when loading the application.
Let's go to the root component to request the token. I start adding the http client client service. And in the NG on init method, the one which is called when loading
the root component. I call get csrf. Let's go to the service to create this
method. It returns void. This HTTP, get on HTTP LocalHost 80 csrf
token. And subscribe to the response, data any. And the data. I
will store it in a variable named CSRF token. Let's create it. String,
initialized empty. And now, in the post method I must add the CSRF token.
I will add it in the headers. The CSRF token only applies to requests which change
the server state, like a post, a put, a patch or delete. Requests like head
or get are not impacted by the CSRF protection. Let's add a header. New HTTP headers, with the name X CSRF token. This is the default header name for the CSRF token. And
take the variable. Let's import the header. And try all together. Here
is my frontend. When loading the application I have the token
request, which receives the token, the CSRF token, with its name as I put
in the Angular application. Let's now login. I have two signing requests, the
pre-flight and the post. The post contains the body have sent. It also contains the
CSRF token in the header. Next to that, I have the messages request, with an empty
response as I added no message. And as said, the get request doesn't need
the CSRF token. If I add a new message, I take the post request, and I see
the CSRF token again. I've created a backend project with Spring Security. I've created
two endpoints, one to read messages and one to create messages. Then I've created the
frontend project. I've created two components, to display the messages and a form to create
messages. Then I've added the Security to my backend. I've created a login endpoint where
I add my user to the Security Context. I've configured my application with Sprint Security as
a stateful application. And I've indicated which endpoints are public. In the frontend, I've added
another component with the login form. Finally, I've added the CSRF token configuration in the
backend with a new endpoint. In the frontend, I call this endpoint when loading the root
component. And in the HTTP client wrapper, I add the CSRF token to the post request as
a header. That's all for this video. I hope you liked it. Please click on the like button,
subscribe to my channel, and see you soon. Bye!