Zoom-Betriebsprotokolle erfassen

Unterstützt in:

In diesem Dokument wird beschrieben, wie Sie Zoom-Vorgangsprotokolle mit Amazon S3 in Google Security Operations aufnehmen. Der Parser wandelt die Rohlogs in ein einheitliches Datenmodell (Unified Data Model, UDM) um. Felder werden aus der Rohlogmeldung extrahiert, Daten werden bereinigt und normalisiert und die extrahierten Informationen werden den entsprechenden UDM-Feldern zugeordnet. So werden die Daten für die Analyse und Korrelation in einem SIEM-System angereichert.

Hinweise

Prüfen Sie, ob folgende Voraussetzungen erfüllt sind:

  • Google SecOps-Instanz
  • Privilegierter Zugriff auf Zoom
  • Privilegierter Zugriff auf AWS (S3, IAM, Lambda, EventBridge)

Voraussetzungen für das Erfassen von Zoom-Vorgangsprotokollen (IDs, API-Schlüssel, Organisations-IDs, Tokens)

  1. Melden Sie sich im Zoom App Marketplace an.
  2. Rufen Sie Develop > Build App > Server-to-Server OAuth auf.
  3. Erstellen Sie die App und fügen Sie den folgenden Bereich hinzu: report:read:operation_logs:admin (oder report:read:admin).
  4. Kopieren und speichern Sie unter App-Anmeldedaten die folgenden Details an einem sicheren Ort:
    • Konto-ID
    • Client-ID
    • Clientschlüssel.

AWS S3-Bucket und IAM für Google SecOps konfigurieren

  1. Erstellen Sie einen Amazon S3-Bucket. Folgen Sie dazu dieser Anleitung: Bucket erstellen.
  2. Speichern Sie den Namen und die Region des Buckets zur späteren Verwendung (z. B. zoom-operation-logs).
  3. Erstellen Sie einen Nutzer gemäß dieser Anleitung: IAM-Nutzer erstellen.
  4. Wählen Sie den erstellten Nutzer aus.
  5. Wählen Sie den Tab Sicherheitsanmeldedaten aus.
  6. Klicken Sie im Abschnitt Zugriffsschlüssel auf Zugriffsschlüssel erstellen.
  7. Wählen Sie als Anwendungsfall Drittanbieterdienst aus.
  8. Klicken Sie auf Weiter.
  9. Optional: Fügen Sie ein Beschreibungstag hinzu.
  10. Klicken Sie auf Zugriffsschlüssel erstellen.
  11. Klicken Sie auf CSV-Datei herunterladen, um den Access Key (Zugriffsschlüssel) und den Secret Access Key (geheimer Zugriffsschlüssel) zur späteren Verwendung zu speichern.
  12. Klicken Sie auf Fertig.
  13. Wählen Sie den Tab Berechtigungen aus.
  14. Klicken Sie im Bereich Berechtigungsrichtlinien auf Berechtigungen hinzufügen.
  15. Wählen Sie Berechtigungen hinzufügen aus.
  16. Wählen Sie Richtlinien direkt anhängen aus.
  17. Suchen Sie nach der Richtlinie AmazonS3FullAccess und wählen Sie sie aus.
  18. Klicken Sie auf Weiter.
  19. Klicken Sie auf Berechtigungen hinzufügen.

IAM-Richtlinie und ‑Rolle für S3-Uploads konfigurieren

  1. Rufen Sie in der AWS-Konsole IAM > Richtlinien > Richtlinie erstellen > JSON-Tab auf.
  2. Geben Sie die folgende Richtlinie ein:

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "AllowPutZoomOperationLogs",
          "Effect": "Allow",
          "Action": ["s3:PutObject"],
          "Resource": "arn:aws:s3:::zoom-operation-logs/zoom/operationlogs/*"
        },
        {
          "Sid": "AllowStateReadWrite",
          "Effect": "Allow",
          "Action": ["s3:GetObject", "s3:PutObject"],
          "Resource": "arn:aws:s3:::zoom-operation-logs/zoom/operationlogs/state.json"
        }
      ]
    }
    
    • Ersetzen Sie zoom-operation-logs, wenn Sie einen anderen Bucket-Namen eingegeben haben.
  3. Klicken Sie auf Weiter > Richtlinie erstellen.

  4. Rufen Sie IAM > Rollen > Rolle erstellen > AWS-Service > Lambda auf.

  5. Hängen Sie die neu erstellte Richtlinie an.

  6. Geben Sie der Rolle den Namen WriteZoomOperationLogsToS3Role und klicken Sie auf Rolle erstellen.

Lambda-Funktion erstellen

  1. Rufen Sie in der AWS Console Lambda > Funktionen > Funktion erstellen auf.
  2. Klicken Sie auf Von Grund auf erstellen.
  3. Geben Sie die folgenden Konfigurationsdetails an:
Einstellung Wert
Name zoom_operationlogs_to_s3
Laufzeit Python 3.13
Architektur x86_64
Ausführungsrolle WriteZoomOperationLogsToS3Role
  1. Nachdem die Funktion erstellt wurde, öffnen Sie den Tab Code, löschen Sie den Stub und geben Sie den folgenden Code ein(zoom_operationlogs_to_s3.py):

    #!/usr/bin/env python3
    import os, json, gzip, io, uuid, datetime as dt, base64, urllib.parse, urllib.request
    import boto3
    
    # ---- Environment ----
    S3_BUCKET = os.environ["S3_BUCKET"]
    S3_PREFIX = os.environ.get("S3_PREFIX", "zoom/operationlogs/")
    STATE_KEY = os.environ.get("STATE_KEY", S3_PREFIX + "state.json")
    ZOOM_ACCOUNT_ID = os.environ["ZOOM_ACCOUNT_ID"]
    ZOOM_CLIENT_ID = os.environ["ZOOM_CLIENT_ID"]
    ZOOM_CLIENT_SECRET = os.environ["ZOOM_CLIENT_SECRET"]
    PAGE_SIZE = int(os.environ.get("PAGE_SIZE", "300"))  # API default 30; max may vary
    TIMEOUT = int(os.environ.get("TIMEOUT", "30"))
    
    TOKEN_URL = "https://zoom.us/oauth/token"
    REPORT_URL = "https://api.zoom.us/v2/report/operationlogs"
    
    s3 = boto3.client("s3")
    
    # ---- Helpers ----
    
    def _http(req: urllib.request.Request):
        return urllib.request.urlopen(req, timeout=TIMEOUT)
    
    def get_token() -> str:
        params = urllib.parse.urlencode({
            "grant_type": "account_credentials",
            "account_id": ZOOM_ACCOUNT_ID,
        }).encode()
        basic = base64.b64encode(f"{ZOOM_CLIENT_ID}:{ZOOM_CLIENT_SECRET}".encode()).decode()
        req = urllib.request.Request(
            TOKEN_URL,
            data=params,
            headers={
                "Authorization": f"Basic {basic}",
                "Content-Type": "application/x-www-form-urlencoded",
                "Accept": "application/json",
                "Host": "zoom.us",
            },
            method="POST",
        )
        with _http(req) as r:
            body = json.loads(r.read())
            return body["access_token"]
    
    def get_state() -> dict:
        try:
            obj = s3.get_object(Bucket=S3_BUCKET, Key=STATE_KEY)
            return json.loads(obj["Body"].read())
        except Exception:
            # initial state: start today
            today = dt.date.today().isoformat()
            return {"cursor_date": today, "next_page_token": None}
    
    def put_state(state: dict):
        state["updated_at"] = dt.datetime.utcnow().isoformat() + "Z"
        s3.put_object(Bucket=S3_BUCKET, Key=STATE_KEY, Body=json.dumps(state).encode())
    
    def write_chunk(items: list[dict], ts: dt.datetime) -> str:
        key = f"{S3_PREFIX}{ts:%Y/%m/%d}/zoom-operationlogs-{uuid.uuid4()}.json.gz"
        buf = io.BytesIO()
        with gzip.GzipFile(fileobj=buf, mode="w") as gz:
            for rec in items:
                gz.write((json.dumps(rec) + "n").encode())
        buf.seek(0)
        s3.upload_fileobj(buf, S3_BUCKET, key)
        return key
    
    def fetch_page(token: str, from_date: str, to_date: str, next_page_token: str | None) -> dict:
        q = {
            "from": from_date,
            "to": to_date,
            "page_size": str(PAGE_SIZE),
        }
        if next_page_token:
            q["next_page_token"] = next_page_token
        url = REPORT_URL + "?" + urllib.parse.urlencode(q)
        req = urllib.request.Request(url, headers={
            "Authorization": f"Bearer {token}",
            "Accept": "application/json",
        })
        with _http(req) as r:
            return json.loads(r.read())
    
    def lambda_handler(event=None, context=None):
        token = get_token()
        state = get_state()
    
        cursor_date = state.get("cursor_date")  # YYYY-MM-DD
        # API requires from/to in yyyy-mm-dd, max one month per request
        from_date = cursor_date
        to_date = cursor_date
    
        total_written = 0
        next_token = state.get("next_page_token")
    
        while True:
            page = fetch_page(token, from_date, to_date, next_token)
            items = page.get("operation_logs", []) or []
            if items:
                write_chunk(items, dt.datetime.utcnow())
                total_written += len(items)
            next_token = page.get("next_page_token")
            if not next_token:
                break
    
        # Advance to next day if we've finished this date
        today = dt.date.today().isoformat()
        if cursor_date < today:
            nxt = (dt.datetime.fromisoformat(cursor_date) + dt.timedelta(days=1)).date().isoformat()
            state["cursor_date"] = nxt
            state["next_page_token"] = None
        else:
            # stay on today; continue later with next_page_token=None
            state["next_page_token"] = None
    
        put_state(state)
        return {"ok": True, "written": total_written, "date": from_date}
    
    if __name__ == "__main__":
        print(lambda_handler())
    
  2. Klicken Sie auf Konfiguration> Umgebungsvariablen> Bearbeiten> Neue Umgebungsvariable hinzufügen.

  3. Geben Sie die folgenden Umgebungsvariablen ein und ersetzen Sie die Platzhalter durch Ihre Werte:

    Schlüssel Beispielwert
    S3_BUCKET zoom-operation-logs
    S3_PREFIX zoom/operationlogs/
    STATE_KEY zoom/operationlogs/state.json
    ZOOM_ACCOUNT_ID <your-zoom-account-id>
    ZOOM_CLIENT_ID <your-zoom-client-id>
    ZOOM_CLIENT_SECRET <your-zoom-client-secret>
    PAGE_SIZE 300
    TIMEOUT 30
  4. Bleiben Sie nach dem Erstellen der Funktion auf der zugehörigen Seite oder öffnen Sie Lambda > Funktionen > Ihre Funktion.

  5. Wählen Sie den Tab Konfiguration aus.

  6. Klicken Sie im Bereich Allgemeine Konfiguration auf Bearbeiten.

  7. Ändern Sie Zeitlimit in 5 Minuten (300 Sekunden) und klicken Sie auf Speichern.

EventBridge-Zeitplan erstellen

  1. Rufen Sie Amazon EventBridge > Scheduler auf.
  2. Klicken Sie auf Zeitplan erstellen.
  3. Geben Sie die folgenden Konfigurationsdetails an:
    • Wiederkehrender Zeitplan: Preis (15 min).
    • Ziel: Ihre Lambda-Funktion zoom_operationlogs_to_s3.
    • Name: zoom-operationlogs-schedule-15min.
  4. Klicken Sie auf Zeitplan erstellen.

Optional: IAM-Nutzer mit Lesezugriff und Schlüssel für Google SecOps erstellen

  1. Rufen Sie in der AWS-Konsole IAM > Nutzer > Nutzer hinzufügen auf.
  2. Klicken Sie auf Add users (Nutzer hinzufügen).
  3. Geben Sie die folgenden Konfigurationsdetails an:
    • Nutzer: secops-reader.
    • Zugriffstyp: Zugriffsschlüssel – programmatischer Zugriff.
  4. Klicken Sie auf Nutzer erstellen.
  5. Minimale Leseberechtigung (benutzerdefiniert) anhängen: Nutzer > secops-reader > Berechtigungen > Berechtigungen hinzufügen > Richtlinien direkt anhängen > Richtlinie erstellen.
  6. Geben Sie im JSON-Editor die folgende Richtlinie ein:

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": ["s3:GetObject"],
          "Resource": "arn:aws:s3:::zoom-operation-logs/*"
        },
        {
          "Effect": "Allow",
          "Action": ["s3:ListBucket"],
          "Resource": "arn:aws:s3:::zoom-operation-logs"
        }
      ]
    }
    
  7. Legen Sie secops-reader-policy als Name fest.

  8. Gehen Sie zu Richtlinie erstellen> suchen/auswählen > Weiter > Berechtigungen hinzufügen.

  9. Rufen Sie Sicherheitsanmeldedaten > Zugriffsschlüssel > Zugriffsschlüssel erstellen auf.

  10. Laden Sie die CSV herunter (diese Werte werden in den Feed eingegeben).

Feed in Google SecOps konfigurieren, um Zoom-Vorgangsprotokolle aufzunehmen

  1. Rufen Sie die SIEM-Einstellungen > Feeds auf.
  2. Klicken Sie auf + Neuen Feed hinzufügen.
  3. Geben Sie im Feld Feed name (Feedname) einen Namen für den Feed ein, z. B. Zoom Operation Logs.
  4. Wählen Sie Amazon S3 V2 als Quelltyp aus.
  5. Wählen Sie Zoom Operation Logs (Zoom-Vorgangsprotokolle) als Log type (Logtyp) aus.
  6. Klicken Sie auf Weiter.
  7. Geben Sie Werte für die folgenden Eingabeparameter an:
    • S3-URI: s3://zoom-operation-logs/zoom/operationlogs/
    • Optionen zum Löschen von Quellen: Wählen Sie die gewünschte Option zum Löschen aus.
    • Maximales Dateialter: Dateien einschließen, die in den letzten Tagen geändert wurden. Der Standardwert ist 180 Tage.
    • Zugriffsschlüssel-ID: Zugriffsschlüssel des Nutzers mit Zugriff auf den S3-Bucket.
    • Secret Access Key (Geheimer Zugriffsschlüssel): Geheimer Nutzersicherheitsschlüssel mit Zugriff auf den S3-Bucket.
    • Asset-Namespace: Der Asset-Namespace.
    • Aufnahmelabels: Das Label, das auf die Ereignisse aus diesem Feed angewendet wird.
  8. Klicken Sie auf Weiter.
  9. Prüfen Sie die neue Feedkonfiguration auf dem Bildschirm Abschließen und klicken Sie dann auf Senden.

UDM-Zuordnungstabelle

Logfeld UDM-Zuordnung Logik
Aktion metadata.product_event_type Das Rohlogfeld „action“ wird diesem UDM-Feld zugeordnet.
category_type additional.fields.key Das Rohlogfeld „category_type“ wird diesem UDM-Feld zugeordnet.
category_type additional.fields.value.string_value Das Rohlogfeld „category_type“ wird diesem UDM-Feld zugeordnet.
Abteilung target.user.department Das Rohlogfeld „Department“ (aus dem Feld „operation_detail“ extrahiert) wird diesem UDM-Feld zugeordnet.
Beschreibung target.user.role_description Das Rohlogfeld „Description“ (aus dem Feld „operation_detail“ extrahiert) wird diesem UDM-Feld zugeordnet.
Anzeigename target.user.user_display_name Das Rohlogfeld „Display Name“ (aus dem Feld „operation_detail“ extrahiert) wird diesem UDM-Feld zugeordnet.
E-Mail-Adresse target.user.email_addresses Das Rohlogfeld „E-Mail-Adresse“ (aus dem Feld „operation_detail“ extrahiert) wird diesem UDM-Feld zugeordnet.
Vorname target.user.first_name Das Rohlogfeld „Vorname“ (aus dem Feld „operation_detail“ extrahiert) wird diesem UDM-Feld zugeordnet.
Position target.user.title Das Rohlogfeld „Job Title“ (aus dem Feld „operation_detail“ extrahiert) wird diesem UDM-Feld zugeordnet.
Nachname target.user.last_name Das Rohlogfeld „Last Name“ (aus dem Feld „operation_detail“ extrahiert) wird diesem UDM-Feld zugeordnet.
Standort target.location.name Das Rohlogfeld „Location“ (aus dem Feld „operation_detail“ extrahiert) wird diesem UDM-Feld zugeordnet.
operation_detail metadata.description Das Rohlogfeld „operation_detail“ wird diesem UDM-Feld zugeordnet.
Operator principal.user.email_addresses Das Rohlogfeld „operator“ wird diesem UDM-Feld zugeordnet, wenn es mit einem E-Mail-regulären Ausdruck übereinstimmt.
Operator principal.user.userid Das Rohlogfeld „operator“ wird diesem UDM-Feld zugeordnet, wenn es nicht mit einem E-Mail-regulären Ausdruck übereinstimmt.
Room Name target.user.attribute.labels.value Das Rohlogfeld „Room Name“ (aus dem Feld „operation_detail“ extrahiert) wird diesem UDM-Feld zugeordnet.
Rollenname target.user.attribute.roles.name Das Rohlogfeld „Role Name“ (aus dem Feld „operation_detail“ extrahiert) wird diesem UDM-Feld zugeordnet.
Zeit metadata.event_timestamp.seconds Das Rohlogfeld „time“ wird geparst und diesem UDM-Feld zugeordnet.
Typ target.user.attribute.labels.value Das Rohlogfeld „Type“ (aus dem Feld „operation_detail“ extrahiert) wird diesem UDM-Feld zugeordnet.
Nutzerrolle target.user.attribute.roles.name Das Rohlogfeld „User Role“ (aus dem Feld „operation_detail“ extrahiert) wird diesem UDM-Feld zugeordnet.
Nutzertyp target.user.attribute.labels.value Das Rohlogfeld „User Type“ (aus dem Feld „operation_detail“ extrahiert) wird diesem UDM-Feld zugeordnet.
metadata.log_type Diesem UDM-Feld wird der Wert „ZOOM_OPERATION_LOGS“ zugewiesen.
metadata.vendor_name Diesem UDM-Feld wird der Wert „ZOOM“ zugewiesen.
metadata.product_name Diesem UDM-Feld wird der Wert „ZOOM_OPERATION_LOGS“ zugewiesen.
metadata.event_type Der Wert wird anhand der folgenden Logik bestimmt:
1. Wenn das Feld „event_type“ nicht leer ist, wird sein Wert verwendet.
2. Wenn die Felder „operator“, „email“ oder „email2“ nicht leer sind, wird der Wert auf „USER_UNCATEGORIZED“ gesetzt.
3. Andernfalls wird der Wert auf „GENERIC_EVENT“ gesetzt.
json_data about.user.attribute.labels.value Das Rohlogfeld „json_data“ (aus dem Feld „operation_detail“ extrahiert) wird als JSON geparst. Die Felder „assistant“ und „options“ aus jedem Element des geparsten JSON-Arrays werden dem Feld „value“ des Arrays „labels“ im UDM zugeordnet.
json_data about.user.userid Das Rohlogfeld „json_data“ (aus dem Feld „operation_detail“ extrahiert) wird als JSON geparst. Das Feld „userId“ aus jedem Element des geparsten JSON-Arrays (mit Ausnahme des ersten) wird dem Feld „userid“ des Objekts „about.user“ im UDM zugeordnet.
json_data target.user.attribute.labels.value Das Rohlogfeld „json_data“ (aus dem Feld „operation_detail“ extrahiert) wird als JSON geparst. Die Felder „assistant“ und „options“ aus dem ersten Element des geparsten JSON-Arrays werden dem Feld „value“ des Arrays „labels“ im UDM zugeordnet.
json_data target.user.userid Das Rohlogfeld „json_data“ (aus dem Feld „operation_detail“ extrahiert) wird als JSON geparst. Das Feld „userId“ aus dem ersten Element des geparsten JSON-Arrays wird dem Feld „userid“ des Objekts „target.user“ im UDM zugeordnet.
E-Mail target.user.email_addresses Das Rohlogfeld „email“ (aus dem Feld „operation_detail“ extrahiert) wird diesem UDM-Feld zugeordnet.
email2 target.user.email_addresses Das Rohlogfeld „email2“ (aus dem Feld „operation_detail“ extrahiert) wird diesem UDM-Feld zugeordnet.
Rolle target.user.attribute.roles.name Das Rohlogfeld „role“ (aus dem Feld „operation_detail“ extrahiert) wird diesem UDM-Feld zugeordnet.

Benötigen Sie weitere Hilfe? Antworten von Community-Mitgliedern und Google SecOps-Experten erhalten