개발환경
- 전자정부프레임워크 3.10
- 오라클 DB
사용 계기
- 현재 프로젝트에서는 스프링 시큐리티 및 CSRF 토큰을 통해 보안처리를 하고있음
- 타 홈페이지(A)에서 로그인이 되어있다면 JWT 토큰을 POST 방식으로 넘겨 내가 작업한 페이지(B)에 도착하고,
B에서도 JWT 토큰 인증을 통해 A에서 로그인 한 정보 그대로 B에서 이용할 수 있게 하는 즉 SSO(Single Sign-On : 1회 사용자 인증으로 다수의 애플리케이션 및 웹사이트에 대한 사용자 로그인을 허용하는 인증 솔루션) 기능을 만들고자 함 - 허나 타 홈페이지(A)에서 POST로 요청하는 경우 CSRF 토큰 인증이 되어야만 내 홈페이지(B)를 볼 수 있음(GET 방식은 CSRF 토큰 인증을 안하기에 무조건 통과가 됨 -> 허나 이번에는 POST로 하기를 요청받음)
- 기존에 CSRF를 프론트에서 발급하고 있었는데, 해당 방식이 통하지 않아 백단에서 CSRF 토큰을 발급하기로 함
- A에서 B로 POST를 타고 오는 과정에 403 에러가 계속 떴고, 찾다보니 CSRF 토큰 인증이 되지않아 권한이 없다고 뜨는 것임을 알아냈음
적용 과정
1. JWT 토큰 정보를 넘겨주는 타 홈페이지(A)
- action에는 내 프로젝트의 화면 주소, 여기서는 간소화해서 작성함(원래는 풀 URL이 들어가야 함)
- JWT 토큰 정보만을 가지고 /createCsrf.do(SSO 로그인 처리를 하기 전 CSRF 토큰을 만들어주는 FUNCTION)으로 이동함
<form id="sendForm" name="sendForm" method="POST" action="/createCsrf.do">
<input type="hidden" id="JWT_TOKEN" name="JWT_TOKEN" value="******" /><label for="JWT_TOKEN"></label>
<input type="submit">
</form>
+
- 참고로 action 내의 페이지는 스프링시큐리티에서 보안 인증을 거치지 않겠다는 예외처리가 되어있어야 함
- 나의 경우는 context-security.xml에 아래 부분을 추가하였음
<sec:http pattern="/createCsrf*" security="none" disable-url-rewriting="true"/>
2. JWT 토큰 정보를 받아 저장하고, CSRF 토큰을 발급하는 내 페이지(B)
/* 자바단 */
@RequestMapping(value = "createCsrf.do", method = RequestMethod.POST )
public String ssoCertified(HttpSessionCsrfTokenRepository repository, HttpServletRequest request, HttpServletResponse response)
throws ClassCastException {
// 토큰 생성
CsrfToken csrfToken = repository.generateToken(request);
repository.saveToken(csrfToken, request, response);
request.setAttribute("_csrf", csrfToken);
// JWT 토큰 저장
request.setAttribute("JWT_TOKEN", request.getParameter("JWT_TOKEN"))
return "/comm/createCsrf";
}
/* 화면단 - createCsrf.jsp */
// ssoProc으로 CSRF 토큰 정보 및 JWT 토큰 정보를 넘김
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri = "http://java.sun.com/jsp/jstl/functions" prefix = "fn" %>
<html>
<script type="text/javascript" src="<c:url value='/common/js/jquery.js'/>"></script>
<Script>
$(function() {
$("#move").submit();
});
</Script>
<body>
<form id="move" name="move" method="POST" action="<c:url value='/ssoProc.do'/>">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<input type="hidden" name="JWT_TOKEN" value="<c:out value="${JWT_TOKEN}" />" />
</form>
</body>
</html>
3. ssoProc에서 CSRF 토큰 및 JWT 토큰 정보를 가지고 인증 절차를 거친 뒤, 해당 사용자가 볼 수 있는 페이지(즉 로그인 한 사람이 볼 수 있는 페이지)로 redirect 시켜줌
추가 - CSRF와 토큰?
- CSRF(Cross Site Request Forgery) : 다른 사이트에서 유저가 보내는 요청을 조작하는 공격
- CSRF 토큰 : 서버에 들어온 요청이 실제 서버에서 허용한 요청이 맞는지 확인하기 위한 토큰
- 즉, form 태그를 통해 정보를 변경하거나 접속하고자 할 때 잘못된 공격을 막기 위해 token을 주어 그 사이트 고유의 토큰을 가지고 있는 경우에만 form 태그가 실행할 수 있게 함
- 로그인이나 로그아웃 혹은 작업화면 요청 시 CSRF 토큰을 생성하여 세션에 저장하고 요청 페이지에 CSRF 토큰을 셋팅하여 전송해야 함
-> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> 와 같이 토큰을 넘겨주어야 정상적으로 처리할 수 있음
추가 - 기존 프론트에서 자동으로 CSRF 토큰을 생성하는 법
- 화면단에 아래 부분을 추가하면 됨
<script>
$(function () {
$('form').append('<sec:csrfInput />');
let token = $("meta[name='_csrf']").prop("content");
let header = $("meta[name='_csrf_header']").prop("content");
$(document).ajaxSend(function(e,xhr,options){
xhr.setRequestHeader(header,token);
});
});
</script>
* 진행하며 느낀 점 *
- 처음엔 CSRF 토큰의 부재로 403 에러가 뜨는지 몰랐고 그냥 context-security.xml 파일에 예외처리를 제대로 하지않아 발생하는 문제인 줄 알고 예외처리를 하는 것에 많은 시간을 할애했다
- xml 파일 수정만 수십 수백번 했는데 대체 왜 안되는지, 예외처리를 하는 방법만 스택오버플로에 엄청 찾아본 것 같다
- 하지만 많은 문서에서 context-security.xml 파일 내에 csrf 설정 자체를 disabled="true"로 변경하라는 내용이 많았고, 아예 설정을 꺼버리면 모든 페이지에서 csrf 보안이 사라지는데 그 방법은 무언가 이상했다
- 그래서 왜 403 에러가 뜨는지 다시 검색하고 디버깅을 해보았을 때 csrf 토큰이 없어서 = 사용자 인증이 안되어서 403 에러가 뜨는 것이라 예측하여 백단에서 토큰을 생성해주자~~ 해서 해보았는데 정상적으로 작동했다
- 그저 오류만 뜨면 예외처리를 하는데만 급급했고, 정작 왜 오류가 나는지 제대로 살펴보지 않아 해결하는데 시간이 오래 걸린 것 같다
- 디버깅을 습관적으로 하고 하나 하나 타고들어가며 꼼꼼하게(!) 처리하는 것이 중요하다는 걸 다시한 번 느꼈다
* Reference *
https://velog.io/@jupiter-j/CSRF%ED%86%A0%ED%81%B0%EC%9D%B4%EB%9E%80
https://www.egovframe.go.kr/wiki/doku.php?id=egovframework:rte3:fdl:server_security:xmlschema_v3_8
https://sowon-dev.github.io/2022/05/02/220502springsecurity-CSRF/
'STUDY' 카테고리의 다른 글
JavaScript & jQuery | for문 내에서 시간차를 두고 함수를 실행하는 방법 + setTimeout과 클로저(Closures) (0) | 2023.08.21 |
---|---|
JAVA | Apache POI를 이용한 Excel 파일 읽기, 수정, 다운로드 (0) | 2023.02.17 |
ORACLE | 자주 쓰는 함수 정리(IN, NOT IN/SUBSTR/NVL, NVL2/TRUNC) (1) | 2022.09.08 |
JavaScript | replace()와 정규식 (0) | 2022.08.30 |
ORACLE | CASE WHEN 표현식과 DECODE (0) | 2022.08.23 |