Security Command Center API를 사용하여 보안 발견 항목 나열

Security Command Center 발견 항목은 프로젝트나 조직에 있는 애셋의 잠재적 보안 위험을 모델링합니다. 발견 항목은 항상 Security Command Center의 특정 애셋과 관련이 있습니다.

이 가이드에서는 Security Command Center 클라이언트 라이브러리를 사용하여 발견 항목에 액세스하는 방법을 설명합니다. 각 발견 항목은 소스에 속합니다. 대부분의 감지기나 발견 항목 제공업체는 동일한 소스 내에서 발견 항목을 생성합니다.

Security Command Center의 IAM 역할은 조직, 폴더, 프로젝트 수준에서 부여할 수 있습니다. 발견 항목, 애셋, 보안 소스를 보거나 수정하거나 만들거나 업데이트할 수 있는 기능은 액세스 권한이 부여된 수준에 따라 다릅니다. Security Command Center 역할에 대해 자세히 알아보려면 액세스 제어를 참조하세요.

시작하기 전에

소스를 설정하기 전에 다음을 완료해야 합니다.

페이지 크기

모든 Security Command Center 목록 API는 페이지로 나뉩니다. 각 응답은 결과 페이지와 다음 페이지를 반환하는 토큰을 반환합니다. 페이지 크기는 구성 가능합니다. 기본 페이지 크기는 10입니다. 최소 1에서 최대 1,000으로 설정할 수 있습니다.

발견 항목 보관

최소 13개월 동안 발견 항목을 나열하거나 쿼리할 수 있습니다.

Security Command Center는 각 발견 항목의 스냅샷을 저장합니다. 발견 항목 스냅샷은 최소 13개월 동안 보관됩니다. 발견 항목의 모든 스냅샷이 삭제되면 더 이상 발견 항목을 나열하거나 복구할 수 없습니다.

Security Command Center 데이터 보관에 대한 자세한 내용은 데이터 보관을 참조하세요.

모든 발견 항목 나열

gcloud

프로젝트, 폴더 또는 조직의 모든 발견 항목을 나열하려면 다음 명령어를 실행합니다.

gcloud scc findings list PARENT_ID

PARENT_ID를 다음 값 중 하나로 바꿉니다.

  • ORGANIZATION_ID 형식의 조직 ID(숫자 ID만 해당)
  • folders/FOLDER_ID 형식의 폴더 ID
  • projects/PROJECT_ID 형식의 프로젝트 ID

더 많은 예시를 보려면 다음을 실행하세요.

gcloud scc findings list --help

문서의 예시는 gcloud scc findings list를 참조하세요.

Python

from google.cloud import securitycenter

# Create a client.
client = securitycenter.SecurityCenterClient()

# 'parent' must be in one of the following formats:
#   "organizations/{organization_id}"
#   "projects/{project_id}"
#   "folders/{folder_id}"
parent = f"organizations/{organization_id}"
# The "sources/-" suffix lists findings across all sources.  You
# also use a specific source_name instead.
all_sources = f"{parent}/sources/-"
finding_result_iterator = client.list_findings(request={"parent": all_sources})
for i, finding_result in enumerate(finding_result_iterator):
    print(
        "{}: name: {} resource: {}".format(
            i, finding_result.finding.name, finding_result.finding.resource_name
        )
    )

자바

static ImmutableList<ListFindingsResult> listAllFindings(OrganizationName organizationName) {
  try (SecurityCenterClient client = SecurityCenterClient.create()) {
    // Input parameters for SourceName must be in one of the following formats:
    //    * OrganizationName organizationName = OrganizationName.of("organization-id");
    //      organizationName.getOrganization();
    //    * ProjectName projectName = ProjectName.of("project-id");
    //      projectName.getProject();
    //    * FolderName folderName = FolderName.of("folder-id");
    //      folderName.getFolder();
    //
    // "-" Indicates listing across all sources.
    SourceName sourceName = SourceName.of(organizationName.getOrganization(), "-");

    ListFindingsRequest.Builder request =
        ListFindingsRequest.newBuilder().setParent(sourceName.toString());

    // Call the API.
    ListFindingsPagedResponse response = client.listFindings(request.build());

    // This creates one list for all findings.  If your organization has a large number of
    // findings this can cause out of memory issues.  You can process them in incrementally
    // by returning the Iterable returned response.iterateAll() directly.
    ImmutableList<ListFindingsResult> results = ImmutableList.copyOf(response.iterateAll());
    System.out.println("Findings:");
    System.out.println(results);
    return results;
  } catch (IOException e) {
    throw new RuntimeException("Couldn't create client.", e);
  }
}

Go

import (
	"context"
	"fmt"
	"io"

	securitycenter "cloud.google.com/go/securitycenter/apiv1"
	"cloud.google.com/go/securitycenter/apiv1/securitycenterpb"
	"google.golang.org/api/iterator"
)

// listFindings prints all findings in orgID to w. orgID is the numeric
// identifier of the organization.
func listFindings(w io.Writer, orgID string) error {
	// orgID := "12321311"
	// Instantiate a context and a security service client to make API calls.
	ctx := context.Background()
	client, err := securitycenter.NewClient(ctx)
	if err != nil {
		return fmt.Errorf("securitycenter.NewClient: %w", err)
	}
	defer client.Close() // Closing the client safely cleans up background resources.

	req := &securitycenterpb.ListFindingsRequest{
		// List findings across all sources.
		// Parent must be in one of the following formats:
		//		"organizations/{orgId}/sources/-"
		//		"projects/{projectId}/sources/-"
		//		"folders/{folderId}/sources/-"
		Parent: fmt.Sprintf("organizations/%s/sources/-", orgID),
	}
	it := client.ListFindings(ctx, req)
	for {
		result, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return fmt.Errorf("it.Next: %w", err)
		}
		finding := result.Finding
		fmt.Fprintf(w, "Finding Name: %s, ", finding.Name)
		fmt.Fprintf(w, "Resource Name %s, ", finding.ResourceName)
		fmt.Fprintf(w, "Category: %s\n", finding.Category)
	}
	return nil
}

Node.js

// Imports the Google Cloud client library.
const {SecurityCenterClient} = require('@google-cloud/security-center');

// Creates a new client.
const client = new SecurityCenterClient();
//  organizationId is the numeric ID of the organization.
/*
 * TODO(developer): Uncomment the following lines
 */
// const organizationId = "1234567777";

async function listAllFindings() {
  const [response] = await client.listFindings({
    // List findings across all sources.
    // parent: must be in one of the following formats:
    //    `organizations/${organization_id}/sources/-`
    //    `projects/${project_id}/sources/-`
    //    `folders/${folder_id}/sources/-`
    parent: `organizations/${organizationId}/sources/-`,
  });
  let count = 0;
  Array.from(response).forEach(result =>
    console.log(
      `${++count} ${result.finding.name} ${result.finding.resourceName}`
    )
  );
}
await listAllFindings();

각 발견 항목의 출력은 다음과 비슷합니다.

"finding": {
    "name": "organizations/ORGANIZATION_ID/sources/SOURCE_ID/findings/FINDING_ID",
    "parent": "organizations/ORGANIZATION_ID/sources/SOURCE_ID",
    "resourceName": "//cloudresourcemanager.googleapis.com/projects/PROJECT_NUMBER",
    "state": "ACTIVE",
    "category": "Malware: Cryptomining Bad Domain",
    "sourceProperties": {
      "sourceId": {
        "projectNumber": "PROJECT_NUMBER",
        "customerOrganizationNumber": "ORGANIZATION_ID"
      },
      "detectionCategory": {
        "technique": "cryptomining",
        "indicator": "domain",
        "ruleName": "bad_domain",
        "subRuleName": "cryptomining"
      },
      "detectionPriority": "LOW",
      "affectedResources": [{
        "gcpResourceName": "//cloudresourcemanager.googleapis.com/projects/PROJECT_NUMBER"
      }],
      "evidence": [{
        "sourceLogId": {
          "projectId": "PROJECT_ID",
          "resourceContainer": "projects/PROJECT_ID",
          "timestamp": {
            "seconds": "1636566099",
            "nanos": 5.41483849E8
          },
          "insertId": "INSERT_ID"
        }
      }],
      "properties": {
        "domains": ["DOMAIN"],
        "instanceDetails": "/projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_ID",
        "network": {
          "project": "PROJECT_ID",
          "location": "ZONE"
        },
        "dnsContexts": [{
          "authAnswer": true,
          "sourceIp": "SOURCE_IP_ADDRESS",
          "queryName": "DOMAIN",
          "queryType": "A",
          "responseCode": "NXDOMAIN"
        }],
        "vpc": {
          "vpcName": "default"
        }
      },
      "findingId": "FINDING_ID",
      "contextUris": {
        "mitreUri": {
          "displayName": "MITRE Link",
          "url": "https://attack.mitre.org/techniques/T1496/"
        },
        "virustotalIndicatorQueryUri": [{
          "displayName": "VirusTotal Domain Link",
          "url": "https://www.virustotal.com/gui/domain/DOMAIN/detection"
        }],
        "cloudLoggingQueryUri": [{
          "displayName": "Cloud Logging Query Link",
          "url": "https://console.cloud.google.com/logs/query;query\u003dtimestamp%3D%222021-11-10T17:41:39.541483849Z%22%0AinsertId%3D%22INSERT_ID%22%0Aresource.labels.project_id%3D%22PROJECT_ID%22?project\u003dPROJECT_ID"
        }],
        "relatedFindingUri": {
        }
      }
    },
    "securityMarks": {
      "name": "organizations/ORGANIZATION_ID/sources/SOURCE_ID/findings/FINDING_ID/securityMarks"
    },
    "eventTime": "2021-11-10T17:41:41.594Z",
    "createTime": "2021-11-10T17:41:42.014Z",
    "severity": "LOW",
    "workflowState": "NEW",
    "canonicalName": "projects/PROJECT_NUMBER/sources/SOURCE_ID/findings/FINDING_ID",
    "mute": "UNDEFINED",
    "findingClass": "THREAT",
    "indicator": {
      "domains": ["DOMAIN"]
    }
  },
  "resource": {
    "name": "//cloudresourcemanager.googleapis.com/projects/PROJECT_NUMBER",
    "projectName": "//cloudresourcemanager.googleapis.com/projects/PROJECT_NUMBER",
    "projectDisplayName": "PROJECT_ID",
    "parentName": "//cloudresourcemanager.googleapis.com/organizations/ORGANIZATION_ID",
    "parentDisplayName": "PARENT_NAME",
    "type": "google.cloud.resourcemanager.Project",
    "displayName": "PROJECT_ID"
  }

발견 항목 필터링

프로젝트, 폴더 또는 조직에는 많은 발견 항목이 있을 수 있습니다. 앞의 예시에서는 필터를 사용하지 않으므로 모든 발견 항목 레코드가 반환됩니다. Security Command Center를 사용 설정하면 발견 항목 필터를 사용하여 원하는 발견 항목에 대한 정보만 가져올 수 있습니다.

발견 항목 필터는 SQL 문의 'where' 절과 같지만 열 대신 API에서 반환한 객체를 적용합니다.

다음 예시에서는 'MEDIUM_RISK_ONE' 카테고리가 있는 발견 항목만 나열합니다. 발견 항목 제공업체(보안 소스라고도 함)마다 서로 다른 카테고리 집합을 사용합니다. 필터에서 사용할 수 있는 카테고리를 확인하려면 발견 항목 제공업체의 문서를 참조하세요.

gcloud

다음 명령어를 사용하여 발견 항목을 필터링합니다.

gcloud scc findings list PARENT_ID --source=SOURCE_ID --filter="FILTER"

다음을 바꿉니다.

  • FILTER를 사용해야 하는 필터로 바꿉니다. 예를 들어 다음 필터는 MEDIUM_RISK_ONE 카테고리의 발견 항목만 반환합니다.
    --filter="category=\"MEDIUM_RISK_ONE\""
  • PARENT_ID를 다음 값 중 하나로 바꿉니다.
    • ORGANIZATION_ID 형식의 조직 ID(숫자 ID만 해당)
    • projects/PROJECT_ID 형식의 프로젝트 ID
    • folders/FOLDER_ID 형식의 폴더 ID
  • SOURCE_ID를 발견 항목 유형을 제공하는 보안 소스의 ID로 바꿉니다.

더 많은 예시를 보려면 다음을 실행하세요.

gcloud scc findings list --help

문서의 예시는 gcloud scc findings list를 참조하세요.

Python

from google.cloud import securitycenter

# Create a new client.
client = securitycenter.SecurityCenterClient()

# 'source_name' is the resource path for a source that has been
# created previously (you can use list_sources to find a specific one).
# Its format is:
# source_name = f"{parent}/sources/{source_id}"
# 'parent' must be in one of the following formats:
#   "organizations/{organization_id}"
#   "projects/{project_id}"
#   "folders/{folder_id}"
# You an also use a wild-card "-" for all sources:
#   source_name = "organizations/111122222444/sources/-"
finding_result_iterator = client.list_findings(
    request={"parent": source_name, "filter": 'category="MEDIUM_RISK_ONE"'}
)
# Iterate an print all finding names and the resource they are
# in reference to.
for i, finding_result in enumerate(finding_result_iterator):
    print(
        "{}: name: {} resource: {}".format(
            i, finding_result.finding.name, finding_result.finding.resource_name
        )
    )

자바

static ImmutableList<ListFindingsResult> listFilteredFindings(SourceName sourceName) {
  try (SecurityCenterClient client = SecurityCenterClient.create()) {
    // parentId: must be one of the following:
    //    "organization-id"
    //    "project-id"
    //    "folder-id"
    // SourceName sourceName = SourceName.of(parentId, sourceId);

    // Create filter to category of MEDIUM_RISK_ONE
    String filter = "category=\"MEDIUM_RISK_ONE\"";

    ListFindingsRequest.Builder request =
        ListFindingsRequest.newBuilder().setParent(sourceName.toString()).setFilter(filter);

    // Call the API.
    ListFindingsPagedResponse response = client.listFindings(request.build());

    // This creates one list for all findings.  If your organization has a large number of
    // findings this can cause out of memory issues.  You can process them in incrementally
    // by returning the Iterable returned response.iterateAll() directly.
    ImmutableList<ListFindingsResult> results = ImmutableList.copyOf(response.iterateAll());
    System.out.println("Findings:");
    System.out.println(results);
    return results;
  } catch (IOException e) {
    throw new RuntimeException("Couldn't create client.", e);
  }
}

Go

import (
	"context"
	"fmt"
	"io"

	securitycenter "cloud.google.com/go/securitycenter/apiv1"
	"cloud.google.com/go/securitycenter/apiv1/securitycenterpb"
	"google.golang.org/api/iterator"
)

// listFilteredFindings prints findings with category 'MEDIUM_RISK_ONE' for a
// specific source to w. sourceName is the full resource name of the source
// to search for findings under.
func listFilteredFindings(w io.Writer, sourceName string) error {
	// Specific source:
	// 		sourceName := "{parent}/sources/{sourceId}"
	// All sources:
	// 		sourceName := "{parent}/sources/-"
	// where,
	// Parent must be in one of the following formats:
	//		"organizations/{orgId}"
	//		"projects/{projectId}"
	//		"folders/{folderId}"
	// Instantiate a context and a security service client to make API calls.
	ctx := context.Background()
	client, err := securitycenter.NewClient(ctx)
	if err != nil {
		return fmt.Errorf("securitycenter.NewClient: %w", err)
	}
	defer client.Close() // Closing the client safely cleans up background resources.

	req := &securitycenterpb.ListFindingsRequest{
		Parent: sourceName,
		Filter: `category="MEDIUM_RISK_ONE"`,
	}
	it := client.ListFindings(ctx, req)
	for {
		result, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return fmt.Errorf("it.Next: %w", err)
		}
		finding := result.Finding
		fmt.Fprintf(w, "Finding Name: %s, ", finding.Name)
		fmt.Fprintf(w, "Resource Name %s, ", finding.ResourceName)
		fmt.Fprintf(w, "Category: %s\n", finding.Category)
	}
	return nil
}

Node.js

// Imports the Google Cloud client library.
const {SecurityCenterClient} = require('@google-cloud/security-center');

// Creates a new client.
const client = new SecurityCenterClient();
//  sourceName is the full resource path of the source to search for
//  findings.
/*
 * TODO(developer): Uncomment the following lines
 */
// const sourceName = `${parent}/sources/${sourceId}`;
// where,
// parent: must be in one of the following formats:
//    `organizations/${organization_id}`
//    `projects/${project_id}`
//    `folders/${folder_id}`
async function listFilteredFindings() {
  const [response] = await client.listFindings({
    // List findings across all sources.
    parent: sourceName,
    filter: 'category="MEDIUM_RISK_ONE"',
  });
  let count = 0;
  Array.from(response).forEach(result =>
    console.log(
      `${++count} ${result.finding.name} ${result.finding.resourceName}`
    )
  );
}
listFilteredFindings();

또한 Security Command Center는 전체 JSON 배열 및 객체를 잠재적인 속성 유형으로 지원합니다. 다음을 기준으로 필터링할 수 있습니다.

  • 배열 요소
  • 객체 내에서 부분 문자열이 일치하는 전체 JSON 객체
  • JSON 객체 하위 필드

지원되는 연산자

Security Command Center 발견 항목의 쿼리 문은 대부분의 Google Cloud API에서 지원하는 연산자를 지원합니다.

다음 목록은 다양한 연산자의 사용을 보여줍니다.

  • state="ACTIVE" AND NOT mute="MUTED"
  • create_time>"2023-08-15T19:05:32.428Z"
  • resource.parent_name:"prod"
  • severity="CRITICAL" OR severity="HIGH"

다음 목록은 발견 항목의 쿼리 문에서 지원되는 모든 연산자와 함수를 보여줍니다.

  • 문자열:
    • 전체 같음: =
    • 부분 문자열 일치: :
  • 숫자:
    • 같지 않음: <, >, <=, >=
    • 같음: =, !=
  • 불리언:
    • 같음: =
  • 논리적 관계:
    • AND
    • OR
    • NOT 또는 -
  • 그룹화 표현식:
    • (, )(괄호)
  • 배열:
    • contains() - 지정된 필터와 일치하는 요소가 최소 하나 이상 포함된 배열 필드가 있는 발견 항목을 쿼리하는 함수입니다.
    • containsOnly() - 지정된 필터와 일치하는 요소만 포함된 배열 필드가 있는 발견 항목을 쿼리하는 함수입니다.
  • IP 주소:
    • inIpRange() - 지정된 CIDR 범위 내의 IP 주소를 쿼리하는 함수입니다.

IP 주소 필터링

특정 발견 항목 속성에는 IP 주소가 포함됩니다. 특정 IP 주소나 IP 주소 범위를 기준으로 발견 항목을 필터링할 수 있습니다.

IP 주소는 다음을 포함한 다양한 발견 항목 및 발견 항목 속성에서 문자열로 표시됩니다.

  • access.caller_ip
  • connections.destinationIp
  • connections.sourceIp
  • indicator.ip_addresses

특정 IP 주소를 필터링하려면 다음 예시와 같이 등호 연산자를 사용하면 됩니다.

access.caller_ip="192.0.2.0"

IP 주소 범위를 기준으로 발견 항목을 필터링하려면 inIpRange 함수를 사용합니다. inIpRange 함수를 사용하면 지정된 CIDR 범위 내의 IP 주소가 포함된 발견 항목으로만 발견 항목을 필터링할 수 있습니다. inIpRange와 함께 NOT을 사용하면 지정된 CIDR 범위 밖에 있는 IP 주소가 포함된 발견 항목으로만 발견 항목을 필터링할 수 있습니다.

다음 예시에서는 inIpRange 함수의 구문을 보여줍니다.

inIpRange(IP_FINDING_FIELD, "CIDR_RANGE")

IP 주소가 배열이 포함된 발견 항목 필드의 배열 요소에 있으면 contains 함수 및 inIpRange 함수와 함께 다음 구문을 사용합니다.

contains(ATTRIBUTE_WITH_ARRAY, inIpRange(IP_FINDING_FIELD, "CIDR_RANGE"))

다음 예시에서 inIpRange 함수는 192.0.2.0/24로 정의된 CIDR 범위에 있는 IP 주소의 connections 발견 항목 필드에 포함된 배열의 각 destination_ip 요소를 평가합니다.

contains(connections, inIpRange(destination_ip, "192.0.2.0/24"))

다음 예시에서는 inIpRange 함수를 사용하여 다른 범위가 아닌 한 범위 내에 있는 IP 주소가 connections.source_ip 필드에 있는 발견 항목을 필터링하는 gcloud CLI 명령어를 보여줍니다. connections 필드는 배열 유형 필드이므로 contains 함수가 사용됩니다.

  gcloud scc findings list example-organization.com \
    --source=123456789012345678 \
    --filter="contains(connections, inIpRange(source_ip, "2001:db8::/32")) \
      AND NOT contains(connections, inIpRange(source_ip, "192.0.2.0/24"))

JSON 객체 예시

이 페이지 뒷부분에 예시에서는 다음 JSON 객체가 발견 항목 속성이라고 가정합니다.

{
  "outer_object": {
    "middle_object": {
      "deeply_nested_object": {
        "x": 123
      },
      "y": "some-string-value"
    },
    "list_middle_object": [
      {
        "v": 321,
        "w": [
          {
            "a": 3,
            "b": 4
          }
        ]
      }
    ],
    "z": "some-other-string-value",
    "u": [
      "list-element-1",
      "list-element-2",
      "list-element-3"
    ]
  }
}

발견 항목 필터링 예시

이전 JSON 예시my_property라는 발견 항목 속성이라고 가정합니다. 다음 예시에는 객체를 속성으로 가진 발견 항목에 대한 쿼리가 포함되어 있습니다. 쿼리에서 ANDOR를 사용하여 이러한 필터를 다른 필터와 함께 사용할 수도 있습니다.

# ORGANIZATION_ID=organization-id
# SOURCE_ID="source-id"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="my_property.outer_object.middle_object.deeply_nested_object.x = 123"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="my_property.outer_object.middle_object.y = \"some-string-value\""

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="my_property.outer_object.middle_object.y : \"string-value\""

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="my_property.outer_object.z = \"some-other-string-value\""

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="my_property.outer_object.z : \"other-string-value\""

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="my_property.outer_object.u : \"list-element-1\""

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="my_property.outer_object.u : \"list-element-2\""

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="my_property.outer_object.u : \"list-element-3\""

배열 유형 필드를 기준으로 필터링

ListFindings를 호출할 때 배열의 전체 콘텐츠에서 부분 문자열 일치를 한 번 확인하는 하위 문자열 일치 :를 사용할 수 있습니다. 또는 다음 함수 중 하나를 사용하여 배열의 요소와 하위 필드에 직접 하위 필터를 실행할 수 있습니다.

  • 배열의 요소에 지정된 값이 포함되면 발견 항목을 반환하는 contains() 함수

  • 배열의 모든 요소가 하위 필터와 일치하는 경우에만 발견 항목을 반환하는 containsOnly() 함수

두 함수 모두 다음과 같은 하위 필터 쿼리 기능을 지원합니다.

  • 정확한 요소 일치: 정확한 문자열 "example"이 포함된 배열 요소를 일치시킵니다.
  • 특정 숫자 연산: 100보다 크거나 같은 배열 요소를 일치시킵니다.
  • 배열 구조에 대한 복잡한 필터링: x 속성이 포함된 배열 요소를 해당 값 y와 일치시킵니다.

contains() 함수 형식

contains() 함수 형식은 다음과 같습니다.

contains(ARRAY_ATTRIBUTE_NAME, SUBFILTER)

다음을 바꿉니다.

  • ARRAY_ATTRIBUTE_NAME: 유형 배열(목록)인 필드나 하위 필드입니다.
  • SUBFILTER: 배열에서 찾을 값을 정의하는 표현식입니다. 하위 필터 형식은 ARRAY_ATTRIBUTE_NAME객체 배열인지 또는 기본 유형 요소의 배열인지 여부에 따라 달라집니다. ARRAY_ATTRIBUTE_NAME가 중첩된 배열이 있는 객체의 배열인 경우 범위가 지정된 하위 필터를 사용하여 모든 조건이 동일한 ARRAY_ATTRIBUTE_NAME 요소 내에서 충족되도록 지정할 수 있습니다.

Security Command Center API는 ARRAY_ATTRIBUTE_NAMESUBFILTER를 충족하는 요소가 최소 하나 이상 포함된 발견 항목을 반환합니다.

containsOnly() 함수 형식

containsOnly() 함수 형식은 다음과 같습니다.

containsOnly(ARRAY_ATTRIBUTE_NAME, SUBFILTER)

다음을 바꿉니다.

  • ARRAY_ATTRIBUTE_NAME: 유형 배열(목록)인 필드나 하위 필드입니다. Security Command Center API를 사용하여 쿼리를 실행할 때는 사용 가능한 배열 속성에 containsOnly() 함수를 사용할 수 있습니다.

  • SUBFILTER: 배열에서 찾을 값을 정의하는 표현식입니다. 하위 필터 형식은 ARRAY_ATTRIBUTE_NAME객체 배열인지 또는 기본 유형 요소의 배열인지 여부에 따라 달라집니다. ARRAY_ATTRIBUTE_NAME이 중첩된 배열이 있는 객체의 배열인 경우 범위가 지정된 하위 필터를 사용하여 모든 조건이 동일한 ARRAY_ATTRIBUTE_NAME 요소 내에서 충족되도록 지정할 수 있습니다.

Security Command Center API는 모든 ARRAY_ATTRIBUTE_NAME 요소가 SUBFILTER와 일치하는 발견 항목을 반환합니다.

객체 배열의 하위 필터

다음은 이전 JSON 예시의 일부입니다. 여기서 list_middle_object 필드는 객체 배열입니다.

    "list_middle_object": [
      {
        "v": 321,
        "w": [
          {
            "a": 3,
            "b": 4
          }
        ]
      }
    ]

다음 예시에서는 list_middle_object 필드의 요소 중 최소 하나 이상에 값이 321보다 크거나 같은 v 하위 필드가 있는 발견 항목을 쿼리합니다.

# ORGANIZATION_ID=organization-id
# SOURCE_ID="source-id"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="contains(my_property.outer_object.list_middle_object, v  >= 321)"

contains()containsOnly() 함수를 사용하는 실제 예시는 특정 배열 값이 포함된 발견 항목을 참조하세요.

기본 유형 요소가 포함된 배열의 하위 필터

기본 유형은 문자열, 숫자, 부울입니다. 기본 유형이 포함된 배열에 contains() 함수를 사용하려면 특수 키워드 elem을 사용합니다.

다음은 이전 JSON 예시의 일부입니다. 여기서 u 필드는 기본 유형 요소의 배열입니다.

"u": ["list-element-1", "list-element-2", "list-element-3"]

다음 예시에서는 u 필드의 요소 중 최소 하나 이상이 'list-element-1'인 발견 항목을 쿼리합니다.

# ORGANIZATION_ID=organization-id
# SOURCE_ID="source-id"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="contains(my_property.outer_object.u, elem = \"list-element-1\")"

contains() 함수를 사용하는 실제 예시는 특정 배열 값이 포함된 발견 항목을 참조하세요.

범위가 지정된 하위 필터

다음은 이전 JSON 예시의 일부입니다. 여기서 list_middle_object 필드는 객체의 배열이며 이 배열의 객체에는 중첩된 배열이 포함됩니다.

    "list_middle_object": [
      {
        "v": 321,
        "w": [
          {
            "a": 3,
            "b": 4
          }
        ]
      }
    ]

다음 예시에서는 동일한 list_middle_object 요소 내에서 다음 두 조건이 모두 충족하는 발견 항목을 쿼리합니다.

  • v 하위 필드 값은 321보다 크거나 같습니다.
  • w 하위 필드에는 a 속성이 3인 요소가 포함되지 않습니다.
# ORGANIZATION_ID=organization-id
# SOURCE_ID="source-id"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --filter="contains(my_property.outer_object.list_middle_object, v  >= 321 AND -contains(w, a = 3))"

contains() 함수를 사용하는 실제 예시는 특정 배열 값이 포함된 발견 항목을 참조하세요.

발견 항목 정렬 예시

기본 유형인 엄격한 하위 필드(문자열, 숫자, 부울)로 발견 항목을 정렬할 수 있습니다. 이전 JSON 예시my_property라는 발견 항목 속성이라고 가정합니다. 다음 예시에는 발견 항목 필드를 정렬하는 쿼리가 포함되어 있습니다. DESC 키워드는 이후 필드가 내림차순으로 정렬되도록 지정합니다. 기본 순서는 오름차순입니다.

# ORGANIZATION_ID=organization-id
# SOURCE_ID="source-id"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --order-by="my_property.outer_object.middle_object.deeply_nested_object.x DESC"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --order-by="my_property.outer_object.middle_object.deeply_nested_object.x"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --order-by="my_property.outer_object.middle_object.y DESC"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --order-by="my_property.outer_object.middle_object.y"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --order-by="my_property.outer_object.z DESC"

gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID \
  --order-by="my_property.outer_object.z"

필터 예시

다음 섹션에서는 발견 항목 필터의 실제 예시를 보여줍니다.

특정 시점 이후에 발생한 발견 항목을 기준으로 필터링

이러한 필터 예시는 2019년 6월 5일 수요일 오후 10:12:05 GMT 이후 가장 최근에 발생한 결과와 일치합니다. event_time 필터를 사용하면 다음 형식과 유형을 사용하여 시간을 표현할 수 있습니다.

  • 정수 리터럴로서의 Unix epoch 시간(밀리초)

    "event_time > 1559772725000"
    
  • 문자열 리터럴로서의 RFC 3339

    "event_time > \"2019-06-05T22:34:40+00:00\""
    

배열 유형 필드를 기준으로 필터링

다음 예시에서는 필터 내 배열 유형 필드에서 부분 문자열 일치를 사용하는 방법을 보여줍니다.

"indicator.domains : \"website.com\""

Security Command Center API는 배열 내에 website.com 부분 문자열이 있는 모든 발견 항목을 반환합니다. 예를 들어 'website.com'이 배열 요소의 하위 문자열이므로 발견 항목을 indicator.domains = [\"onewebsite.com\"]과 일치시킵니다.

다음 섹션의 예시 필터에서는 contains() 함수를 사용하여 다양한 배열 유형 필터링을 사용하기 위한 몇 가지 옵션을 보여줍니다.

vulnerability.cve.references 필드를 기준으로 필터링

다음 예시에서는 vulnerability.cve.references 배열의 요소 중 최소 하나 이상에서 SOURCE_OF_REFERENCE와 동일한 source 속성과 FILTERED_URI가 있는 uri 속성이 모두 있는 발견 항목을 반환합니다.

"contains(vulnerability.cve.references, source = \"SOURCE_OF_REFERENCE\" AND uri : \"FILTERED_URI\")"

다음을 바꿉니다.

indicator.domains 필드를 기준으로 필터링

다음 예시에서는 표시기 도메인 최소 하나 이상에서 mycompanyprefix.ca가 모두 있는 발견 항목을 반환합니다.

"contains(indicator.domains, elem : \"mycompanyprefix\" AND elem : \".ca\")"

indicator.ip_addresses 필드를 기준으로 필터링

다음 예시에서는 indicator.ip_addresses 배열의 요소 중 최소 하나 이상이 IP_ADDRESS과 동일한 발견 항목을 반환합니다.

"contains(indicator.ip_addresses, elem = \"IP_ADDRESS\")"

IP_ADDRESS를 검색중인 발견 항목과 연결된 IP 주소로 바꿉니다.

외부 시스템 담당자를 기준으로 필터링

다음 예시에서는 external_systems.EXTERNAL_SYSTEM_NAME.assignees 배열의 요소 중 최소 하나 이상이 ASSIGNEE과 동일한 발견 항목을 반환합니다.

"contains(external_systems.EXTERNAL_SYSTEM_NAME.assignees, elem = \"ASSIGNEE\")"

다음을 바꿉니다.

  • EXTERNAL_SYSTEM_NAME: 타사 SIEM/SOAR 시스템 이름입니다(예: demisto).
  • ASSIGNEE: 외부 시스템의 담당자입니다.

resource.folders.resource_folder 필드를 기준으로 필터링

다음 예시에서는 resource.folders.resource_folder 배열의 요소 중 최소 하나 이상이 FOLDER_NAME과 같지 않은 발견 항목을 반환합니다.

"contains(resource.folders.resource_folder, -(elem = \"FOLDER_NAME\"))"

resource.folders.resource_folder_display_name 필드를 기준으로 필터링

다음 예시에서는 resource.folders.resource_folder_display_name 배열의 요소 중 최소 하나 이상이 DISPLAY_NAME과 동일한 발견 항목을 반환합니다.

"contains(resource.folders.resource_folder_display_name, elem = \"DISPLAY_NAME\")"

DISPLAY_NAME를 검색 중인 발견 항목과 연결된 폴더의 사용자 정의 이름으로 바꿉니다.

필터에 특정 서비스 계정만 포함

다음 예시는 모든 iam_bindings 항목의 구성원 값이 제공된 서비스 계정 중 하나와 동일한 경우에만 발견 항목을 반환합니다.

containsOnly(iam_bindings, (member = SERVICE_ACCOUNT1 OR member = SERVICE_ACCOUNT2 OR member = "SERVICE_ACCOUNT3 "))

SERVICE_ACCOUNT1, SERVICE_ACCOUNT2, SERVICE_ACCOUNT3을 서비스 계정의 이메일 주소로 바꿉니다.

발견 항목 필터에서 contains()containsOnly() 함수를 사용하는 방법은 배열 유형 필드를 기준으로 필터링을 참조하세요.

다음 단계

발견 항목 알림 설정 자세히 알아보기