본문 바로가기
프로그래밍/스프링프레임워크

전자정부표준프레임워크 3.6 Spring Security 설정 간소화에서 CSRF 설정하기

by pentode 2018. 4. 19.

전자정부표준프레임워크 3.6 에서는 Spring Security가 권한 정보를 데이터베이스에서 가져오도록 커스터마이징 되어 있는데, 이로 인해 설정이 상당히 복잡해 집니다. 그래서 자체적인 설정을 만들어서 간소화 하고 있습니다.


여기에 CSRF(Cross-Site Request Forgery) 방어를 위해서 설정을 하려고 합니다. 그런데 간소화 설정에는 CSRF 관련 설정이 없는것 같습니다.


다음 예는 전자정부표준프레임워크 3.6.0 버전의 Enterprise Business 템플릿으로 테스트 하였습니다. 여기에는 Spring Security 3.2.4 버전이 사용되고 있습니다. 스프링 시큐리티를 xml 설정파일을 사용해서 설정할때는 CSRF가 기본적으로 비활성화 되어 있습니다. 보통 다음과 같이 활성화 합니다.


<http>
    ...
    <csrf />
    ...
</http>


Spring Security 설정 간소화가 되어 있는 상태에서 이처럼 <csrf />를 사용하면 다음과 같은 에러가 발생합니다.


Caused by: java.lang.IllegalArgumentException:
A universal match pattern ('/**') is defined  before other patterns in the filter chain


전자정부표준프레임워크의 egovframework.rte.fdl.security-3.6.0.jar 파일이 로딩될때 jar 파일내의 /META-INF/spring/security/security-config.xml 파일이 로딩되어 Spring Security 기본 설정을 하게 되는데, 이것과 순서상에 문제가 있는것 같습니다.


이 설정파일을 로딩하는 클래스는 egovframework.rte.fdl.security.config.EgovSecurityConfigInitializerBeanDefinitionParser로 다음과 같이 기본 설정 파일을 로딩하고 있습니다.


@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {

    LOGGER.debug("Load '/META-INF/spring/security/security-config.xml'");

    parserContext.getReaderContext().getReader().loadBeanDefinitions("classpath*:/META-INF/spring/security/security-config.xml");
}


설정 간소화에서는 초기화하는 클래스를 사용자 정의하거나 하는 방법을 제공하고 있지 않은것 같습니다. 그래서 편법이지만 WAS의 클래스로더가 class를 로딩할 /WEB-INF/classes 내의 파일을 /WEB-INF/lib 보다 먼저 로딩하고, 동일한 클래스는 순환 로딩이 되지 않게 한번만 로딩 된다는 것을 이용하여 설정을 변경할 수 있습니다. 모든 WAS 의 클래스로더가 동일한 순서인지는 알 수 없지만 Tomcat과 Jeus에서는 이 순서로 로딩되는것을 확인했습니다.



다음 순서로 작업을 합니다.


1. src/main/javaegovframework.rte.fdl.security.config 패키지를 만들고 EgovSecurityConfigInitializerBeanDefinitionParser 클래스 생성합니다. 클래스 내용을 원본과 동일하게 만든 다음에 security-config.xml 파일의 위치를 /egovframework/spring/com/ 아래로 수정합니다.


@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {

    LOGGER.debug("Load '/egovframework/spring/com/security-config.xml'");

    parserContext.getReaderContext().getReader().loadBeanDefinitions("classpath*:/egovframework/spring/com/security-config.xml");
    ...
}


2. jar 파일내의 secruity-config.xml 파일을 /egovframework/spring/com 폴더에 복사해서 <csrf /> 설정을 추가합니다. 스프링 프레임웍 시동시의 설정파일과 같은 폴더지만 시동시에는 context-*.xml 파일을 사용하도록 설정되어 있으므로 중복 로딩은 발생하지 않습니다.


<http entry-point-ref="loginUrlAuthenticationEntryPoint" request-matcher-ref="requestMatcherTypeFactoryBean">
    <form-login />
    <logout />
    <anonymous />
    <csrf />
    ...
</http>




WAS를 재시작하면 CSRF가 활성화 되어 있는것을 확인할 수 있습니다. 스프링 폼태그(<form:form>)를 사용하는 폼에는 CSRF 체크를 위한 토큰이 다음처럼 자동으로 삽입되어 있을 것입니다.


<input type="hidden" name="_csrf" value="9deb4a24-2331-4f14-9333-6b8f8f146b37" />


스프링 폼태그를 사용하지 않는 다면 다음과 같이 수동으로 토큰을 삽입해야 합니다.


<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />


CSRF가 활성화 되면 로그아웃시에 로그아웃 URL을 POST 방식으로 호출 해야 합니다. 


※참고사항

jar 파일의 소스와 security-config.xml 파일의 내용은 프로젝트 트리에서 Maven Dependencies 아래 jar 파일을 클릭해서 트리를 확장한 다음 class 파일과 xml 파일을 클릭하면 소스를 확인할 수 있습니다. 필요한 부분을 복사해서 사용하면 되겠습니다.




반응형