Configure the CSRF Protection With Spring Security 6 and Angular

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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!
Info
Channel: The Dev World - by Sergio Lema
Views: 2,142
Rating: undefined out of 5
Keywords: java, spring boot, spring security, angular, csrf, csrf attack, csrf token
Id: tgjLsEmxcuY
Channel Id: undefined
Length: 51min 54sec (3114 seconds)
Published: Mon Oct 23 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.