성능 튜닝 및 테스트 속도

API Fuzzing과 같은 API 퍼증을 수행하는 보안 도구는 실행 중인 응용 프로그램의 인스턴스로 요청을 보내어 테스트를 수행합니다. 우리의 퍼증 엔진에 의해 변형된 이러한 요청은 응용 프로그램에 존재할 수 있는 예기치 않은 동작을 유발합니다. API 퍼증 테스트의 속도는 다음에 달려 있습니다:

  • 우리의 도구를 통해 응용 프로그램으로 초당 전송할 수 있는 요청의 수
  • 응용 프로그램이 요청에 응답하는 속도
  • 응용 프로그램을 테스트하기 위해 보내야 하는 요청의 수
    • API가 구성된 작업의 수
    • 각 작업에 포함된 필드의 수 (JSON body, 헤더, 쿼리 문자열, 쿠키 등을 고려)

이 성능 가이드에서 제안된 조언을 따라도 API Fuzzing 테스트 작업이 여전히 예상보다 오래 걸릴 경우, 추가 지원을 받기 위해 지원팀에 문의하십시오.

성능 문제 진단

성능 문제를 해결하는 첫 번째 단계는 예상보다 느린 테스트 시간에 기여하는 요소를 이해하는 것입니다. 우리가 종종 볼 수 있는 몇 가지 일반적인 문제는 다음과 같습니다:

  • API Fuzzing이 낮은 vCPU 러너에서 실행 중
  • 응용 프로그램이 느린/단일 CPU 인스턴스에 배포되어 테스트 부하를 견딜 수 없음
  • 응용 프로그램에 전반적인 테스트 속도에 영향을 미치는 느린 작업이 포함됨(> 1/2초)
  • 응용 프로그램에 대량의 데이터를 반환하는 작업이 포함됨(> 500K+)
  • 응용 프로그램에 많은 수의 작업이 포함됨(> 40)

응용 프로그램에 전반적인 테스트 속도에 영향을 미치는 느린 작업이 포함됨(> 1/2초)

API Fuzzing 작업 출력에는 우리가 얼마나 빠르게 테스트하는지, 각 테스트 중인 작업의 응답 속도, 요약 정보 등이 포함되어 유용한 정보를 제공합니다. 성능 문제를 추적하기 위해 샘플 출력을 살펴보겠습니다:

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 Fuzzing이 이 작업 및 관련 필드를 완전히 테스트하는 데 767개의 요청이 소요되었음을 볼 수 있습니다. 또한 평균 응답 시간이 2초이고 작업 당 평균 응답 시간은 14분이었습니다.

2초의 평균 응답 시간은 이 특정 작업이 오래 걸리는 것임을 나타내는 좋은 초기 지표입니다. 더 나아가 응답 바디 크기가 상당히 크다는 것을 알 수 있습니다. 큰 바디 크기가 여기서 문제가 되며, 각 요청마다 그만큼의 데이터를 전송하는 것이 2초의 대부분을 차지합니다.

이 문제에 대해 팀은 다음과 같은 결정을 내릴 수 있습니다:

  • 보다 많은 vCPU를 가진 러너를 사용합니다. 이는 API Fuzzing이 수행 중인 작업을 병렬화하는 데 도움이 되어 테스트 시간을 줄이는 데 도움이 됩니다. 그러나 작업 시간을 10분 이내로 줄이는 것은 여전히 작업이 긴 경우에는 높은 CPU 기계로 이동하지 않으면 문제가 될 수 있습니다. 더 큰 러너는 더 비싸지만, 작업 실행 시간이 더 짧아지면 더 적은 시간에 대해 지불하기 때문에 더 비용이 듭니다.
  • 이 작업을 제외하여 API Fuzzing 테스트를 실행에서 제외합니다. 이것은 가장 간단하지만 보안 테스트 범위에 차질을 빚을 수 있습니다.
  • 특징 브랜치 API Fuzzing 테스트에서 이 작업을 제외하되, 기본 브랜치 테스트에서는 포함.
  • API Fuzzing 테스트를 여러 작업으로 분할합니다.

팀의 요구 사항이 5-7분 범위에 있는 경우, 이러한 솔루션의 조합을 사용하여 합당한 테스트 시간에 도달하는 것이 타당한 해결책일 것으로 예상됩니다.

성능 문제 해결

다음 섹션에서는 API Fuzzing의 성능 문제를 해결하기 위한 여러 옵션을 문서화하고 있습니다:

더 큰 러너 사용하기

가장 쉬운 성능 향상 중 하나는 더 큰 러너 를 사용하여 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 퍼징 작업을 실행하고 작업 콘솔 출력을 확인하십시오. 테스트의 끝에는 포함된 작업과 제외된 작업의 목록이 포함됩니다.

apifuzzer_fuzz:
  variables:
    FUZZAPI_EXCLUDE_PATHS: /api/large_response_json

테스트에서 작업을 제외하는 것은 일부 취약점이 감지되지 못하도록 할 수 있습니다.

테스트를 여러 작업으로 분할하기

테스트를 여러 작업으로 분할하는 것은 API 퍼징을 통해 FUZZAPI_EXCLUDE_PATHSFUZZAPI_EXCLUDE_URLS을 사용하여 지원됩니다. 테스트를 분할할 때, 좋은 패턴은 apifuzzer_fuzz 작업을 비활성화하고 식별 가능한 이름을 가진 두 개의 작업으로 교체하는 것입니다. 이 예시에서는 두 개의 작업이 있으며, 각 작업은 API의 버전을 테스트하므로 이름도 그에 맞게 나타냅니다. 그러나 이 기술은 API의 버전 뿐만 아니라 어떠한 상황에도 적용될 수 있습니다.

apifuzzer_v1apifuzzer_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_jsonFUZZAPI_EXCLUDE_PATHS 구성 변수로 제공합니다. 우리의 구성은 main apifuzzer_fuzz 작업을 비활성화시키고, apifuzzer_mainapifuzzer_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:
    - 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

# 기본 브랜치(이 예에서는 main)를 위한 API Fuzzing
# 긴 작업을 포함합니다
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