Sicherheitsergebnisse mit Security Command Center API auflisten

>

Security Command Center-Ergebnisse modellieren die potenziellen Sicherheitsrisiken der Assets einer Organisation. Ein Ergebnis bezieht sich immer auf einen bestimmten Asset im Security Command Center.

In diesem Leitfaden erfahren Sie, wie Sie mit Security Command Center-Clientbibliotheken auf die Ergebnisse einer Organisation zugreifen. Jedes Ergebnis gehört zu einer Quelle. Die meisten Detektoren oder Anbieter von Ergebnissen liefern Ergebnisse innerhalb derselben Quelle.

Hinweis

Bevor Sie eine Quelle einrichten, müssen Sie die folgenden Schritte ausführen:

Seitengröße

Alle Security Command Center-Listen-APIs sind paginiert. Jede Antwort gibt eine Seite mit Ergebnissen und ein Token zurück, um die nächste Seite zurückzugeben. Die Seitengröße kann konfiguriert werden. Die Standardseitengröße ist 10. Sie kann auf ein Minimum von 1 und maximal 1.000 festgelegt werden.

Aufbewahrung von Ergebnissen

Die Ergebnisse enthalten eine Reihe von event_time-Snapshots, die den Status und die Attribute des Ergebnisses jedes Mal erfassen, wenn die zugehörige Sicherheitslücke oder Bedrohung bei Scans erkannt wird.

Das Security Command Center speichert 13 Monate lang Snapshots nach ihrem event_time oder der Uhrzeit des Ereignisses. Nach 13 Monaten werden die Snapshots und ihre Daten aus der Security Command Center-Datenbank gelöscht und können nicht wiederhergestellt werden. Dies führt zu weniger Snapshots bei der Suche und begrenzt die Möglichkeit, den Verlauf eines Ergebnisses anzuzeigen und wie er sich im Laufe der Zeit verändert.

Das Ergebnis ist im Security Command Center, solange es mindestens einen Snapshot mit einem event_time enthält, der mehr als 13 Monate zurückliegt. Wenn Sie die Ergebnisse und alle zugehörigen Daten über einen längeren Zeitraum speichern möchten, exportieren Sie sie in einen anderen Speicherort. Eine Anleitung finden Sie unter Security Command Center-Daten exportieren.

Alle Ergebnisse in einer Organisation auflisten

gcloud

  # ORGANIZATION_ID=organization-id

  gcloud scc findings list $ORGANIZATION_ID

Führen Sie den folgenden Befehl aus, um weitere Beispiele aufzurufen:

  gcloud scc findings list --help

Python

from google.cloud import securitycenter

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

# organization_id is the numeric ID of the organization. e.g.:
# organization_id = "111122222444"
org_name = "organizations/{org_id}".format(org_id=organization_id)
# The "sources/-" suffix lists findings across all sources.  You
# also use a specific source_name instead.
all_sources = "{org_name}/sources/-".format(org_name=org_name)
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()) {
    // OrganizationName organizationName = OrganizationName.of(/*organizationId=*/"123234324");
    // "-" 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"
	"google.golang.org/api/iterator"
	securitycenterpb "google.golang.org/genproto/googleapis/cloud/securitycenter/v1"
)

// 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: %v", err)
	}
	defer client.Close() // Closing the client safely cleans up background resources.

	req := &securitycenterpb.ListFindingsRequest{
		// List findings across all 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: %v", 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: `organizations/${organizationId}/sources/-`,
  });
  let count = 0;
  Array.from(response).forEach(result =>
    console.log(
      `${++count} ${result.finding.name} ${result.finding.resourceName}`
    )
  );
}
listAllFindings();

Die Ausgabe für jedes Ergebnis sieht in etwa so aus:

finding:
  category: 'Persistence: IAM Anomalous Grant'
  createTime: '2020-09-13T12:30:10.248Z'
  eventTime: '2020-09-13T12:30:09.528Z'
  name: organizations/organization-id/sources/source-id/findings/finding-id
  parent: organizations/organization-id/sources/source-id
  resourceName: //cloudresourcemanager.googleapis.com/projects/project-number
  securityMarks:
    name: organizations/organization-id/sources/source-id/findings/finding-id/securityMarks
  sourceProperties:
    affectedResources:
    - gcpResourceName: //cloudresourcemanager.googleapis.com/projects/project-number
    detectionCategory:
      indicator: audit_log
      ruleName: iam_anomalous_grant
      subRuleName: service_account_granted_sensitive_role_to_member
      technique: persistence
    detectionPriority: HIGH
    evidence:
    - sourceLogId:
        insertId: insert-id
        projectId: project-id
        timestamp:
          nanos: 183000000
          seconds: '1600000206'
    findingId: finding-id
    properties:
      sensitiveRoleGrant:
        bindingDeltas:
        - action: ADD
          member: user:user-email@gmail.com
          role: roles/editor
        members:
        - user:user-email@gmail.com
        principalEmail: principal-email.iam.gserviceaccount.com
    sourceId:
      customerOrganizationNumber: 'organization-id'
      projectNumber: 'project-number'
  state: ACTIVE
resource:
  name: //cloudresourcemanager.googleapis.com/projects/project-number
  parentDisplayName: organization-name
  parentName: //cloudresourcemanager.googleapis.com/folders/folder-id
  projectDisplayName: project-id
  projectName: //cloudresourcemanager.googleapis.com/projects/project-number

Ergebnisse filtern

Eine Organisation hat möglicherweise viele Ergebnisse. Im vorherigen Beispiel wird kein Filter verwendet, daher werden alle Ergebnisdatensätze zurückgegeben. Mit Security Command Center können Sie Filter verwenden, um Informationen zu den gewünschten Ergebnissen zu erhalten, und das übergeordnete Element auf eine bestimmte Quelle beschränken.

Die Ergebnisfilter sind mit "where"-Klauseln in SQL-Anweisungen vergleichbar, mit Ausnahme von Spalten. Sie gelten für die von der API zurückgegebenen Objekte.

Das folgende Beispiel zeigt nur Ergebnisse mit der Kategorie "MEDIUM_RISK_ONE" an. Bestimmte Kategorien können sich ändern. Sie sollten daher anhand der Dokumentation eines Ergebnisanbieters die von ihm verwendeten Kategorien ermitteln.

gcloud

  # ORGANIZATION_ID=organization-id
  # SOURCE_ID="source-id"
  FILTER="category=\"MEDIUM_RISK_ONE\""

  gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID --filter="$FILTER"

Führen Sie den folgenden Befehl aus, um weitere Beispiele aufzurufen:

  gcloud scc findings list --help

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 = "organizations/{organization_id}/sources/{source_id}"
# e.g.:
# source_name = "organizations/111122222444/sources/1234"
# 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()) {
    // SourceName sourceName = SourceName.of(/*organizationId=*/"123234324",
    // /*sourceId=*/"423432321");

    // 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"
	"google.golang.org/api/iterator"
	securitycenterpb "google.golang.org/genproto/googleapis/cloud/securitycenter/v1"
)

// 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 := "organizations/111122222444/sources/1234"
	// All sources.
	// sourceName := "organizations/111122222444/sources/-"
	// 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: %v", 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: %v", 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 = "organizations/111122222444/sources/1234";

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 unterstützt auch vollständige JSON-Arrays und Objekte als potenzielle Property-Typen. Sie können nach folgenden Kriterien filtern:

  • Arrayelemente
  • Vollständige JSON-Objekte mit partieller Stringübereinstimmung im Objekt
  • Untergeordnete JSON-Objekt-Felder

Untergeordnete Felder müssen Zahlen, Strings oder boolesche Werte enthalten und müssen die folgenden Operatoren verwenden:

  • Strings:
    • Vollständige Gleichheit =
    • Teilstring, der mit : übereinstimmt
  • Nummern:
    • Ungleichungen <, >, <=, >=
    • Gleichheit =
  • Boolesche Werte:
    • Gleichheit =

In den Beispielen weiter unten auf dieser Seite wird davon ausgegangen, dass das folgende JSON-Objekt ein Quellattribut für ein Ergebnis ist:

{
    "outer_object": {
        "middle_object": {
            "deeply_nested_object": {
                "x": 123,
            },
            "y": "some-string-value",
        },
        "z": "some-other-string-value",
        "u": ["list-element-1", "list-element-2", "list-element-3", ],
    },
}

Beispiel für das Filtern von Ergebnissen

In diesem Beispiel ist das vorherige JSON-Objekt ein Quellattribut namens my_property bei einem Ergebnis. Das folgende Beispiel enthält Abfragen für Ergebnisse, die das Objekt als Attribut haben. Sie können diese Filter auch mit anderen Filtern verwenden, indem Sie AND und OR in der Abfrage verwenden.

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

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

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

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

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

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

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

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

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

Beispiel für das Sortieren von Ergebnissen

Sie können Ergebnisse nach strengen Teilfeldern sortieren, die einfache Typen sind, wie Strings, Zahlen und boolesche Werte. In diesem Beispiel ist das vorherige JSON-Objekt ein Quellattribut namens my_property bei einem Ergebnis. Das folgende Beispiel enthält Abfragen zum Sortieren der Ergebnisfelder:

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

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

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

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

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

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

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

Abfragen zu einem bestimmten Zeitpunkt

Mit Security Command Center können Sie Ergebnisse zu einer bestimmten Snapshot-Zeit bis zu 13 Monate auflisten. Weitere Informationen finden Sie unter Aufbewahrungsdauer.

gcloud

  # ORGANIZATION_ID=organization-id
  # SOURCE_ID="source-id"
  # READ_TIME follows the format YYYY-MM-DDThh:mm:ss.ffffffZ
  READ_TIME=2019-02-28T07:00:06.861Z

  gcloud scc findings list $ORGANIZATION_ID --source=$SOURCE_ID --read-time=$READ_TIME

Führen Sie den folgenden Befehl aus, um weitere Beispiele aufzurufen:

  gcloud scc findings list --help

Python

from google.cloud import securitycenter
from datetime import timedelta, datetime

# 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 = "organizations/{organization_id}/sources/{source_id}"
# e.g.:
# source_name = "organizations/111122222444/sources/1234"
# You an also use a wild-card "-" for all sources:
#   source_name = "organizations/111122222444/sources/-"
five_days_ago = str(datetime.now() - timedelta(days=5))

finding_result_iterator = client.list_findings(
    request={"parent": source_name, "filter": five_days_ago}
)
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> listFindingsAtTime(SourceName sourceName) {
  try (SecurityCenterClient client = SecurityCenterClient.create()) {
    // SourceName sourceName = SourceName.of(/*organizationId=*/"123234324",
    // /*sourceId=*/"423432321");

    // 5 days ago
    Instant fiveDaysAgo = Instant.now().minus(Duration.ofDays(5));

    ListFindingsRequest.Builder request =
        ListFindingsRequest.newBuilder()
            .setParent(sourceName.toString())
            .setReadTime(
                Timestamp.newBuilder()
                    .setSeconds(fiveDaysAgo.getEpochSecond())
                    .setNanos(fiveDaysAgo.getNano()));

    // 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"
	"time"

	securitycenter "cloud.google.com/go/securitycenter/apiv1"
	"github.com/golang/protobuf/ptypes"
	"google.golang.org/api/iterator"
	securitycenterpb "google.golang.org/genproto/googleapis/cloud/securitycenter/v1"
)

// listFindingsAtTime prints findings that where present for a specific source
// as of five days ago to w. sourceName is the full resource name of the
// source to search for findings under.
func listFindingsAtTime(w io.Writer, sourceName string) error {
	// Specific source.
	// sourceName := "organizations/111122222444/sources/1234"
	// All sources.
	// sourceName := "organizations/111122222444/sources/-"
	ctx := context.Background()
	client, err := securitycenter.NewClient(ctx)
	if err != nil {
		return fmt.Errorf("securitycenter.NewClient: %v", err)
	}
	defer client.Close() // Closing the client safely cleans up background resources.
	fiveDaysAgo, err := ptypes.TimestampProto(time.Now().AddDate(0, 0, -5))
	if err != nil {
		return fmt.Errorf("Error converting five days ago: %v", err)
	}

	req := &securitycenterpb.ListFindingsRequest{
		Parent:   sourceName,
		ReadTime: fiveDaysAgo,
	}
	it := client.ListFindings(ctx, req)
	for {
		result, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return fmt.Errorf("it.Next: %v", 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 fully qualified source name to search for findings
// under.
/*
 * TODO(developer): Uncomment the following lines
 */
// const sourceName = "organizations/111122222444/sources/1234";

const fiveDaysAgo = new Date();
fiveDaysAgo.setDate(fiveDaysAgo.getDate() - 5);

async function listFindingsAtTime() {
  const [response] = await client.listFindings({
    // List findings across all sources.
    parent: sourceName,
    readTime: {
      seconds: Math.floor(fiveDaysAgo.getTime() / 1000),
      nanos: (fiveDaysAgo.getTime() % 1000) * 1e6,
    },
  });
  let count = 0;
  Array.from(response).forEach(result =>
    console.log(
      `${++count} ${result.finding.name} ${result.finding.resourceName}`
    )
  );
}
listFindingsAtTime();

Beispiele für Filter

Im Folgenden sind einige weitere nützliche Filter für Ergebnisse aufgeführt.

Ergebnisse, die nach einem bestimmten Zeitpunkt erfasst wurden

Diese Beispielfilter stimmen mit Ergebnissen überein, die erst nach Mittwoch, 5. Juni 2019, 22:12:05 Uhr (GMT) aufgetreten sind. Mit dem Filter event_time können Sie die Zeit in den folgenden Formaten und Typen angeben:

  • Unix-Zeit (in Millisekunden) als Ganzzahlliteral

    "event_time > 1559772725000"
    
  • RFC 3339 als String-Literal

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