성능 튜닝 및 테스팅 속도
API 퍼징과 같이 API 퍼즈 테스팅을 수행하는 보안 도구는 실행 중인 애플리케이션 인스턴스에 요청을 전송하여 테스팅을 수행합니다. 이 성능 가이드의 조언을 따른 후에도 API 퍼징 테스팅 작업이 예상보다 오래 걸리면 지원팀에 추가 도움을 요청하세요.
API 퍼징과 같이 API 퍼즈 테스팅을 수행하는 보안 도구는 실행 중인 애플리케이션 인스턴스에 요청을 전송하여 테스팅을 수행합니다. 요청은 퍼징 엔진에 의해 변경되어 애플리케이션에 존재할 수 있는 예상치 못한 동작을 트리거합니다. API 퍼징 테스트의 속도는 다음에 따라 달라집니다:
- GitLab 도구가 애플리케이션에 초당 몇 개의 요청을 전송할 수 있는지
- 애플리케이션이 요청에 얼마나 빠르게 응답하는지
- 애플리케이션을 테스트하려면 얼마나 많은 요청을 전송해야 하는지
- 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개의 요청이 필요했음을 보여줍니다. 또한 이 작업이 완료하는 데 14분이 걸렸으며 평균 응답 시간은 2초였음을 보여줍니다.
2초의 평균 응답 시간은 이 특정 작업을 테스트하는 데 오랜 시간이 걸린다는 초기 지표입니다. 응답 본문 크기가 크다는 것도 볼 수 있으며, 이것이 긴 응답 시간의 원인입니다. 각 요청의 응답 시간 대부분은 응답 본문 데이터를 전송하는 데 소요됩니다.
이 이슈에 대해 팀은 다음을 결정할 수 있습니다:
- 더 많은 vCPU가 있는 러너를 사용합니다. 이렇게 하면 API 퍼징이 수행되는 작업을 병렬화할 수 있어 테스트 시간을 줄이는 데 도움이 됩니다. 이 작업을 테스트하는 데 얼마나 오랜 시간이 걸리는지 때문에 높은 CPU 머신으로 이동하지 않으면 10분 이하로 테스트를 줄이기 어려울 수 있습니다. 더 큰 러너는 비용이 더 많이 들지만 작업 실행이 빠르면 사용하는 분도 줄어듭니다.
- API 퍼징 테스트에서 이 작업을 제외합니다. 이것이 가장 간단하지만 보안 테스트 범위에 공백이 생기는 단점이 있습니다.
- 기능 브랜치 API 퍼징 테스트에서 작업을 제외하되 기본 브랜치 테스트에는 포함합니다.
- API 퍼징 테스팅을 여러 작업으로 분할합니다.
팀의 요구 사항이 5-7분 범위에 있다고 가정하면 이러한 솔루션의 조합을 사용하여 허용 가능한 테스트 시간에 도달할 가능성이 높습니다.
성능 이슈 해결#
다음 섹션에서는 API 퍼징의 성능 이슈를 해결하기 위한 다양한 옵션을 문서화합니다:
더 큰 러너 사용#
API 퍼징에 더 큰 러너를 사용하면 가장 쉬운 성능 향상 중 하나를 달성할 수 있습니다. 이 표는 Java Spring Boot REST API의 벤치마킹 중에 수집된 통계를 보여줍니다. 이 벤치마크에서 대상과 API 퍼징은 단일 러너 인스턴스를 공유합니다.
| Linux의 호스팅된 러너 태그 | 초당 요청 수 |
|---|---|
saas-linux-small-amd64 (기본값) |
255 |
saas-linux-medium-amd64 |
400 |
이 표는 러너 크기와 vCPU 수를 늘리면 테스팅 속도/성능에 큰 영향을 미칠 수 있음을 보여줍니다.
다음은 API 퍼징용 작업 정의 예제로, Linux의 중간 SaaS 러너를 사용하기 위해 tags 섹션을 추가합니다. 작업은 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] 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 퍼징 작업을 실행하고 작업 콘솔 출력을 검토합니다. 테스트 끝에 포함된 작업과 제외된 작업 목록이 포함됩니다.
apifuzzer_fuzz:
variables:
FUZZAPI_EXCLUDE_PATHS: /api/large_response_json
테스트에서 작업을 제외하면 일부 취약성이 탐지되지 않을 수 있습니다.
테스트를 여러 작업으로 분할#
테스트를 여러 작업으로 분할하는 것은 FUZZAPI_EXCLUDE_PATHS 및 FUZZAPI_EXCLUDE_URLS를 사용하여 API 퍼징에서 지원됩니다. 테스트를 분할할 때 좋은 패턴은 apifuzzer_fuzz 작업을 비활성화하고 식별 이름이 있는 두 개의 작업으로 교체하는 것입니다. 이 예제는 두 개의 작업을 보여줍니다. 각 작업은 이름에 반영된 대로 API 버전을 테스트합니다. 그러나 이 기술은 API 버전에만 국한되지 않고 모든 상황에 적용할 수 있습니다.
apifuzzer_v1 및 apifuzzer_v2 작업에 사용된 규칙은 API 퍼징 템플릿에서 복사되었습니다.
# Disable the main apifuzzer_fuzz job
apifuzzer_fuzz:
rules:
- if: $CI_COMMIT_BRANCH
when: never
apifuzzer_v1:
extends: apifuzzer_fuzz
variables:
FUZZAPI_EXCLUDE_PATHS: /api/v1/**
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:
- 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입니다. 제외하려면
FUZZAPI_EXCLUDE_PATHS 구성 변수에 작업 URL의 경로 부분
/api/large_response_json을 제공합니다. 구성은 메인
apifuzzer_fuzz 작업을 비활성화하고 두 개의 새 작업 apifuzzer_main과
apifuzzer_branch를 생성합니다. apifuzzer_branch는 긴
작업을 제외하고 비기본 브랜치(예: 기능 브랜치)에서만 실행되도록 설정됩니다.
apifuzzer_main 브랜치는 기본 브랜치
(이 예에서는 main)에서만 실행되도록 설정됩니다. apifuzzer_branch 작업은 더 빠르게 실행되어
빠른 개발 주기를 가능하게 하는 반면, 기본 브랜치 빌드에서만 실행되는 apifuzzer_main 작업은
실행하는 데 더 오래 걸립니다.
작업이 제외되었는지 확인하려면 API 퍼징 작업을 실행하고 작업 콘솔 출력을 검토합니다. 테스트 끝에 포함된 작업과 제외된 작업 목록이 포함됩니다.
# Disable the main job so you can create two jobs with
# different names
apifuzzer_fuzz:
rules:
- if: $CI_COMMIT_BRANCH
when: never
# API fuzzing for feature branch work, excludes /api/large_response_json
apifuzzer_branch:
extends: apifuzzer_fuzz
variables:
FUZZAPI_EXCLUDE_PATHS: /api/large_response_json
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 for default branch (main in this case)
# Includes the long running operations
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
