성능 튜닝 및 테스트 속도

API 보안 테스트와 같은 동적 분석 테스트를 수행하는 보안 도구는 실행 중인 애플리케이션의 인스턴스로 요청을 보내어 테스트를 수행합니다. 이 요청은 애플리케이션에 존재할 수 있는 특정 취약성을 테스트하기 위해 설계되었습니다. 동적 분석 테스트의 속도는 다음에 따라 달라집니다.

  • 우리의 도구를 통해 애플리케이션에 초당 전송할 수 있는 요청의 수
  • 애플리케이션이 요청에 얼마나 빠르게 응답하는가
  • 애플리케이션을 테스트하기 위해 보내야 하는 요청의 수
    • API가 어떤 작업으로 구성되어 있는가
    • 각 작업에는 몇 개의 필드가 있는가 (JSON 본문, 헤더, 쿼리 문자열, 쿠키 등)

만약 API 보안 테스트 작업이 여전히 예상보다 더 오랜 시간이 걸린다면, 이 성능 가이드에 따른 조언을 따라도 여전히 지원팀에 문의하여 추가 지원을 받으십시오.

성능 문제 진단

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

  • API 보안 테스트가 저용량 vCPU 러너에서 실행 중
  • 애플리케이션이 느린/단일 CPU 인스턴스에 배포되어 테스트 부하를 따라갈 수 없음
  • 애플리케이션이 전체 테스트 속도에 영향을 주는 느린 작업이 포함되어 있음(> 1/2초)
  • 애플리케이션이 많은 양의 데이터를 반환하는 작업이 포함되어 있음(> 500K+)
  • 애플리케이션이 많은 수의 작업을 포함하고 있음(> 40)

애플리케이션이 전체 테스트 속도에 영향을 주는 느린 작업이 포함되어 있음(> 1/2초)

API 보안 테스트 작업 출력에는 테스트 속도, 각 테스트 중인 작업의 응답 속도, 요약 정보에 대한 유용한 정보가 포함되어 있습니다. 성능 문제를 찾아내는 데 이 출력을 어떻게 사용할 수 있는지 알아봅시다:

DAST API: Loaded 10 operations from: assets/har-large-response/large_responses.har
DAST API:
DAST API: Testing operation [1/10]: 'GET http://target:7777/api/large_response_json'.
DAST API:  - Parameters: (Headers: 4, Query: 0, Body: 0)
DAST API:  - Request body size: 0 Bytes (0 bytes)
DAST API:
DAST API: Finished testing operation 'GET http://target:7777/api/large_response_json'.
DAST API:  - Excluded Parameters: (Headers: 0, Query: 0, Body: 0)
DAST API:  - Performed 767 requests
DAST API:  - Average response body size: 130 MB
DAST API:  - Average call time: 2 seconds and 82.69 milliseconds (2.082693 seconds)
DAST API:  - Time to complete: 14 minutes, 8 seconds and 788.36 milliseconds (848.788358 seconds)

이 작업 콘솔 출력 스니펫은 몇 가지 작업이 발견된 것(10)을 알려주며, 특정 작업에서 테스트가 시작되었음과 그 작업에 대한 요약이 완료되었다는 알림이 나옵니다. 이 요약은 이 로그 출력의 가장 흥미로운 부분입니다. 요약에서 우리는 이 작업 및 관련 필드를 완전히 테스트하기 위해 API 보안 테스트가 767개의 요청을 보냈고 그것이 얼마나 오랜 시간이 걸렸는지 볼 수 있습니다. 또한 평균 응답 시간이 2초이고 이 한 작업에 대해 완료하는 데 걸린 시간이 14분임을 볼 수 있습니다.

평균 응답 시간이 2초인 것은 이 특정 작업이 테스트를 수행하는 데 오랜 시간이 걸린다는 초기 증거입니다. 더욱이 응답 본문 크기가 상당히 크다는 것을 알 수 있습니다. 큰 본문 크기가 문제이며 각 요청에 대해 이렇게 많은 데이터를 전송하는 것이 그 2초의 대부분을 차지합니다.

이 문제에 대해 팀은 아마도 다음과 같은 결정을 내릴 것입니다:

팀의 요구 사항이 5-7분 대에 있는 경우 이러한 솔루션을 조합하여 수용 가능한 테스트 시간에 도달하는 것이 가능할 것으로 예상됩니다.

성능 문제 해결

다음 섹션에서는 API 보안 테스트의 성능 문제를 해결하기 위한 여러 옵션에 대해 문서로 작성합니다:

더 큰 러너 사용하기

API 보안 테스트에 대한 가장 쉬운 성능 향상 중 하나는 보다 큰 러너를 사용하는 것입니다. 이 표에서는 Java Spring Boot REST API의 벤치마킹 중 수집한 통계를 보여줍니다. 이 벤치마킹에서 타겟과 API 보안 테스트가 동일한 러너 인스턴스를 공유합니다.

호스팅된 러너 Linux 태그 초당 요청 수
saas-linux-small-amd64 (기본값) 255
saas-linux-medium-amd64 400

이 표에서 볼 수 있듯이 러너 크기와 vCPU 수를 증가시키는 것이 테스트 속도/성능에 큰 영향을 미칠 수 있습니다.

다음은 API 보안 테스트를 위한 크기가 중간인 SaaS 러너를 사용하기 위해 tags 섹션을 추가한 예제 작업 정의입니다. 이 작업은 API 보안 테스트 템플릿을 통해 작업 정의를 확장합니다.

dast_api:
  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

느린 작업 제외하기

한 또는 두 개의 느린 작업의 경우 팀은 해당 작업의 테스트를 건너뛰기로 결정할 수 있습니다. 작업을 제외하는 것은 이 작업의 URL 경로 부분인 /api/large_response_json과 같은 DAST_API_EXCLUDE_PATHS 구성 변수를 이용하여 수행됩니다.

이 예에서, 우리는 많은 양의 데이터를 반환하는 작업이 있는데, 해당 작업은 GET http://target:7777/api/large_response_json입니다. 이것을 제외하기 위해 DAST_API_EXCLUDE_PATHS 구성 변수에 작업 URL의 경로 부분을 제공합니다 /api/large_response_json.

작업이 제외되었는지 확인하려면 API 보안 테스트 작업을 실행하고 작업 콘솔 출력을 검토하시면 됩니다. 테스트 결과의 끝에 포함된 작업 및 제외된 작업 디렉터리이 포함되어 있습니다.

dast_api:
  variables:
    DAST_API_EXCLUDE_PATHS: /api/large_response_json

테스트에서 작업을 제외하면 일부 취약점을 감지하지 못할 수 있습니다.

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

테스트를 여러 작업으로 분할하는 것은 DAST_API_EXCLUDE_PATHSDAST_API_EXCLUDE_URLS를 통해 API 보안 테스트에서 지원합니다. 테스트를 분할할 때, dast_api 작업을 비활성화하고 식별 가능한 이름을 가진 두 개의 작업으로 대체하는 것이 좋은 패턴입니다. 이 예에서는 각 작업이 API의 버전을 테스트하는 두 개의 작업이며, 이름이 해당 내용을 반영하고 있습니다. 그러나 이 기법은 API의 버전뿐만 아니라 어떤 상황에도 적용할 수 있습니다.

dast_api_v1dast_api_v2 작업에 사용하는 규칙은 API 보안 테스트 템플릿에서 복사한 것입니다.

# 메인 dast_api 작업 비활성화
dast_api:
  rules:
  - if: $CI_COMMIT_BRANCH
    when: never

dast_api_v1:
  extends: dast_api
  variables:
    DAST_API_EXCLUDE_PATHS: /api/v1/**
  rules:
  - if: $DAST_API_DISABLED == 'true' || $DAST_API_DISABLED == '1'
    when: never
  - if: $DAST_API_DISABLED_FOR_DEFAULT_BRANCH == 'true' &&
        $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
    when: never
  - if: $DAST_API_DISABLED_FOR_DEFAULT_BRANCH == '1' &&
        $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
    when: never
  - if: $CI_COMMIT_BRANCH &&
        $CI_GITLAB_FIPS_MODE == "true"
    variables:
      DAST_API_IMAGE_SUFFIX: "-fips"
  - if: $CI_COMMIT_BRANCH

dast_api_v2:
  variables:
    DAST_API_EXCLUDE_PATHS: /api/v2/**
  rules:
  - if: $DAST_API_DISABLED == 'true' || $DAST_API_DISABLED == '1'
    when: never
  - if: $DAST_API_DISABLED_FOR_DEFAULT_BRANCH == 'true' &&
        $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
    when: never
  - if: $DAST_API_DISABLED_FOR_DEFAULT_BRANCH == '1' &&
        $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
    when: never
  - if: $CI_COMMIT_BRANCH &&
        $CI_GITLAB_FIPS_MODE == "true"
    variables:
      DAST_API_IMAGE_SUFFIX: "-fips"
  - if: $CI_COMMIT_BRANCH

기능 브랜치에서의 작업 제외, 그러나 기본 브랜치에서는 제외하지 않기

하나 또는 두 번의 느린 작업의 경우, 팀은 작업을 건너뛰거나(테스트를 제외) 기능 브랜치 테스트에서 작업을 제외하되 기본 브랜치 테스트에는 포함하는 것으로 결정할 수 있습니다. 작업 제외는 DAST_API_EXCLUDE_PATHS 구성 변수를 사용하여 작업 URL의 경로 부분인 /api/large_response_json을 제공함으로써 수행됩니다.

이 예에서는 대량의 데이터를 반환하는 작업이 있습니다. 작업은 GET http://target:7777/api/large_response_json입니다. 이를 제외하기 위해 작업 URL의 경로 부분 /api/large_response_json과 함께 DAST_API_EXCLUDE_PATHS 구성 변수를 제공합니다. 우리의 구성은 주 dast_api 작업을 비활성화하고 새로운 dast_api_maindast_api_branch 두 작업을 생성합니다. dast_api_branch는 긴 작업을 제외하고 기본 브랜치(예: 기능 브랜치)에서만 실행되도록 설정됩니다. dast_api_main 브랜치는 기본 브랜치(main이 이 예시에서 사용되었습니다)에서만 실행되도록 설정됩니다. dast_api_branch 작업은 빠른 개발 주기를 가능하게 하기 위해 더 빨리 실행되고, 기본 브랜치 빌드에서만 실행되는 dast_api_main 작업은 더 오랜 시간이 소요됩니다.

작업이 제외되었는지 확인하려면 API 보안 테스트 작업을 실행하고 작업 콘솔 출력을 확인하십시오. 테스트의 끝에 포함된 작업 및 제외된 작업 디렉터리이 포함됩니다.

```yaml # 두 개의 이름이 다른 작업을 만들기 위해 메인 작업을 비활성화 dast_api: rules: - if: $CI_COMMIT_BRANCH when: never

기능 브랜치 작업을 위한 API 보안 테스트, /api/large_response_json를 제외

dast_api_branch: extends: dast_api variables: DAST_API_EXCLUDE_PATHS: /api/large_response_json rules: - if: $DAST_API_DISABLED == ‘true’ || $DAST_API_DISABLED == ‘1’ when: never - if: $DAST_API_DISABLED_FOR_DEFAULT_BRANCH == ‘true’ && $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME when: never - if: $DAST_API_DISABLED_FOR_DEFAULT_BRANCH == ‘1’ && $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME when: never - if: $CI_COMMIT_BRANCH && $CI_GITLAB_FIPS_MODE == “true” variables: DAST_API_IMAGE_SUFFIX: “-fips” - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH when: never - if: $CI_COMMIT_BRANCH

기본 브랜치 (여기서는 main)를 위한 API 보안 테스트

긴 실행 작업 포함

dast_api_main: extends: dast_api rules: - if: $DAST_API_DISABLED == ‘true’ || $DAST_API_DISABLED == ‘1’ when: never - if: $DAST_API_DISABLED_FOR_DEFAULT_BRANCH == ‘true’ && $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME when: never - if: $DAST_API_DISABLED_FOR_DEFAULT_BRANCH == ‘1’ && $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME when: never - if: $CI_COMMIT_BRANCH && $CI_GITLAB_FIPS_MODE == “true” variables: DAST_API_IMAGE_SUFFIX: “-fips” - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH