Security Command Center API を使用してセキュリティの検出結果を一覧表示する

Security Command Center の検出結果により、プロジェクトまたは組織のアセットの潜在的なセキュリティ リスクがモデル化されます。検出結果は、常に Security Command Center の特定のアセットに関連付けられます。

このガイドでは、Security Command Center クライアント ライブラリを使用して検出結果にアクセスする方法について説明します。各検出結果は 1 つのソースに属します。ほとんどの検出器や検出プロバイダで、同じソースの検出結果が生成されます。

Security Command Center の IAM ロールは、組織レベル、フォルダレベル、またはプロジェクト レベルで付与できます。検出結果、アセット、セキュリティ ソースを表示、編集、作成、更新する権限は、アクセス権が付与されているレベルによって異なります。Security Command Center のロールの詳細については、アクセス制御をご覧ください。

始める前に

ソースを設定する前に、次のことが完了している必要があります。

ページサイズ

Security Command Center の list API は、すべてページ分けされます。各レスポンスでは、結果のページと次のページを返すためのトークンが戻されます。ページサイズは構成可能です。デフォルトのページサイズは 10 です。最小値は 1、最大値は 1, 000 に設定できます。

検出結果の保持

検出結果は、少なくとも 13 か月間、一覧表示またはクエリを行うことができます。

Security Command Center には、各検出結果のスナップショットが保存されます。検出結果のスナップショットは少なくとも 13 か月間保持されます。検出結果のスナップショットがすべて削除されると、検出結果の一覧表示や復元ができなくなります。

Security Command Center のデータ保持の詳細については、データの保持をご覧ください。

すべての検出結果を一覧表示する

gcloud

プロジェクト、フォルダ、または組織内のすべての検出結果を一覧表示するには、次のコマンドを実行します。

gcloud scc findings list PARENT_ID

PARENT_ID は次のいずれかの値に置き換えます。

  • 次の形式の組織 ID: ORGANIZATION_ID(数値 ID のみ)
  • 次の形式のフォルダ ID: folders/FOLDER_ID
  • 次の形式のプロジェクト ID: projects/PROJECT_ID

他の例については、次のコマンドを実行します。

gcloud scc findings list --help

ドキュメントの例については、gcloud scc 検出結果リストをご覧ください。

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
        )
    )

Java

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}`
    )
  );
}
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 は次のいずれかの値に置き換えます。
    • 次の形式の組織 ID: ORGANIZATION_ID(数値 ID のみ)
    • 次の形式のプロジェクト ID: projects/PROJECT_ID
    • 次の形式のフォルダ ID: folders/FOLDER_ID
  • SOURCE_ID は、検出結果のタイプを示すセキュリティ ソースの ID に置き換えます。

他の例については、次のコマンドを実行します。

gcloud scc findings list --help

ドキュメントの例については、gcloud scc 検出結果リストをご覧ください。

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
        )
    )

Java

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 APIs でサポートされている演算子をサポートしています。

以下に、演算子の使用例を示します。

  • 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(): 指定したフィルタに一致する要素を 1 つ以上含む配列フィールドを使用して、検出結果をクエリする関数
    • 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 アドレスを含む検出結果だけに絞り込みます。inIpRangeNOT 演算子を使用すると、指定した 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 関数を使用して、connections.source_ip フィールドの IP アドレスが 1 つの範囲内にあり、別の範囲内にはない検出結果をフィルタリングする 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 を呼び出す際に部分文字列一致 : を使用できます。この照合では、配列のコンテンツ全体に対して部分的に一致する文字列が 1 回チェックされます。または、次のいずれかの関数を使用して、配列とそのサブフィールドの要素に対してサブフィルタを直接実行することもできます。

  • 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 を満たす要素が少なくとも 1 つ含まれている検出結果を返します。

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 フィールドの 1 つ以上の要素に v サブフィールドがあり、その値が 321 以上であるものをクエリしています。

# 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 フィールドの 1 つ以上の要素が 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 分 5 秒(GMT)以降に発生した検出結果と一致します。event_time フィルタを使用すると、次の形式と型を使用して時間を表すことができます。

  • 整数リテラルとしてのエポック時間(ミリ秒単位)

    "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 配列の少なくとも 1 つの要素に、SOURCE_OF_REFERENCE と等しい source プロパティと FILTERED_URI を持つ uri プロパティの両方が含まれています。

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

次のように置き換えます。

indicator.domains フィールドのフィルタ

次の例では、少なくとも 1 つの指標ドメインに mycompanyprefix.ca の両方が含まれる検出結果が返されます。

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

indicator.ip_addresses フィールドのフィルタ

次の例では、indicator.ip_addresses 配列内の少なくとも 1 つの要素が IP_ADDRESS と等しい検出結果が返されます。

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

IP_ADDRESS は、検索する検索結果に関連付けられた IP アドレスに置き換えます。

外部システムの割り当て先のフィルタ

次の例では、external_systems.EXTERNAL_SYSTEM_NAME.assignees 配列内の少なくとも 1 つの要素が 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 配列内の少なくとも 1 つの要素が FOLDER_NAME と等しくない検出結果が返されます。

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

resource.folders.resource_folder_display_name フィールドのフィルタ

次の例では、resource.folders.resource_folder_display_name 配列内の少なくとも 1 つの要素が 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_ACCOUNT1SERVICE_ACCOUNT2SERVICE_ACCOUNT3 は、サービス アカウントのメールアドレスに置き換えます。

検出結果フィルタで contains() 関数と containsOnly() 関数を使用する方法については、配列型フィールドでのフィルタリングをご覧ください。

次のステップ

検出通知の設定で詳細を確認する。