הצגת ממצאי אבטחה באמצעות Security Command Center API

מודל הממצאים של Security Command Center מציג את סיכוני האבטחה הפוטנציאליים של נכסים בפרויקט או בארגון. ממצא תמיד קשור לנכס ספציפי ב-Security Command Center.

במדריך הזה מוסבר איך להשתמש בספריות הלקוח של Security Command Center כדי לגשת לממצאים. כל ממצא שייך למקור. רוב הספקים של גלאים או ממצאים מייצרים ממצאים באותו מקור.

אפשר להעניק את תפקידי ה-IAM של Security Command Center ברמת הארגון, התיקייה או הפרויקט. היכולת שלכם להציג, לערוך, ליצור או לעדכן ממצאים, נכסים ומקורות אבטחה תלויה ברמת הגישה שניתנה לכם. מידע נוסף על תפקידים ב-Security Command Center זמין במאמר בקרת גישה.

לפני שמתחילים

לפני שמגדירים מקור, צריך לבצע את הפעולות הבאות:

גודל דף

כל ממשקי ה-API של רשימות ב-Security Command Center מחולקים לדפים. כל תגובה מחזירה דף של תוצאות ואסימון להחזרת הדף הבא. אפשר להגדיר את גודל הדף. גודל הדף שמוגדר כברירת מחדל הוא 10. אפשר להגדיר ערך מינימלי של 1 וערך מקסימלי של 1, 000.

שמירת הממצאים

בהתאם לרמת השירות של Security Command Center, הממצאים נשארים זמינים לכם כדי ליצור רשימה או לשלוח שאילתה לגבי תקופות זמן ספציפיות. מידע נוסף על שמירת נתונים ב-Security Command Center זמין במאמר שמירת נתונים.

הצגת כל הממצאים

gcloud

כדי להציג ברשימה את כל הממצאים בפרויקט, בתיקייה או בארגון, מריצים את הפקודה הבאה:

gcloud scc findings list PARENT_TYPE/PARENT_ID \
  --location=LOCATION

מחליפים את מה שכתוב בשדות הבאים:

  • PARENT_TYPE: הרמה בהיררכיית המשאבים שבה רוצים להציג את הממצאים. אפשר להשתמש בערכים organizations, folders או projects.
  • PARENT_ID: המזהה המספרי של הארגון, התיקייה או הפרויקט, או מזהה הפרויקט האלפאנומרי.
  • LOCATION: המיקום של Security Command Center שבו יופיעו הממצאים. אם מופעלת תכונת שמירת הנתונים במיקום גיאוגרפי, צריך להשתמש בערכים eu,‏ sa או us. אחרת, צריך להשתמש בערך global.

כדי לראות עוד דוגמאות, מריצים את הפקודה:

gcloud scc findings list --help

דוגמאות מופיעות במאמרי העזרה בנושא gcloud scc findings list.

Go

import (
	"context"
	"fmt"
	"io"

	securitycenter "cloud.google.com/go/securitycenter/apiv2"
	"cloud.google.com/go/securitycenter/apiv2/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/-/locations/global"
		//		"projects/{projectId}/sources/-/locations/global"
		//		"folders/{folderId}/sources/-/locations/global"
		Parent: fmt.Sprintf("organizations/%s/sources/-/locations/global", 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
}

Java


import com.google.cloud.securitycenter.v2.ListFindingsRequest;
import com.google.cloud.securitycenter.v2.ListFindingsResponse.ListFindingsResult;
import com.google.cloud.securitycenter.v2.SecurityCenterClient;
import java.io.IOException;

public class ListAllFindings {

  public static void main(String[] args) throws IOException {
    // organizationId: The source to list all findings for.
    // You can also use project/ folder as the parent resource.
    String organizationId = "google-cloud-organization-id";

    // Specify the location to list the findings.
    String location = "global";

    // The source id to scope the findings.
    String sourceId = "source-id";

    listAllFindings(organizationId, sourceId, location);
  }

  // List all findings under a given parent resource.
  public static void listAllFindings(String organizationId, String sourceId, String location)
      throws IOException {
    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests.
    try (SecurityCenterClient client = SecurityCenterClient.create()) {
      ListFindingsRequest request =
          ListFindingsRequest.newBuilder()
              // To list findings across all sources, use "-".
              .setParent(
                  String.format("organizations/%s/sources/%s/locations/%s", organizationId,
                      sourceId,
                      location))
              .build();

      for (ListFindingsResult result : client.listFindings(request).iterateAll()) {
        System.out.printf("Finding: %s", result.getFinding().getName());
      }
      System.out.println("\nListing complete.");
    }
  }
}

Node.js

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

// Creates a new client.
const client = new SecurityCenterClient();

// TODO(developer): Update the following for your own environment.
const organizationId = '1081635000895';
const location = 'global';

// Required. Name of the source the findings belong to. If no location is
// specified, the default is global. The following list shows some examples:
// - `organizations/[organization_id]/sources/[source_id]/locations/[location_id]`
// - `folders/[folder_id]/sources/[source_id]`
// - `folders/[folder_id]/sources/[source_id]/locations/[location_id]`
// - `projects/[project_id]/sources/[source_id]`
// - `projects/[project_id]/sources/[source_id]/locations/[location_id]`
// To groupBy across all sources provide a source_id of `-`.
const parent = `organizations/${organizationId}/sources/-/locations/${location}`;

// Build the list findings request.
const listFindingsRequest = {
  parent,
};

async function listAllFindings() {
  // Call the API.
  const iterable = client.listFindingsAsync(listFindingsRequest);
  let count = 0;

  for await (const response of iterable) {
    // Just print a few for demonstration.
    if (count > 5) break;
    console.log(
      `${++count} ${response.finding.name} ${response.finding.resourceName}`
    );
  }
}

await listAllFindings();

Python

def list_all_findings(organization_id, source_name, location_id) -> int:
    """
    lists all findings for a source
    Args:
       organization_id: organization_id is the numeric ID of the organization. e.g.:organization_id = "111122222444"
       source_name: is the resource path for a source that has been created
       location_id: GCP location id; example: 'global'
    Returns:
        int: returns the count of all findings for a source
    """
    from google.cloud import securitycenter_v2

    # Create a client.
    client = securitycenter_v2.SecurityCenterClient()
    parent = f"organizations/{organization_id}"
    all_sources = f"{parent}/sources/{source_name}/locations/{location_id}"

    # Create the request dictionary
    request = {"parent": all_sources}

    # Print the request for debugging
    print("Request: ", request)

    finding_result_iterator = client.list_findings(request={"parent": all_sources})
    for count, finding_result in enumerate(finding_result_iterator):
        print(
            "{}: name: {} resource: {}".format(
                count, finding_result.finding.name, finding_result.finding.resource_name
            )
        )
    return finding_result_iterator

הפלט של כל ממצא נראה כך: לרשימה מלאה של השדותListFindingsResult

{
  "finding": {
    "name": "organizations/ORGANIZATION_ID/sources/SOURCE_ID/findings/FINDING_ID",
    "parentDisplayName": "SOURCE_DISPLAY_NAME",
    "resourceName": "//cloudresourcemanager.googleapis.com/projects/PROJECT_NUMBER",
    "state": "ACTIVE",
    "category": "Malware: Cryptomining Bad Domain",
    "eventTime": "2021-11-10T17:41:41.594Z",
    "createTime": "2021-11-10T17:41:42.014Z",
    "severity": "LOW",
    "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",
    "gcpMetadata": {
      "projectDisplayName": "PROJECT_ID"
    }
  }
}

סינון הממצאים

יכול להיות שבפרויקט, בתיקייה או בארגון יהיו הרבה ממצאים. בדוגמה שלמעלה לא נעשה שימוש במסנן, ולכן כל הרשומות של הממצאים מוחזרות.

כדי לקבל מידע רק על השדות שרוצים, אפשר להשתמש במסנני חיפוש. המסננים האלה דומים לסעיפי WHERE בהצהרות SQL, אבל במקום להחיל אותם על עמודות, הם חלים על האובייקטים שמוחזרים על ידי ה-API.

בדוגמה הבאה מפורטות רק הממצאים ששייכים לקטגוריה MEDIUM_RISK_ONE. ספקי ממצאים שונים (שנקראים גם מקורות אבטחה) משתמשים בקבוצות שונות של קטגוריות. כדי לדעת באילו קטגוריות אפשר להשתמש במסנן, אפשר לעיין במסמכי התיעוד של ספק הממצאים.

gcloud

כדי לסנן את הממצאים, משתמשים בפקודה הבאה:

gcloud scc findings list PARENT_TYPE/PARENT_ID \
  --location=LOCATION \
  --source=SOURCE_ID \
  --filter="FILTER"

מחליפים את מה שכתוב בשדות הבאים:

  • PARENT_TYPE: הרמה בהיררכיית המשאבים שבה רוצים להציג את הממצאים. אפשר להשתמש בערכים organizations, folders או projects.
  • PARENT_ID: המזהה המספרי של הארגון, התיקייה או הפרויקט, או מזהה הפרויקט האלפאנומרי.
  • LOCATION: המיקום של Security Command Center שבו רוצים להציג את הממצאים עם מסנן. אם מופעלת שמירת נתונים באזור מסוים, צריך להשתמש בערכים eu,‏ sa או us. אחרת, צריך להשתמש בערך global.
  • SOURCE_ID: המזהה של מקור האבטחה שמספק את סוג הממצא.
  • FILTER: המסנן שצריך להשתמש בו. לדוגמה, המסנן הבא מחזיר ממצאים מהקטגוריה MEDIUM_RISK_ONE בלבד:
    --filter="category=\"MEDIUM_RISK_ONE\""

כדי לראות עוד דוגמאות, מריצים את הפקודה:

gcloud scc findings list --help

דוגמאות מופיעות במאמרי העזרה בנושא gcloud scc findings list.

Go

import (
	"context"
	"fmt"
	"io"

	securitycenter "cloud.google.com/go/securitycenter/apiv2"
	"cloud.google.com/go/securitycenter/apiv2/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
}

Java


import com.google.cloud.securitycenter.v2.ListFindingsRequest;
import com.google.cloud.securitycenter.v2.ListFindingsResponse.ListFindingsResult;
import com.google.cloud.securitycenter.v2.SecurityCenterClient;
import java.io.IOException;

public class ListFindingsWithFilter {

  public static void main(String[] args) throws IOException {
    // TODO: Replace the variables within {}
    // organizationId: Google Cloud Organization id.
    // You can also use project/ folder as the parent resource.
    String organizationId = "google-cloud-organization-id";

    // Specify the location to list the findings.
    String location = "global";

    // The source id to scope the findings.
    String sourceId = "source-id";

    listFilteredFindings(organizationId, sourceId, location);
  }

  // List filtered findings under a source.
  public static void listFilteredFindings(String organizationId, String sourceId, String location)
      throws IOException {
    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests.
    try (SecurityCenterClient client = SecurityCenterClient.create()) {

      // Use any one of the following formats:
      //  * organizations/{organization_id}/sources/{source_id}/locations/{location}
      //  * folders/{folder_id}/sources/{source_id}/locations/{location}
      //  * projects/{project_id}/sources/{source_id}/locations/{location}
      String parent = String.format("organizations/%s/sources/%s/locations/%s", organizationId,
          sourceId,
          location);

      // Listing all findings of category "MEDIUM_RISK_ONE".
      String filter = "category=\"MEDIUM_RISK_ONE\"";

      ListFindingsRequest request =
          ListFindingsRequest.newBuilder()
              .setParent(parent)
              .setFilter(filter)
              .build();

      for (ListFindingsResult result : client.listFindings(request).iterateAll()) {
        System.out.printf("Finding: %s", result.getFinding().getName());
      }
      System.out.println("\nListing complete.");
    }
  }
}

Node.js

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

// Creates a new client.
const client = new SecurityCenterClient();

// TODO(developer): Update the following for your own environment.
const organizationId = '1081635000895';
const location = 'global';

// Required. Name of the source to groupBy. If no location is specified,
// finding is assumed to be in global.
//  The following list shows some examples:
// - `organizations/[organization_id]/sources/[source_id]`
// - `organizations/[organization_id]/sources/[source_id]/locations/[location_id]`
// - `folders/[folder_id]/sources/[source_id]`
// - `folders/[folder_id]/sources/[source_id]/locations/[location_id]`
// - `projects/[project_id]/sources/[source_id]`
// - `projects/[project_id]/sources/[source_id]/locations/[location_id]`
// To groupBy across all sources provide a source_id of `-`.
const parent = `organizations/${organizationId}/sources/-/locations/${location}`;

// Listing all findings of category "MEDIUM_RISK_ONE".
const filter = 'category="MEDIUM_RISK_ONE"';

// Build the list findings with filter request.
const listFilteredFindingsRequest = {
  parent,
  filter,
};

async function listFilteredFindings() {
  // Call the API.
  const iterable = client.listFindingsAsync(listFilteredFindingsRequest);
  let count = 0;
  console.log('Findings:');
  for await (const response of iterable) {
    // Just print a few for demonstration.
    if (count > 5) break;
    console.log(
      `${++count} ${response.finding.name} ${response.finding.resourceName}`
    );
  }
}
await listFilteredFindings();

Python

def list_filtered_findings(organization_id, source_name, location_id) -> int:
    """
    lists filtered findings for a source
    Args:
        organization_id: organization_id is the numeric ID of the organization. e.g.:organization_id = "111122222444"
        source_name: is the resource path for a source that has been created
        location_id: GCP location id; example: 'global'
    Returns:
         int: returns the filtered findings for a source
    """
    count = 0
    from google.cloud import securitycenter_v2

    # Create a new client.
    client = securitycenter_v2.SecurityCenterClient()
    parent = f"organizations/{organization_id}"
    all_sources = f"{parent}/sources/{source_name}/locations/{location_id}"
    finding_result_iterator = client.list_findings(
        request={"parent": all_sources, "filter": 'severity="LOW"'}
    )
    # Iterate an print all finding names and the resource they are
    # in reference to.
    for count, finding_result in enumerate(finding_result_iterator):
        print(
            "{}: name: {} resource: {}".format(
                count, finding_result.finding.name, finding_result.finding.resource_name
            )
        )
    return count

בנוסף, Security Command Center תומך במערכים ובאובייקטים מלאים של JSON כסוגי מאפיינים פוטנציאליים. אפשר לסנן לפי:

  • רכיבי מערך
  • אובייקטים מלאים בפורמט JSON עם התאמה חלקית של מחרוזת בתוך האובייקט
  • שדות משנה של אובייקט JSON

אופרטורים נתמכים

ההצהרות של השאילתות לגבי הממצאים של Security Command Center תומכות באופרטורים שרוב ממשקי ה-API תומכים בהם. Google Cloud

ברשימה הבאה מוצג שימוש באופרטורים שונים:

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

ברשימה הבאה מפורטים כל האופרטורים והפונקציות שנתמכים בהצהרות של שאילתות לגבי ממצאים:

  • למחרוזות:

    • = להתאמה מדויקת

      לדוגמה, resource.parent_name="prod" תואם לערך prod. הוא לא תואם ל-production או ל-canaryprod.

    • : להתאמה חלקית

      לדוגמה, resource.parent_name:"prod" מתאים לערכים prod, production ו-canaryprod.

  • למספרים (חוץ מ-int64):

    • <, ‏ >, ‏ <=, ‏ >= לאי-שוויונים
    • =, != לשוויון
  • למספרים (int 64):

    • =, != לשוויון
  • לערכים בוליאניים:

    • = for equality
  • לגבי קשרים לוגיים:

    • AND
    • OR
    • NOT או -
  • לקיבוץ ביטויים:

    • (, ) (סוגריים)
  • למערכים:
    • contains(), פונקציה לשליחת שאילתות לגבי ממצאים עם שדה מערך שמכיל לפחות רכיב אחד שתואם למסנן שצוין
    • containsOnly(), פונקציה לשליחת שאילתות לגבי ממצאים עם שדה מערך שמכיל רק רכיבים שתואמים למסנן שצוין
  • לכתובות IP:
    • inIpRange(), פונקציה לשליחת שאילתות לגבי כתובות IP בטווח CIDR שצוין

סינון לפי כתובות 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, אפשר לסנן את הממצאים כך שיוצגו רק הממצאים שמכילים כתובת IP בטווח CIDR שצוין. באמצעות הפעולה NOT עם inIpRange, אפשר לסנן את הממצאים כך שיוצגו רק הממצאים שמכילים כתובת IP מחוץ לטווח ה-CIDR שצוין.

בדוגמה הבאה מוצג התחביר של הפונקציה inIpRange:

inIpRange(IP_FINDING_FIELD, "CIDR_RANGE")

אם כתובת ה-IP נמצאת ברכיב של מערך בשדה של ממצא שמכיל מערך, משתמשים בתחביר הבא עם הפונקציה contains ועם הפונקציה inIpRange:

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

בדוגמה הבאה, הפונקציה inIpRange מעריכה כל רכיב destination_ip במערך שמוכל בשדה connections של כתובת IP שנמצאת בטווח CIDR שמוגדר על ידי 192.0.2.0/24:

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

בדוגמה הבאה מוצגת פקודה ב-CLI של gcloud שמשתמשת בפונקציה inIpRange כדי לסנן ממצאים שיש להם כתובת IP בשדה connections.source_ip שנמצאת בטווח אחד, אבל לא בטווח אחר. השדה connections הוא שדה מסוג מערך, ולכן נעשה שימוש בפונקציה contains:

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --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. בדוגמה הבאה מוצגות שאילתות לגבי ממצאים שהאובייקט מוגדר בהם כמאפיין. אפשר גם להשתמש במסננים האלה עם מסננים אחרים באמצעות AND ו-OR בשאילתה. לדוגמה, השאילתה הראשונה משתמשת במסנן ACTIVE כדי להציג ממצאים פעילים.

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="state=\"ACTIVE\""
    --filter="my_property.outer_object.middle_object.deeply_nested_object.x = 123"

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="my_property.outer_object.middle_object.y = \"some-string-value\""

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="my_property.outer_object.middle_object.y : \"string-value\""

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="my_property.outer_object.z = \"some-other-string-value\""

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="my_property.outer_object.z : \"other-string-value\""

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="my_property.outer_object.u : \"list-element-1\""

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="my_property.outer_object.u : \"list-element-2\""

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --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.

ממשק ה-API של Security Command Center מחזיר ממצאים שבהם ARRAY_ATTRIBUTE_NAME מכיל לפחות רכיב אחד שעומד בדרישות של SUBFILTER.

הפורמט של הפונקציה 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.

ה-API של Security Command Center מחזיר ממצאים שבהם כל הרכיבים של ARRAY_ATTRIBUTE_NAME תואמים ל-SUBFILTER.

סינון משנה של מערך אובייקטים

הקטע הבא הוא קטע מדוגמת ה-JSON הקודמת. בדוגמה הבאה, השדה list_middle_object הוא מערך של אובייקטים:

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

בדוגמה הבאה מוצגת שאילתה למציאת ממצאים שבהם לפחות אחד מהרכיבים בשדה list_middle_object כולל שדה משנה v עם ערך שגדול מ-321 או שווה לו:

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --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:

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --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.
gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --filter="contains(my_property.outer_object.list_middle_object, v  >= 321 AND -contains(w, a = 3))"

דוגמאות מעשיות לשימוש בפונקציה contains() מופיעות במאמר ממצאים שמכילים ערכים ספציפיים במערך.

דוגמה למיון ממצאים

אפשר למיין את הממצאים לפי שדות משנה מחמירים שהם סוגים פרימיטיביים – מחרוזות, מספרים ובוליאנים. נניח שדוגמת ה-JSON הקודמת היא מאפיין של ממצא שנקרא my_property. הדוגמה הבאה כוללת שאילתות למיון השדות של הממצא. מילת המפתח DESC מציינת שהשדה שאחריה צריך להיות ממוין בסדר יורד. ברירת המחדל מוגדרת לסדר עולה.

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --order-by="my_property.outer_object.middle_object.deeply_nested_object.x DESC"

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --order-by="my_property.outer_object.middle_object.deeply_nested_object.x"

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --order-by="my_property.outer_object.middle_object.y DESC"

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --order-by="my_property.outer_object.middle_object.y"

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --order-by="my_property.outer_object.z DESC"

gcloud scc findings list PARENT_TYPE/PARENT_ID \
    --location=LOCATION \
    --source=SOURCE_ID \
    --order-by="my_property.outer_object.z"

דוגמאות למסננים

בקטעים הבאים מוצגות דוגמאות מעשיות למציאת מסננים.

סינון ממצאים שהתרחשו אחרי נקודת זמן מסוימת

המסננים לדוגמה האלה תואמים לממצאים שהתרחשו לאחרונה אחרי יום רביעי, 5 ביוני 2019 בשעה 22:12:05 לפי שעון GMT. בעזרת המסנן event_time, אפשר לציין זמן בפורמטים ובסוגים הבאים:

  • זמן ראשית זמן יוניקס (באלפיות השנייה) כמספר שלם

    "event_time > 1559772725000"
    
  • RFC 3339 כמחרוזת ליטרלית

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

סינון לפי שדות מסוג מערך

בדוגמה הבאה אפשר לראות שימוש בהתאמה חלקית של מחרוזת בשדה מסוג מערך בתוך מסנן:

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

ה-API של Security Command Center מחזיר כל ממצא עם מחרוזת חלקית website.com במערך. לדוגמה, הוא מתאים לממצא עם indicator.domains = [\"onewebsite.com\"] כי 'website.com' היא מחרוזת משנה ברכיב במערך.

בקטעים הבאים, מסנני הדוגמה מציגים כמה אפשרויות לשימוש בסינון של מערכים עשירים באמצעות הפונקציה contains().

סינון לפי השדה vulnerability.cve.references

בדוגמה הבאה מוחזרות תוצאות חיפוש שבהן לפחות רכיב אחד במערך vulnerability.cve.references כולל גם מאפיין source ששווה ל-SOURCE_OF_REFERENCE וגם מאפיין uri שכולל FILTERED_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() במסנן של ממצא.

המאמרים הבאים

מידע נוסף על הגדרת התראות על מציאת פריטים