오늘도 삽질중

spring SQL injection 방지 (with. paging sort dynamic query) 본문

스프링

spring SQL injection 방지 (with. paging sort dynamic query)

Choi3950 2023. 9. 10. 17:46
반응형

프로젝트에서 동적으로 쿼리를 처리하는 API 에서 SQL Injection이 발생하는 취약점이 확인됬다.

 

발생 위치는 페이징 처리 API 에서 파라미터로 sort 를 넘겨주는 부분에 임의의 쿼리를 삽입 할 경우 그대로 실행되었다.

직접 테스트해보니 DB 자체에 sleep 을 걸수도 있고, 잘하면 삭제 및 데이터 추가도 가능하다고 판단되었다.

 

우선 해당 프로젝트는 아래처럼 구조가 되어있다.

 

 

getOrderByProperty 는 아래처럼 구성되어 있다.

protected OrderSpecifier<?>[] getOrderByProperty(Sort sort, Class clazz){
        return sort.stream().map(x -> {
            
            //문제가 되는 부분
            String property = x.getProperty();

            Sort.Direction direction = x.getDirection();
            boolean isAscending = direction.isAscending();
            Order order = isAscending ? Order.ASC : Order.DESC;
            
            
            if(....)
            else if(....)
            ....
            ....
            ....
            
            //동적으로 처리되는 부분
            SimplePath<?> filedPath = Expressions.path(clazz, property);
            return new OrderSpecifier(order, filedPath, nullHandling);
        }).toArray(OrderSpecifier[]::new);
    }

 

 

첫번째 해결방법으로 각 API 마다 조건에 맞는 별도의 OrderSpecifier 함수를 구현하여 임의의 값이 들어오더라도 동적으로 처리되는게 아니라 Throw 또는 기본 리턴값을 매핑해준다.

 

사실 이 방법이 가장 깔끔하긴 하지만 시간상의 제약과 고칠 경우 전수 테스트 및 사이드 이펙트를 고려하여 차후 진행하기로 협의하였고,

기존에 쓰고 있는 getOrderByProperty() 함수를 수정하여 SQL Injection 을 방어하는 것으로 진행했다.

 

우선 문제가 되는 부분은 property 부분이였다. (위 소스 참조)

직접 임의의 DB 명령어를 sort 파라미터에 삽입 후 로그를 찍어보니 그대로 명령문이 출력되고 실행되었다.

 

그래서 property 에 해당하는 부분에 의도하지 않은 특수문자 및 DB 관련 실행문이 들어갈 경우 Throw 처리를 하도록 수정하였다.

 

	//property 에 의도하지 않은 특수문자 및 DB관련 명령어가 들어가는지 검사한다.
    protected boolean isValidProperty(String property){
        Pattern specialPattern = Pattern.compile("[`\"\\-#()@;=*/+]");
        boolean isFindSpecial = specialPattern.matcher(property).find();
        if (isFindSpecial) {
            return false;
        }

        String regex = "(union|select|from|where|null|nulls)";
        Pattern stringPattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        boolean isFindString = stringPattern.matcher(property).find();
        if (isFindString) {
            return false;
        }

        return true;
    }
    
    
    //property 유효성 검증 추가
    protected OrderSpecifier<?>[] getOrderByProperty(Sort sort, Class clazz){
        return sort.stream().map(x -> {
            
            //문제가 되는 부분
            String property = x.getProperty();
            
            //property 가 유효하지 않으면 Throw 처리
            if (!isValidProperty(property)){
                throw new BadRequestException(ExceptionMessage.BAD_REQUEST);
            }

            Sort.Direction direction = x.getDirection();
            boolean isAscending = direction.isAscending();
            Order order = isAscending ? Order.ASC : Order.DESC;
            
            
            if(....)
            else if(....)
            ....
            ....
            ....
            
            //동적으로 처리되는 부분
            SimplePath<?> filedPath = Expressions.path(clazz, property);
            return new OrderSpecifier(order, filedPath, nullHandling);
        }).toArray(OrderSpecifier[]::new);
    }
반응형

'스프링' 카테고리의 다른 글

spring ExceptionHandler Response remove stackTrace, cause  (0) 2023.09.10
Comments