OpenFeign Querydsl 6 버전 도입과 QClass 생성 문제

2024. 12. 29. 15:23·Querydsl

프로젝트에서 동적 쿼리를 적용하기 위해 Querydsl을 도입하기로 의사 결정했습니다.

예전 프로젝트에서 사용했던 Querydsl 5.1 버전을 사용하려 했으나 보안 취약점(CVE-2024-49203)이 발견되어 사용할 수 없었습니다.

CVE-2024-49203 보안 취약점

Querydsl and OpenFeign Querydsl Java Library Vulnerability Permits SQL/HQL Injection 내용에 따르면, 보안 취약점은 정렬을 수행하기 위해 `OrderSpecifier`를 사용할 때 공격자가 SQL/HQL Injection 공격을 할 수 있다는 것입니다.

`OrderSpecifier`가 공격자의 입력을 삭제하지 않기 때문에 발생하는 문제로 소개하고 있는데, 좀 더 자세한 내용을 살펴보겠습니다.

 

아래 코드는 위 링크의 내용의 일부를 발췌한 내용입니다.

OrderSpecifier order = new OrderSpecifier(Order.ASC, pathBuilder.get(orderBy));
JPAQuery<Test> orderedQuery = query.orderBy(order);
return orderedQuery.fetch();

위의 코드처럼 `OrderSpecifier`를 생성할 때, Expression 부분(`pathBuilder.get(String property)` 부분)은 정렬할 필드를 의미하고 Presentation 계층에서 받을 수 있습니다.

 

공격자는 정렬 조건을 전달할 때 쿼리 파라미터로 `orderBy`에 정렬할 필드와 정렬 순서를 전달할 수 있습니다.

그러나 `http://localhost:8000/products?orderBy=name+INTERSECT+SELECT+t+FROM+Test+t+WHERE+(SELECT+'2')='2'+ORDER+BY+t.id HTTP/1.1`처럼 입력한 예시를 보면, `orderBy`의 값으로 표현식을 전달할 수 있는데,

전달한 쿼리를 보면 `SELECT '2' = '2'` 표현식은 `True`가 됩니다.

 

이렇게 되면 `Test` 테이블의 모든 레코드를 읽을 수 있게되는 문제가 있습니다.

이 문제는 의도치 않은 정보가 유출되거나 테이블 크기가 클 경우 서비스 거부까지 이어질 수 있는 꽤 심각한 문제입니다.

직접 해결하는 방법

문제는 `pathBuilder.get(String property)` 부분에서 공격자의 SQL 또는 HQL이 그대로 쿼리되기 때문에 직접 필드를 지정하면 간단하게 해결할 수 있습니다.

// myQClass 내의 id 필드로 정렬하는 예시

queryFactory
        .selectFrom(myQclass)
        .orderBy(new OrderSpecifier<>(Order.DESC, myQclass.id))
        .fetch();

 

만약 동적으로 여러 필드를 이용해 정렬을 수행해야 한다면 아래 코드 처럼 pathBuilder를 이용할 수도 있습니다. (보일러 플레이트 코드가 생기니 적절하게 함수로 빼면 되겠죠)

// Pageable 을 이용해 복합 필드로 정렬하는 예시

// OrderSpecifier 리스트 생성
List<OrderSpecifier> specifiers = new ArrayList<>();

for (Sort.Order order : pageable.getSort()) {
    String property = order.getProperty();
    if (property.equals("fieldA")) {
        PathBuilder<MyQClass> pathBuilder = new PathBuilder<>(myQClass.getType(), myQClass.getMetadata());
        specifiers.add(new OrderSpecifier<>(Order.DESC, pathBuilder.get(property, myQclass.fieldA.getType())));
    }
    if (property.equals("fieldB")) {
        PathBuilder<MyQClass> pathBuilder = new PathBuilder<>(myQClass.getType(), myQClass.getMetadata());
        specifiers.add(new OrderSpecifier<>(Order.DESC, pathBuilder.get(property, myQclass.fieldB.getType())));
    }
}

// orderBy 적용
queryFactory.selectFrom(myQClass)
        .orderBy(specifiers.toArray(OrderSpecifier[]::new));

 

다른 Querydsl 프로젝트 찾기

master 브랜치의 마지막 merge

Querydsl 프로젝트는 2024년 5월 11일 마지막으로 JDK21 호환을 위한 패치, 충돌 해결 이외의 별다른 활동이 없습니다.

openfeign 팀에서 포크한 프로젝트에서 패치가 됨을 확인

다른 querydsl 프로젝트에서 해결할 수 있는 방법이 없나 찾다 `CVE-2024-49203` 문제를 제기한 Issue 글들이 있었습니다.

몇 년 전부터 OpenFeign 팀에서 포크해서 유지 보수 중인 프로젝트가 있었고, 해당 보안 취약점을 패치한 것을 알 수 있었는데요,

마이그레이션은 정말 간단하게 `com.querydsl` 부분을 `io.github.openfeign.querydsl`로 바꾸기만 하면 된다고 합니다.

OpenFeign Querydsl 6 도입하기

QueryDSL 6.0 부터 Hibernate 6.4가 완전히 통합

기존에는 예전 프로젝트에서 사용했던 Querydsl 5 버전을 사용하려 했지만, Querydsl 6 버전을 도입하기로 의사 결정했습니다.

그 이유는 OpenFeign 팀의 Querydsl 프로젝트 릴리즈 노트에서 확인할 수 있었는데요.

Spring Boot 3.x 버전부터 Hibernate 6.x 버전을 사용하는데, Querydsl 5 버전은 Hibernate 6 버전을 완전히 지원하지 않았습니다.

Querydsl 6 버전부터 Hibernate 6.4 버전을 완전히 지원한다고 나와있어 Querydsl 6 버전을 도입하지 않을 이유가 없습니다.

maven에서 보안 취약점이 패치된 버전 확인

 

의존성 추가

`build.gradle`에 의존성 추가를 합니다. 참고로 Querydsl 6 버전은 `jakarta` 패키지 의존성을 추가할 필요가 없습니다.

jakarta 패키지 의존성을 갖는다.

/* querydsl*/
implementation 'io.github.openfeign.querydsl:querydsl-jpa:6.10.1'
annotationProcessor 'io.github.openfeign.querydsl:querydsl-apt:6.10.1' // QClass 생성이 안됨
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

 

이렇게 설정하고 빌드를 하면 QClass가 생성되지 않은 문제가 있었습니다.

jpa 아티팩트

이를 해결하기 위해선 `annotationProcessor 'io.github.openfeign.querydsl:querydsl-apt:6.10.1'` 부분에 `jpa` 아티팩트 사용함을 명시해야 합니다.

 

AS-IS

annotationProcessor 'io.github.openfeign.querydsl:querydsl-apt:6.10.1'

 

TO-BE

annotationProcessor 'io.github.openfeign.querydsl:querydsl-apt:6.10.1:jpa'

 

의존성 추가 최종 버전

/* querydsl */
implementation 'io.github.openfeign.querydsl:querydsl-jpa:6.10.1'
annotationProcessor 'io.github.openfeign.querydsl:querydsl-apt:6.10.1:jpa'
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

 

이렇게 설정하고 빌드를 하면 build 폴더에 QClass가 정상적으로 생성됨을 확인할 수 있습니다.

QClass 생성

 

프로젝트에서 Querydsl을 사용하시는 분들이 이 포스팅을 읽고 도움이 되셨으면 합니다.

긴 글 읽어주셔서 감사합니다.

'Querydsl' 카테고리의 다른 글

[Querydsl] MySQL 공간 데이터(Point)의 반경 검색 (ST_CONTAINS)  (1) 2024.07.03
'Querydsl' 카테고리의 다른 글
  • [Querydsl] MySQL 공간 데이터(Point)의 반경 검색 (ST_CONTAINS)
옐리yelly
옐리yelly
전시회에서 도슨트를 따라다니며 작품 해설을 들으면 더 재밌었던 기억들이 있습니다. 글로 더 재밌는 개발이 되도록 노력하고 있습니다.
  • 옐리yelly
    개발 갤러리
    옐리yelly
  • 전체
    오늘
    어제
    • 모든 글 보기 (82)
      • Project (22)
      • Java (4)
      • Spring (6)
      • Kubernetes (6)
      • Docker (2)
      • JPA (2)
      • Querydsl (2)
      • MySQL (8)
      • ElasticSearch (7)
      • DevOps (4)
      • Message Broker (3)
      • Git & GitHub (2)
      • Svelte (1)
      • Python (8)
        • Python Distilled (4)
        • Anaconda (1)
        • Django (0)
        • pandas (3)
      • Algorithm (1)
      • Computer Science (0)
      • 내 생각 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Spring
    리팩토링
    데드락
    querydsl
    성능 테스트
    devops
    argocd
    elasticsearch
    RabbitMQ
    프로젝트
    Python
    gitops
    Project
    OOP
    Message Broker
    pymysql
    예약 시스템
    커넥션 풀
    JPA
    pandas
    k8s
    dataframe
    blue-green 배포
    포텐데이
    비사이드
    svelte
    nks
    docker
    MySQL
    ncloud
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
옐리yelly
OpenFeign Querydsl 6 버전 도입과 QClass 생성 문제
상단으로

티스토리툴바