๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๊ฐœ๋ฐœ/Web

[Spring Framework] CSRF ์ ์šฉ

by ynzu๐Ÿค 2021. 12. 14.
๋ฐ˜์‘ํ˜•

์ง€๋‚œ ๋ฒˆ์— spring boot์— csrf๋ฅผ ์ ์šฉํ•˜๋Š” ํฌ์ŠคํŒ…์„ ์˜ฌ๋ ธ์—ˆ๋Š”๋ฐ  ๊ทธ๋ƒฅ spring๊ณผ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๋‹ฌ๋ผ ๋˜ ํฌ์ŠคํŒ…์„ ์˜ฌ๋ ค๋ณธ๋‹ค!

 

[Spring Boot] spring security - CSRF ์ ์šฉ , +) ajax csrf ์ ์šฉ

1. gradle ํ˜น์€ maven์— 'spring-boot-starter-security' ์ถ”๊ฐ€ implementation 'org.springframework.boot:spring-boot-starter-security' org.springframework.boot spring-boot-starter-security 2.5.5 Spring S..

ynzu-dev.tistory.com

 

  • web.xml์— dispactcher servlet์„ ๋“ฑ๋กํ•œ๋‹ค.
<servlet> 
	<servlet-name>appServlet</servlet-name> 
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
	<init-param> 
		<param-name>contextConfigLocation</param-name> 
		<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value> 
	</init-param> 
	<load-on-startup>1</load-on-startup> 
</servlet> 
<servlet-mapping> 
	<servlet-name>appServlet</servlet-name> 
	<url-pattern>*.do</url-pattern> 
</servlet-mapping>

 

  • servlet-context.xml์— csrf hidden์ด form์— ์ž๋™์œผ๋กœ ์ถ”๊ฐ€ํ•ด์ฃผ๋Š” class๋ฅผ ๋นˆ(bean)์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค
<beans:bean id="requestDataValueProcessor" class="com.xxxx.framework.processor.CSRFRequestDataValueProcessor"/>

 

  • CSRFRequestDataValueProcessor.java

- csrf hidden ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋™ ์ถ”๊ฐ€ํ•ด์ฃผ๋Š” ํด๋ž˜์Šค, RequestDataValueProcessor๋ฅผ implementsํ•ด์•ผ ํ•œ๋‹ค.

public class CSRFRequestDataValueProcessor implements RequestDataValueProcessor{ 
	@Override 
	public Map<String, String> getExtraHiddenFields(HttpServletRequest request) { 
		Map<String, String> hiddenFields =  new HashMap<String, String>(); 
		HttpSession session = request.getSession(false); 
		if(session == null) { 
			return hiddenFields; 
		}		 
		String token = UUID.randomUUID().toString().replaceAll("-", ""); 		 
		session.setAttribute(FrameworkConstants.CSRF_TOKEN_NAME, token); 
		hiddenFields.put(FrameworkConstants.CSRF_PARAM_NAME, token); 
		return hiddenFields; 
	} 
	@Override 
	public String processAction(HttpServletRequest arg0, String arg1) {		 
		return null; 
	} 
	@Override 
	public String processFormFieldValue(HttpServletRequest arg0, String arg1, String arg2, String arg3) {		 
		return null; 
	} 
	@Override 
	public String processUrl(HttpServletRequest arg0, String arg1) {		 
		return null; 
	} 
}

 

  • servlet-context.xml ์— CSRF ์ฒดํฌ interceptor ๋“ฑ๋ก
<mvc:interceptors> 
	<mvc:interceptor> 
		<mvc:mapping path="/**/*.do"/> 
		<beans:bean  class="com.xxx.framework.interceptor.CSRFInterceptor"> 
			<beans:property name="excludeURLs"> 
				<beans:array value-type="java.lang.String"> 
					<beans:value>login.do</beans:value> 
					<beans:value>logout.do</beans:value> 
					<beans:value>view.do</beans:value> 
				</beans:array>		  			 
			</beans:property> 
		</beans:bean > 
	</mvc:interceptor> 
</mvc:interceptors>

- login.do, logout.do, view.do๋Š” csrf๋ฅผ ์ฒดํฌํ•˜์ง€ ์•Š์Œ!

- csrf๋ฅผ ์ฒดํฌํ•˜์ง€ ์•Š์„ url ํ˜น์€ ํŒจํ„ด์„ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  csrf ์ธํ„ฐ์…‰ํ„ฐ์—์„œ ํ•ด๋‹น url์€ ์ฒดํฌํ•˜์ง€ ์•Š๋„๋ก ๋กœ์ง์„ ์งœ๋ฉด ๋จ!

 

  • CSRFInterceptor.java
public class CSRFInterceptor extends HandlerInterceptorAdapter{ 
	Logger log = Logger.getLogger(this.getClass().getName()); 
	private String excludeURL ; 
	private String[] excludeURLs; 
	@Override 
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 
		boolean result = true; 
		if(request.getMethod() == null) { 
			return result; 
		} 
		if(!request.getMethod().equalsIgnoreCase("post")) { 
			return result; 
		}else { 
			if(!isExcludeURL(request.getRequestURI())) { 
				if(!this.verifyCSRFToken(request)) { 
					this.log.error("Incorrect CSRF value"); 
					response.sendError(HttpServletResponse.SC_FORBIDDEN, "Bad Request"); 
					result = false; 
				}						 
			}					 
		} 
		return result; 
	} 
	private boolean verifyCSRFToken(HttpServletRequest request) { 
		boolean result = true; 
		if(request.getSession().getAttribute(FrameworkConstants.CSRF_TOKEN_NAME) != null) { 
			String sessionToken = (String)request.getSession().getAttribute(FrameworkConstants.CSRF_TOKEN_NAME); 
			String paramToken = request.getParameter(FrameworkConstants.CSRF_PARAM_NAME);			 
			if(paramToken == null || !sessionToken.equals(paramToken)) { 
				this.log.debug("Incorrect CSRF value"); 
				result = false; 
			} 
		}		 
		return result; 
	} 
	@Override 
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 
		super.postHandle(request, response, handler, modelAndView); 
	} 
	@Override 
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 
			throws Exception { 
		super.afterCompletion(request, response, handler, ex); 
	} 
	private boolean isExcludeURL(String url) { 
		boolean result = false; 
		if(url == null) { 
			return true; 
		} 
		url = url.toLowerCase(); 
		for (int i = 0; i < this.excludeURLs.length; i++) {				 
			if (url.indexOf(  this.excludeURLs[i].toLowerCase() ) < 0) { 
				continue; 
			} 
			result = true; 
			break; 
		} 
		return result; 
	} 
	public String getExcludeURL() { 
		return excludeURL; 
	} 
	public void setExcludeURL(String excludeURL) { 
		this.excludeURL = excludeURL; 
	} 
	public String[] getExcludeURLs() { 
		return excludeURLs.clone(); 
	} 
	public void setExcludeURLs(String[] excludeURLs) { 
		if (excludeURLs == null) { 
			this.excludeURLs = new String[0]; 
		} else { 
			this.excludeURLs = Arrays.copyOf(excludeURLs, excludeURLs.length); 
		} 
	}	 
}

 

ํ™•์‹คํžˆ spring boot๊ฐ€ ํ›จ์”ฌ ๊ฐ„๋‹จํ•˜๋‹ค.. ๋„ˆ๋ฌด ํŽธํ•ด!

728x90
๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€