성능 튜닝 및 테스트 속도
API 퍼징과 같은 API 퍼즈 테스트를 수행하는 보안 도구는 실행 중인 응용 프로그램의 인스턴스에 요청을 보내어 테스트를 수행합니다. 요청은 퍼징 엔진에 의해 변형되어 응용 프로그램에 존재할 수 있는 예상치 못한 동작을 유발합니다. API 퍼징 테스트의 속도는 다음에 따라 달라집니다:
- 우리의 도구를 통해 응용 프로그램으로 초당 전송할 수 있는 요청의 양
- 응용 프로그램이 요청에 빠르게 응답하는 속도
- 응용 프로그램을 테스트하기 위해 전송해야 하는 요청의 양
- API가 구성된 작업 수
- 각 작업에 포함된 필드 수 (JSON 바디, 헤더, 쿼리 문자열, 쿠키 등을 생각해보세요)
이 성능 가이드에서 제시된 조언을 따라도 API 퍼징 테스트 작업이 예상보다 더 오랜 시간이 걸린다면, 추가 지원을 위해 지원팀에 문의하세요.
성능 문제 진단
성능 문제를 해결하는 첫 번째 단계는 예상보다 더 느린 테스트 시간에 기여하는 요소를 이해하는 것입니다. 우리가 보는 몇 가지 일반적인 문제는 다음과 같습니다:
- API 퍼징이 저CPU 러너에서 실행 중
- 응용 프로그램이 느린/단일 CPU 인스턴스에 배포되어 있으며 테스트 부하를 감당할 수 없음
- 응용 프로그램에 전체 테스트 속도에 영향을 주는 느린 작업이 포함되어 있음(> 1/2 초)
- 응용 프로그램이 많은 양의 데이터를 반환하는 작업이 포함되어 있음(> 500K+)
- 응용 프로그램이 많은 수의 작업을 포함하고 있음(> 40)
응용 프로그램에 전체 테스트 속도에 영향을 주는 느린 작업이 포함되어 있음(> 1/2 초)
API 퍼징 작업 출력에는 테스트 속도, 각 테스트 중인 작업의 응답 속도 및 요약 정보에 대한 유용한 정보가 포함되어 있습니다. 성능 문제를 추적하는 데 사용할 수 있는 샘플 출력을 보기 위해 몇 가지 샘플 출력을 살펴보겠습니다:
API Fuzzing: assets/har-large-response/large_responses.har에서 10개의 작업 로드함
API Fuzzing:
API Fuzzing: 작업 테스트 중 [1/10]: 'GET http://target:7777/api/large_response_json'.
API Fuzzing: - 매개변수: (헤더: 4, 쿼리: 0, 본문: 0)
API Fuzzing: - 요청 본문 크기: 0 바이트 (0 바이트)
API Fuzzing:
API Fuzzing: 'GET http://target:7777/api/large_response_json' 작업 테스트 완료.
API Fuzzing: - 제외된 매개변수: (헤더: 0, 쿼리: 0, 본문: 0)
API Fuzzing: - 수행된 요청: 767개
API Fuzzing: - 평균 응답 본문 크기: 130MB
API Fuzzing: - 평균 호출 시간: 2초 82.69밀리초 (2.082693 초)
API Fuzzing: - 완료까지 걸린 시간: 14분 8초 788.36밀리초 (848.788358 초)
이 작업 콘솔 출력 단편은 찾아진 작업의 수(10)를 알려주면서, 지정된 작업에서 테스트가 시작되었다는 알림을 시작으로 작업의 요약이 완료되었습니다. 요약은 이 로그 출력의 가장 흥미로운 부분입니다. 요약에서 이 작업에 대한 테스트에 완전히 요청하기 위해 API 퍼징이 767개의 요청을 수행했음을 알 수 있습니다. 또한 평균 응답 시간이 2초였고, 이 한 작업에는 완료까지 14분이 걸렸음을 볼 수 있습니다.
2초의 평균 응답 시간은 이 구체적인 작업이 테스트하는 데 오랜 시간이 걸린다는 좋은 초기 지표입니다. 더 나아가서 응답 본문 크기가 상당히 크다는 것을 알 수 있습니다. 이 큰 본문 크기가 문제입니다. 각 요청에서 그만큼의 데이터를 전송하는 것이 2초의 대부분을 차지하는 이유입니다.
이 문제에 대해 팀은 아래와 같이 결정할 수 있습니다:
- 더 많은 vCPU를 갖춘 러너를 사용합니다. 이는 API 퍼징이 수행 중인 작업을 병렬화하는 데 도움이 됩니다. 이는 테스트 시간을 줄이는 데 도움이 되지만, 작업이 긴 시간이 걸리는 문제 때문에 높은 CPU 머신으로 전환하지 않으면 10분 미만으로 테스트를 완료하기 여려울 수 있습니다. 더 큰 러너는 더 비쌉니다만, 빨리 실행되는 경우 작업 실행 시간이 더 짧아지므로 적은 시간에 대해서 지불하게 됩니다.
- 이 작업을 제외합니다. 이것은 가장 간단하지만, 보안 테스트 범위에 공백이 생길 수 있습니다.
- 기능 브랜치 API 퍼징 테스트에서는 이 작업을 제외하고, 기본 브랜치 테스트에는 포함합니다.
- API 퍼징 테스트를 여러 작업으로 나눕니다.
가능한 솔루션은 팀의 요구 사항이 5-7분 정도인 경우 허용 가능한 테스트 시간에 도달하기 위해 이러한 솔루션의 조합을 사용하는 것입니다.
성능 문제 해결
다음 섹션에서는 API 퍼징의 성능 문제를 해결하는 다양한 옵션에 대해 문서화되어 있습니다:
보다 큰 러너 사용
API 퍼징을 통해 성능을 크게 향상시킬 수 있는 가장 쉬운 방법 중 하나는 더 큰 러너를 사용하는 것입니다. 이 표는 Java Spring Boot REST API의 벤치마킹 중 수집된 통계를 보여줍니다. 이 벤치마킹에서 대상과 API 퍼징은 단일 러너 인스턴스를 공유합니다.
Linux용 SaaS 러너 태그 | 초당 요청 수 |
---|---|
saas-linux-small-amd64 (기본값)
| 255 |
saas-linux-medium-amd64
| 400 |
이 표에서 볼 수 있듯이 러너의 크기와 vCPU 수를 늘리면 테스트 속도/성능에 큰 영향을 미칠 수 있습니다.
다음은 Linux용 중간 SaaS 러너를 사용하는 API 퍼징에 대한 예제 작업 정의입니다. 이 작업은 API 퍼징 템플릿을 통해 포함된 작업 정의를 확장합니다.
apifuzzer_fuzz:
tags:
- saas-linux-medium-amd64
gl-api-security-scanner.log
파일에서 문자열 Starting work item processor
를 검색하여 보고된 최대 DOP(병렬성)을 검사할 수 있습니다. 최대 DOP는 러너에 할당된 vCPU 수와 동일하거나 크거나 같아야 합니다. 문제를 식별할 수 없다면, 지원팀에 문의하여 도움을 받으세요.
예시 로그 항목:
17:00:01.084 [INF] <Peach.Web.Core.Services.WebRunnerMachine> Starting work item processor with 4 max DOP
느린 작업 제외하기
한 두 개의 느린 작업이 있는 경우 팀은 해당 작업을 테스트하지 않기로 결정할 수 있습니다. 작업을 제외하는 방법은 FUZZAPI_EXCLUDE_PATHS
구성 변수를 사용하여 해당 작업을 제외하는 것입니다. 이에 대한 설명은 이 섹션에서 찾을 수 있습니다.
이 예시에서는 많은 양의 데이터를 반환하는 작업이 있습니다. 이 작업은 GET http://target:7777/api/large_response_json
입니다. 이를 제외하기 위해 작업 URL의 경로 부분(/api/large_response_json
)과 함께 FUZZAPI_EXCLUDE_PATHS
구성 변수를 제공합니다.
작업이 제외되었는지 확인하려면 API Fuzzing 작업을 실행하고 작업 콘솔 출력을 확인합니다. 이는 테스트의 마지막에 포함된 작업 및 제외된 작업 디렉터리을 포함합니다.
apifuzzer_fuzz:
variables:
FUZZAPI_EXCLUDE_PATHS: /api/large_response_json
테스트 작업을 제외하는 것은 몇 가지 취약점이 감지되지 못할 수 있게 합니다.
테스트를 여러 작업으로 분할하기
API Fuzzing은 테스트를 여러 작업으로 분할하는 것을 지원하며, 이는 FUZZAPI_EXCLUDE_PATHS
및 FUZZAPI_EXCLUDE_URLS
의 사용을 통해 수행됩니다. 테스트를 분할할 때, apifuzzer_fuzz
작업을 비활성화하고 그 대신 식별 가능한 이름의 두 개의 작업으로 대체하는 것이 좋은 방법입니다. 이 예에서는 두 개의 작업을 갖고 있으며 각 작업은 API의 버전을 테스트하므로 그에 맞게 이름이 지정되었습니다. 그러나 이 기법은 API의 버전뿐만 아니라 다른 상황에도 적용할 수 있습니다.
apifuzzer_v1
및 apifuzzer_v2
작업에서 사용하는 규칙은 API Fuzzing 템플릿에서 복사되었습니다.
# 주 작업 apifuzzer_fuzz 비활성화
apifuzzer_fuzz:
rules:
- if: $CI_COMMIT_BRANCH
when: never
apifuzzer_v1:
extends: apifuzzer_fuzz
variables:
FUZZAPI_EXCLUDE_PATHS: /api/v1/**
rules:
rules:
- if: $API_FUZZING_DISABLED == 'true' || $API_FUZZING_DISABLED == '1'
when: never
- if: $API_FUZZING_DISABLED_FOR_DEFAULT_BRANCH == 'true' &&
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
when: never
- if: $API_FUZZING_DISABLED_FOR_DEFAULT_BRANCH == '1' &&
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
when: never
- if: $CI_COMMIT_BRANCH &&
$CI_GITLAB_FIPS_MODE == "true"
variables:
FUZZAPI_IMAGE_SUFFIX: "-fips"
- if: $CI_COMMIT_BRANCH
apifuzzer_v2:
variables:
FUZZAPI_EXCLUDE_PATHS: /api/v2/**
rules:
rules:
- if: $API_FUZZING_DISABLED == 'true' || $API_FUZZING_DISABLED == '1'
when: never
- if: $API_FUZZING_DISABLED_FOR_DEFAULT_BRANCH &&
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
when: never
- if: $CI_COMMIT_BRANCH &&
$CI_GITLAB_FIPS_MODE == "true"
variables:
FUZZAPI_IMAGE_SUFFIX: "-fips"
- if: $CI_COMMIT_BRANCH
기능 브랜치에서 작업 제외, 그러나 기본 브랜치에서는 포함하기
한 두 개의 느린 작업이 있는 경우 팀은 해당 작업을 테스트를 건너뛰거나 기능 브랜치 테스트에서 제외시킬 수 있지만, 기본 브랜치 테스트에서는 이를 포함할 수 있습니다. 작업을 제외하는 방법은 FUZZAPI_EXCLUDE_PATHS
구성 변수를 사용하여 해당 작업을 제외하는 것입니다. 이에 대한 설명은 이 섹션에서 찾을 수 있습니다.
이 예시에서는 많은 양의 데이터를 반환하는 작업이 있습니다. 이 작업은 GET http://target:7777/api/large_response_json
입니다. 이를 제외하기 위해 작업 URL의 경로 부분(/api/large_response_json
)과 함께 FUZZAPI_EXCLUDE_PATHS
구성 변수를 제공합니다. 우리의 구성은 apifuzzer_fuzz
주 작업을 비활성화하고 두 개의 새 작업 apifuzzer_main
및 apifuzzer_branch
을 생성합니다. apifuzzer_branch
는 긴 작업을 제외하고 기본 브랜치 이외의 브랜치에서만 실행되도록 설정되어 있습니다. apifuzzer_main
브랜치는 기본 브랜치(main
이 예제에서)에서만 실행되도록 설정되어 있습니다. apifuzzer_branch
작업은 빠른 개발 주기를 가능하게 하고, 기본 브랜치 빌드에서만 실행되는 apifuzzer_main
작업은 더 오래 걸립니다.
작업이 제외됐는지 확인하려면 API Fuzzing 작업을 실행하고 작업 콘솔 출력을 확인합니다. 이는 테스트의 마지막에 포함된 작업 및 제외된 작업 디렉터리을 포함합니다.
# 두 개의 작업을 만들기 위해 주 작업 비활성화
apifuzzer_fuzz:
rules:
- if: $CI_COMMIT_BRANCH
when: never
# 기능 브랜치 작업에 대한 API Fuzzing, /api/large_response_json을 제외함
apifuzzer_branch:
extends: apifuzzer_fuzz
variables:
FUZZAPI_EXCLUDE_PATHS: /api/large_response_json
rules:
rules:
- if: $API_FUZZING_DISABLED == 'true' || $API_FUZZING_DISABLED == '1'
when: never
- if: $API_FUZZING_DISABLED_FOR_DEFAULT_BRANCH &&
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
when: never
- if: $CI_COMMIT_BRANCH &&
$CI_GITLAB_FIPS_MODE == "true"
variables:
FUZZAPI_IMAGE_SUFFIX: "-fips"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: never
- if: $CI_COMMIT_BRANCH
# 기본 브랜치용 API Fuzzing (우리의 경우 main)
# 긴 실행 작업 포함
apifuzzer_main:
extends: apifuzzer_fuzz
rules:
- if: $API_FUZZING_DISABLED == 'true' || $API_FUZZING_DISABLED == '1'
when: never
- if: $API_FUZZING_DISABLED_FOR_DEFAULT_BRANCH &&
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
when: never
- if: $CI_COMMIT_BRANCH &&
$CI_GITLAB_FIPS_MODE == "true"
variables:
FUZZAPI_IMAGE_SUFFIX: "-fips"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH