Spring Project를 진행하며 회원가입 시 프로필 사진 추가를 위해 첨부파일을 추가하였다.
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<form:form name="empFrm"
action="${pageContext.request.contextPath}/emp/empEnroll.do"
method="post"
enctype="multipart/form-data">
<table id="emp-enroll-table"></table>
</form:form>
하지만 오류가 발생하였다.
request method 'post' not supported
원인은 CSRF Token
이 없어서였다.
CSRF (Cross-Site Request Forgery) Token
은 웹 보안에 사용되는 기술로, 웹 어플리케이션에서 발생하는 CSRF 공격을 방지하는 데 사용된다.
CSRF 공격
은 웹 어플리케이션 사용자가 악의적인 웹사이트에 방문할 때 발생할 수 있다. 공격자는 악의적인 웹사이트를 통해 사용자의 브라우저에서 해당 웹 어플리케이션에 요청을 보내고, 사용자는 이를 모르고 실행합니다. 이렇게 함으로써 공격자는 사용자의 계정으로부터 해당 웹 어플리케이션에 대한 액세스를 획득하거나 데이터를 조작할 수 있다.
CSRF Token
은 이러한 공격을 방지하기 위해 사용자의 세션과 연관된 무작위의 문자열이다. 웹 어플리케이션은 사용자의 모든 요청에CSRF Token
을 포함시키고, 이 Token이 제대로된지 확인한다. 공격자는 CSRF Token을 알지 못하므로, 이 Token이 유효하지 않으면 해당 요청은 거부된다.
따라서, CSRF Token
은 웹 어플리케이션에서 발생할 수 있는 CSRF 공격
으로부터 보호하는 데 중요한 역할을 한다.
Spring Security는 모든 양식에 CSRF Token을 자동으로 추가함으로써 CSRF 공격으로부터 애플리케이션을 보호하는 쉽고 효과적인 방법을 제공한다. 애플리케이션에서 Spring Security를 구성하면 기본적으로 CSRF 필터가 필터 체인에 추가된다. 이 필터는 고유한 CSRF Token을 생성하고 애플리케이션의 모든 양식에 숨겨진 입력 필드로 추가한다. 양식이 제출되면 CSRF Token이 요청 본문에 포함되고 서버에서 확인하여 요청이 악의적인 소스가 아니라 동일한 애플리케이션에서 시작되었는지를 확인할 수 있다. 그러면 왜 multipart/form-data와 Ajax 요청 시 CSRF Token을 따로 추가해야 할까?
multipart/form-data와 Ajax 요청은 구조화 및 데이터 전송 방식에 표준 양식 요청과 다르다.
표준 양식 요청에서 양식 데이터는 일반적으로 요청 본문으로 전송된다. CSRF 토큰은 양식이 제출될 때 요청 본문에 자동으로 숨겨진 입력 필드에 포함된다.
multipart/form-data 요청에서 양식 데이터는 파일 데이터 및 기타 양식 필드를 포함하여 별도의 부분으로, Ajax 요청에서 양식 데이터는 일반적으로 요청 본문의 JSON 데이터로 전송된다. CSRF 토큰은 요청 본문에 자동으로 포함되지 않으므로 요청 헤더에 별도로 추가하거나 양식 데이터(양식 필드로 별도로 추가(multipart/form-data) / JSON 데이터에 포함(Ajax))에 별도로 추가해야 한다.
요약하면 모든 유형의 요청에는 CSRF 공격으로부터 보호하기 위한 CSRF Token이 포함되어야 하는데 포함되는 방식은 요청 유형에 따라 다를 수 있다. Spring Security는 표준 양식 요청에 대해 이를 자동으로 처리하는 매커니즘을 제공하지만, Ajax 및 multipart/form-data 요청의 경우 CSRF 토큰을 요청 헤더 또는 양식 데이터에 별도로 추가해야 한다.
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<form:form name="empFrm"
action="${pageContext.request.contextPath}/emp/empEnroll.do?${_csrf.parameterName}=${_csrf.token}"
method="post"
enctype="multipart/form-data">
<table id="emp-enroll-table"></table>
</form:form>
url
뒤에 ?${_csrf.parameterName}=${_csrf.token}
을 추가해주면 된다.
const csrfHeader = "${_csrf.headerName}";
const csrfToken = "${_csrf.token}";
const headers = {};
headers[csrfHeader] = csrfToken;
$.ajax({
url : '${pageContext.request.contextPath}/workingManagement/insertStartWork.do',
method : 'POST',
headers,
contentType : "application/json; charset=utf-8",
success(data){
console.log(data);
},
error : console.log
});
headers
를 ajax요청
에 같이 보낸다.