오픈소스 CATS로 간단히 퍼즈(Fuzz) 테스트

퍼징이란

퍼즈(Fuzz) 또는 퍼징(Fuzzing)이란 블랙박스 테스트의 한 방법으로 정상/비정상 데이터를 자동으로 생성하고 함수나 API에 전달한다. 어떻게 보면 암호 해독(crypto anaylysis) 과정과 비슷하게 보이지만 퍼징은 프로토콜이나 입력 데이터 타입에 의존적이다. 그래서 실패한 테스트 결과들은 암호 해독과는 다르게 실제로 유의미하다.

여담으로 몇년전 대기업에서는 유사한 테스트를 수행하기 위해서 전문 인력까지 두었었다. 그때도 도구를 사용하긴 했지만 테스트 설정부터 수행 후 보고서 작성까지 상당히 많은 부분에서 고급 인력들을 투입했었는데, 이번에 발견한 도구는 만약 테스트 대상이 Rest API이고 Open API 스펙 (i.e. swagger)을 가지고 있다면 CI/CD에 바로 연동하고 바로 깔끔한 리포트를 확인할 수 있을 정도로 고도화 되었다. 소프트웨어 테스트쪽은 정말 빠르게 컴퓨터로 대체되어가고 있는 것 같다.

CATS

https://github.com/Endava/cats

CATS는 자바 기반의 라이브러리로 거의 코딩을 하지 않고도 몇백가지의 API테스트를 자동으로 수행해준다. 6.0 버전 기준으로 76개의 퍼저(Fuzzer)가 존재한다.

퍼저는 크게 5가지로 분류된다

  • 필드 퍼저 – Post 요청의 몸통이나 URL의 경로 변수(path variable)을 대상으로 하는 퍼저
  • 헤더 퍼저 - HTTP 헤더들을 대상으로하는 퍼저
  • HTTP 퍼저 - 필드나 헤더와 관계없는 HTTP 요청을 대상으로하는 퍼저
  • API 계약 검증 퍼저 – Open API 정의가 베스트 프랙티스를 따르고 있는지 검사하는 퍼저.
  • 특수 퍼저 - 보안이나 특별한 절차를 필요로 하는 조금 더 복잡한 행동들을 테스트하기 위한 퍼저

팀에서 관리중인 내부 API는 이미 swagger 2.0 으로 관리되고 있었기 때문에 CATS jar를 다운 받은 뒤 아주 간단하게 테스트를 실행할 수 있었다.

cats.jar --contract=swagger.yaml --server=$FUZZ_SERVER_ADDR --headers=header.yml --refData=refData.yml

아틀라시안은 내부 API 인증에 JWT토큰의 일종인 ASAP을 사용하기 때문에 모든 API 호출에 Authorization 헤더를 제공해줘야 한다. herader.yml 에는 다음과 같이 작성한다. CI/CD에서는 매 빌드마다 값을 갱신 시켜준다.

all:
  Authorization: Bearer TOKEN_PLACE_HOLDER

그 다음이 데이터 파일로 API에 비즈니스 특유의 값을 제공한다. 여기서는 간단히 고정된 값을 사용했지만 Apache Common의 라이브러리등도 호출이 가능해 보인다.

all:
    boardId: 29c3fd3c-0239-3cbb-ac02-3ef08e267d4f
    columnId: 52

이것으로 기본적인 준비는 모두 끝이다. 로컬에서 돌아가는 것을 확인하고 바로 Bitbucket Cloud 의 custom Pipe를 생성해 CATS jar를 포함하는 도커이미지를 생성한 뒤 API 리포지토리의 파이프라인에서 일주일에 한번씩 테스트가 수행되도록 설정해 놓았다. Github Action을 사용해서도 유사한 결과를 얻을 수 있으리라 생각한다.

테스트 결과

실제 내가 관리하고 있는 API는 다음과 같은 테스트들이 실패했다. 실패한 모든 테스트들을 수정하지는 않을 것 이다. 아마 REST API가 외부에 공개된 것이라면 대단히 유용할 것 같다.

권당되는 헤더를 찾을 수 없다는 에러, 자세한 내용은 아래 링크에 있고  사설 API이기 때문에 이는 무시해도 좋을 것 같다.   [{name=X-XSS-Protection, value=1; mode=block}]

X-XSS-Protection – Preventing Cross-Site Scripting Attacks – KeyCDN

중복된 헤더입력

중복된 헤더입력을 허용하고 내부적으로는 리스트로 변환되고 있음. HTTP 스펙에서는 허용되지만 보안상 권장되지 않음.

HTTP Desync Attacks in the Wild and How to Defend Against Them | Imperva

유니코드 제어 문자를 인식하지 못함

유니코드 제어문자는 시각적으로 표현되는 데이터가 아니기 때문에 입력에 포함되어 있을 경우에 일반 문자가 아니라 제어문자로 인식되어야 한다.

Unicode control characters

권장되는 REST API 네이밍 규칙을 따르지 않음

API경로에 복수형, 명사, 소문자를 사용해야 하며 엔드포인트에는 스네이크 케이스나 케밥 케이스를 사용해야한다. JSON 프로퍼티에는 스네이크 케이스와 카멜 케이스를 허용함.

새로운 JSON 필드 입력을 허용함

요청의 몸통에 새로운 JSON 필드 입력을 허용하고 있는데 OWASP 에서는 이경우에 요청을 거절하도록 권장한다.

REST Security – OWASP Cheat Sheet Series

존재하지 않는 HTTP 메서드에 대해서 405(Method Not Allowed)를 반환하지 않음

제목 그대로 CONTENT와 같이 존재하지 않는 HTTP 메서드에 대해 403 에러를 반환하고 있다.

지원하지 않는 Content Types 헤더를 허용함

Content-Type 헤더에 OpenAPI 계약아 정의되지 않은 값을 보냈을 때 요청을 허용함. OWASP 는 검증을 수행하도록 권장한다.

REST Security – OWASP Cheat Sheet Series

API 사양의 GET 경로에 권장되는 헤더인 TracedId/CorrelationId가 존재하지 않음.

마치며

실제 jar 파일을 다운받아서 로컬에서 실험해 보는데는 한시간도 걸리지 않았다. Open API 스펙을 가지고 있다면 반드시 돌려보면 좋을 것 같다. CATS에서 좀 아쉬운 점은 테스트 갯수가 엄청나게 많은데 비해 클라이언트 측 Rate limiting 이나 Throttling 기능이 있어야 할 것 같다. 많은 API 응답이 503 에러를 반환해서 의도치 않게 성능 테스트가 되어버릴 수도 있다.