STACKJAVA

CRSF là gì? Phòng chống CRSF với Spring Security

CRSF là gì? Phòng chống CRSF với Spring Security

CRSF là gì?

CSRF hay còn gọi là là “session riding“, “XSRF“, “one-click attack

CSRF ( Cross Site Request Forgery) là kỹ thuật tấn công bằng cách sử dụng quyền chứng thực của người dùng đối với một website. Hiểu một cách nôm na, đây là kỹ thuật tấn công dựa vào mượn quyền trái phép.

Ví dụ, trên trang stackjava.com có một form submit đến máy chủ của trang vietcombank.com.vn. (có thể là click vào một cái ảnh rồi gửi lệnh chuyển tiền tới tài khoản của hacker)

Cách phòng chống:

Phòng chống CRSF với Spring Security

Từ Spring Security 5, chức năng bảo vệ khỏi crsf mặc định được bật, hoặc bạn cũng có thể chỉ rõ bằng thẻ <csrf/> hoặc <csrf disabled="false"/> trong file config spring security. Để tắt crsf ta sử dụng <csrf disabled="true"/>

Cơ chế hoạt bảo vệ crsf trong spring security: mỗi khi nhận một request, spring security sẽ kiểm tra tham số “_csrf” có giá trị đúng với giá trị mà spring security đã khởi tạo cho request nhận được đầu tiên hay không, nếu không có tham số này hoặc giá trị tham số bị sai thì nó sẽ từ chối request.

Để thêm tham số “_csrf” vào request ta thêm tham số sau vào mỗi form:<input type=”hidden” name=”${_csrf.parameterName}” value=”${_csrf.token}” />

CRSF là gì? Phòng chống CRSF với Spring Security stackjava.com

Code ví dụ CRSF trong Spring Security

Các công nghệ sử dụng:

File Cấu hình Spring Security:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">

  <http auto-config="true">
    <intercept-url pattern="/admin**" access="hasRole('ROLE_ADMIN')" />
    <form-login
        login-page="/login"
        login-processing-url="/j_spring_security_login"
        default-target-url="/admin"
      authentication-failure-url="/login?error"
      username-parameter="username"
      password-parameter="password" />
    <logout logout-url="/j_spring_security_logout"
      logout-success-url="/logout" delete-cookies="JSESSIONID" />
    
    <csrf/>			
  </http>

  <authentication-manager>
    <authentication-provider>
      <user-service>
        <user name="admin" password="{noop}123456" authorities="ROLE_ADMIN" />
      </user-service>
    </authentication-provider>
  </authentication-manager>
</beans:beans>

File Controller:

package stackjava.com.springsecurityformlogin.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class BaseController {

  @RequestMapping(value = { "/login", "/" })
  public String login(@RequestParam(value = "error", required = false) final String error, final Model model) {
    if (error != null) {
      model.addAttribute("message", "Login Failed!");
    }
    return "login";
  }

  @RequestMapping("/admin")
  public String admin() {
    return "admin";
  }

  @RequestMapping("/logout")
  public String logout(final Model model) {
    model.addAttribute("message", "Logged out!");
    return "login";
  }

  @RequestMapping("/demo1")
  public String demo1() {
    return "demo1";
  }

  @RequestMapping("/demo2")
  public String demo2() {
    return "demo2";
  }

}

File view:

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>login</title>
</head>
<body>
  <h1>Spring MVC-Security Login Form</h1>
  <h2>${message}</h2>

  <form name='loginForm' action="<c:url value='j_spring_security_login' />" method='POST'>
    <table>
      <tr>
        <td>User:</td>
        <td><input type='text' name='username'></td>
      </tr>
      <tr>
        <td>Password:</td>
        <td><input type='password' name='password' /></td>
      </tr>
      <tr>
        <td colspan='2'><input name="submit" type="submit" value="login" /></td>
      </tr>
    </table>
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />

  </form>
</body>
</html>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Admin Page</title>
</head>
<body>
  <h1>Admin Page</h1>
  <h2>Welcome: ${pageContext.request.userPrincipal.name}</h2>

  <form action="<c:url value="/j_spring_security_logout" />" method="post">
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
    <input type="submit" value="Logout" />
  </form>

  <form action="<c:url value="/demo1" />" method="post">
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
    <input type="submit" value="Goto Demo Page1" />
  </form>

  <form action="<c:url value="/demo2" />" method="post">
    <input type="submit" value="Goto Demo Page2" />
  </form>

</body>
</html>
<html>
<head>
<title>Demo Page1</title>
</head>
<body>
  <h1>Demo Page1</h1>
</body>
</html>
<html>
<head>
<title>Demo Page 2</title>
</head>
<body>
  <h1>Demo Page 2</h1>
</body>
</html>

Demo:

Đăng nhập với tài khoản admin/123456

Form logout và demo1 có chứa trường <input type=”hidden” name=”${_csrf.parameterName}” value=”${_csrf.token}” /> nên khi generate ra html nó sẽ có trường”_crsf

Truy cập trang demo1.jsp (submit form demo1)

Truy cập trang demo2.jsp (submit form demo2) bị lỗi vì nó không có trường “_crsf

Để truy cập trang demo2.jsp bạn phải thêm <input type=”hidden” name=”${_csrf.parameterName}” value=”${_csrf.token}” /> vào form demo2 hoặc tắt crsf bằng thẻ <csrf disabled="true"/>

CRSF là gì? Phòng chống CRSF với Spring Security stackjava.com

Okay, Done!

Download code ví dụ trên tại đây

 

Referneces:

https://en.wikipedia.org/wiki/Cross-site_request_forgery

https://viblo.asia/p/ky-thuat-tan-cong-csrf-va-cach-phong-chong-amoG84bOGz8P