@PathVariable을 활용해서 URI를 구성하는데 String 타입에 대해서 보안 취약점이 리포팅 되었다.
그냥 BlackList에 포함되면 뱉으면 될 것 같은데 어찌저찌하다 보니 특정 문자들은 치환하는 것으로 결정됐다.
AOP를 활용해서 구성해보았다.
java Config Registration
@Slf4j
@Aspect
@Component
public class ValidationBlackListedAspect {
private static final String BLACKLIST_REGEX = "[<>]|<|>";
@Pointcut("execution(* *(.., @org.springframework.web.bind.annotation.PathVariable (String), ..))")
public void pathVariablePointCut() {}
@Around("pathVariablePointCut()")
public Object validationPathVariable(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
if (arg instanceof String) {
String pathVariable = (String) arg;
String replacePathVariable = pathVariable.replaceAll(BLACKLIST_REGEX, "");
args[i] = replacePathVariable;
if (pathVariable.equals(replacePathVariable)) continue;
log.debug("pathVariable : {} => replacePathVariable : {} ", pathVariable, replacePathVariable);
}
}
return joinPoint.proceed(args);
}
}
xml Config Registration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-#{version}.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-{version}.xsd">
<!-- Bean Registration -->
<bean id="validationPathVariable" class="stour.common.aop.ValidationBlackListedAspect"/>
<!-- AOP Configuration -->
<aop:config>
<aop:pointcut expression="execution(* *(.., @org.springframework.web.bind.annotation.PathVariable (String), ..))" id="pathVariable"/>
<aop:aspect ref="verifyBlackListedAspect">
<aop:around method="validationPathVariable" pointcut-ref="pathVariable"/>
</aop:aspect>
</aop:config>
</beans>
포인트컷은 어떤 메서드가 AOP Advice를 적용할 위치를 결정하는 데 사용된다.
위의 코드에서 pathVariablePointCut() 메서드는 @Pointcut 애노테이션이 적용되어 있다. 해당 포인트컷은 execution 표현식을 사용하여 메서드 실행 지점을 설정할 수 있다.
execution 표현식은 메서드의 시그니처를 기반으로 메서드 실행 지점을 정의하는 데 사용되는데, 다음은 execution 표현식의 구성 요소이다.
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
modifiers-pattern: 메서드의 접근 제어자 지정 (public, private, protected 등)
ret-type-pattern: 메서드의 반환 타입 지정 (void, String, int 등)
declaring-type-pattern: 메서드를 선언한 클래스 타입 지정 (me.choicore.domain.controller.*)
name-pattern: 메서드명 지정
param-pattern: 메서드의 매개변수 패턴 지정 (..은 0개 이상)의 매개변수를 나타내며,
throws-pattern: 메서드가 던질 수 있는 예외 타입을 지정한다.
따라서, pathVariablePointCut() 메서드의 @Pointcut 어노테이션에서 사용된 execution 표현식은 다음을 의미한다.
execution: 메서드 실행 지점을 정의
*: 반환 타입을 모든 타입으로 지정
*(.., @org.springframework.web.bind.annotation.PathVariable (String), ..): 메서드의 매개변수 패턴 지정 (..은 0개 이상의 매개변수)
@org.springframework.web.bind.annotation.PathVariable (String)은 @PathVariable 어노테이션이 적용된 String 타입의 매개변수를 의미하기 때문에
즉, 해당 포인트컷은 모든 반환 타입의 메서드 중에서 @PathVariable 어노테이션이 적용된 String 타입의 매개변수를 가지는 메서드를 선택합니다.
enum으로 해결할 수 있는 부분이지만 이미 상당 부분 String으로 구현한 구역들이 많아 AOP를 통해 SQLInjection 등 보안 취약점을 보완해보았다.
사실 Dispatcher Servlet 계층까지 끌고 들어오는건 조금 이상하다.
public class BlackListedFilter implements Filter {
private static final String BLACKLIST_REGEX = "[<>]|<|>|%3E|%3C";
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String requestURI = request.getRequestURI();
String newURI = requestURI.replaceAll(BLACKLIST_REGEX, "");
request.getRequestDispatcher(newURI).forward(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
Filter 계층에서 검증하기로 하자
그럼 20000