성능 조정 및 테스트 속도
API 퍼징(API Fuzzing)과 같은 보안 도구는 실행 중인 애플리케이션의 인스턴스에 요청을 전송하여 테스트를 수행합니다. 요청은 우리의 퍼징 엔진에 의해 변형되어 애플리케이션에 존재할 수 있는 예기치 않은 동작을 유발합니다. API 퍼징 테스트의 속도는 다음 사항에 따라 달라집니다:
- 우리의 도구가 애플리케이션에 초당 얼마나 많은 요청을 보낼 수 있는지
- 애플리케이션이 요청에 얼마나 빠르게 응답하는지
- 애플리케이션을 테스트하기 위해 얼마나 많은 요청을 전송해야 하는지
- API가 구성하고 있는 작업의 수
- 각 작업에 포함된 필드의 수 (JSON 본문, 헤더, 쿼리 문자열, 쿠키 등)
이 성능 가이드의 조언을 따랐음에도 불구하고 API 퍼징 테스트 작업이 여전히 예상보다 오래 걸린다면, 추가 지원을 위해 지원팀에 문의하십시오.
성능 문제 진단
성능 문제를 해결하는 첫 번째 단계는 예상보다 느린 테스트 시간의 원인을 이해하는 것입니다. 일반적으로 우리가 보는 몇 가지 문제는 다음과 같습니다:
- API 퍼징이 저사양 vCPU 러너에서 실행되고 있다
- 애플리케이션이 느리거나 단일 CPU 인스턴스에 배포되어 테스트 부하를 따라잡지 못하고 있다
- 애플리케이션에 전체 테스트 속도에 영향을 미치는 느린 작업이 포함되어 있다 (> 1/2 초)
- 애플리케이션에 대량의 데이터를 반환하는 작업이 포함되어 있다 (> 500K+)
- 애플리케이션에 많은 수의 작업이 포함되어 있다 (> 40)
애플리케이션에 전체 테스트 속도에 영향을 미치는 느린 작업이 포함되어 있다 (> 1/2 초)
API 퍼징 작업의 출력에는 우리가 얼마나 빠르게 테스트하고 있는지, 각 테스트된 작업이 얼마나 빠르게 응답하는지, 그리고 요약 정보에 대한 유용한 정보가 포함되어 있습니다. 성능 문제를 추적하는 데 어떻게 사용될 수 있는지 보려면 몇 가지 샘플 출력을 살펴보겠습니다:
API Fuzzing: Loaded 10 operations from: assets/har-large-response/large_responses.har
API Fuzzing:
API Fuzzing: Testing operation [1/10]: 'GET http://target:7777/api/large_response_json'.
API Fuzzing: - Parameters: (Headers: 4, Query: 0, Body: 0)
API Fuzzing: - Request body size: 0 Bytes (0 bytes)
API Fuzzing:
API Fuzzing: Finished testing operation 'GET http://target:7777/api/large_response_json'.
API Fuzzing: - Excluded Parameters: (Headers: 0, Query: 0, Body: 0)
API Fuzzing: - Performed 767 requests
API Fuzzing: - Average response body size: 130 MB
API Fuzzing: - Average call time: 2 seconds and 82.69 milliseconds (2.082693 seconds)
API Fuzzing: - Time to complete: 14 minutes, 8 seconds and 788.36 milliseconds (848.788358 seconds)
이 작업 콘솔 출력의 일부는 우리가 찾은 작업 수(10)에 대한 정보를 제공하고, 특정 작업에 대한 테스트가 시작되었음을 알리며, 작업이 완료되었음을 요약하는 내용을 포함하고 있습니다. 요약 정보는 이 로그 출력의 가장 흥미로운 부분입니다. 요약에서 우리는 API 퍼징이 이 작업과 관련된 필드를 완전히 테스트하는 데 767 요청이 필요했다는 것을 볼 수 있습니다. 또한 평균 응답 시간이 2초였고, 이 하나의 작업을 완료하는 데 14분이 걸렸다는 것을 알 수 있습니다.
평균 응답 시간이 2초라는 것은 이 특정 작업이 테스트하는 데 오랜 시간이 걸린다는 것을 나타내는 좋은 초기 지표입니다. 더군다나 응답 본체의 크기가 상당히 큽니다. 이처럼 큰 본체의 크기가 문제로, 각 요청에서 그렇게 많은 데이터를 전송하는 것이 2초의 대부분을 차지합니다.
이 문제에 대해 팀은 다음과 같은 결정을 내릴 수 있습니다:
- 더 많은 vCPU가 있는 러너를 사용하여 API 퍼징이 수행하는 작업을 병렬화합니다. 이는 테스트 시간을 줄이는 데 도움이 되지만, 이 작업의 테스트 시간이 얼마나 걸리는지를 고려했을 때 10분 이내로 줄이는 것은 여전히 문제가 될 수 있습니다. 더 큰 러너는 비용이 더 비싸지만, 작업 실행 시간이 더 빠르면 지출하는 분은 줄어듭니다.
- 이 작업을 API 퍼징 테스트에서 제외합니다. 이는 가장 간단하지만 보안 테스트 범위에 공백이 생긴다는 단점이 있습니다.
- 기능 브랜치 API 퍼징 테스트에서 이 작업을 제외하되 기본 브랜치 테스트에는 포함합니다.
- API 퍼징 테스트를 여러 작업으로 나누어 수행합니다.
가능한 해결책은 팀의 요구 사항이 5-7분 범위에 있다고 가정할 때 이러한 해결책의 조합을 사용하여 수용 가능한 테스트 시간을 도달하는 것입니다.
성능 문제 해결
다음 섹션에서는 API 퍼징의 성능 문제를 해결하기 위한 다양한 옵션을 문서화합니다:
더 큰 러너 사용하기
성능을 높이는 가장 쉬운 방법 중 하나는 API Fuzzing과 함께 더 큰 러너를 사용하는 것입니다. 이 표는 Java Spring Boot REST API의 벤치마킹 동안 수집된 통계를 보여줍니다. 이 벤치마크에서 대상과 API 퍼징은 단일 러너 인스턴스를 공유합니다.
리눅스 호스팅 러너 태그 | 초당 요청 수 |
---|---|
saas-linux-small-amd64 (기본) |
255 |
saas-linux-medium-amd64 |
400 |
이 표에서 볼 수 있듯이, 러너의 크기와 vCPU 수를 증가시키면 테스트 속도/성능에 큰 영향을 미칠 수 있습니다.
다음은 리눅스에서 중간 SaaS 러너를 사용하기 위해 tags
섹션을 추가한 API Fuzzing의 작업 정의 예입니다. 이 작업은 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> 작업 항목 프로세서를 4 최대 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 퍼징 템플릿에서 복사한 것입니다.
# 기본 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