[Spring] @PathVariable String Type Validation (With. AOP)

1

들어가며

@PathVariable을 활용해서 URI를 구성하는데 String 타입에 대해서 보안 취약점이 리포팅 되었다.

그냥 BlackList에 포함되면 뱉으면 될 것 같은데 어찌저찌하다 보니 특정 문자들은 치환하는 것으로 결정됐다.

AOP를 활용해서 구성해보았다.

java Config Registration

@Slf4j
@Aspect
@Component
public class ValidationBlackListedAspect {

    private static final String BLACKLIST_REGEX = "[<>]|&lt;|&gt;";

    @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 = "[<>]|&lt;|&gt;|%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

0개의 댓글