Collecter les journaux du WAF Signal Sciences

Compatible avec:

Ce document explique comment ingérer les journaux du WAF Signal Sciences dans Google Security Operations à l'aide de Google Cloud Storage. L'analyseur transforme les journaux Signal Sciences au format JSON en modèle de données unifié (UDM) de Chronicle. Il gère deux structures de messages principales : les messages "RPC.PreRequest/PostRequest" sont analysés à l'aide de modèles Grok, tandis que les autres messages sont traités en tant qu'objets JSON, en extrayant les champs pertinents et en les mappant sur le schéma UDM.

Avant de commencer

Assurez-vous de remplir les conditions suivantes :

  • Instance Google SecOps
  • Le flux VPC est configuré et actif dans votre environnement Google Cloud
  • Accès privilégié au WAF Signal Sciences

Créer un Google Cloud bucket de stockage

  1. Connectez-vous à la console Google Cloud .
  2. Accédez à la page Buckets Cloud Storage.

    Accéder à la page "Buckets"

  3. Cliquez sur Créer.

  4. Sur la page Créer un bucket, saisissez les informations concernant votre bucket. Après chacune de ces étapes, cliquez sur Continuer pour passer à l'étape suivante:

    1. Dans la section Premiers pas, procédez comme suit :

      • Saisissez un nom unique qui répond aux exigences de dénomination des buckets (par exemple, vpcflow-logs).
      • Pour activer l'espace de noms hiérarchique, cliquez sur la flèche d'expansion pour développer la section Optimiser pour les charges de travail orientées fichiers et à forte intensité de données, puis sélectionnez Activer l'espace de noms hiérarchique sur ce bucket.
      • Pour ajouter une étiquette de bucket, cliquez sur la flèche de développement pour développer la section Étiquettes.
      • Cliquez sur Ajouter un libellé, puis spécifiez une clé et une valeur pour votre libellé.
    2. Dans la section Choisir l'emplacement de stockage de vos données, procédez comme suit :

      • Sélectionnez un type d'emplacement.
      • Utilisez le menu du type d'emplacement pour sélectionner un emplacement où les données d'objets de votre bucket seront stockées de manière permanente.
      • Pour configurer la réplication entre buckets, développez la section Configurer la réplication entre buckets.
    3. Dans la section Choisir une classe de stockage pour vos données, sélectionnez une classe de stockage par défaut pour le bucket, ou bien classe automatique pour une gestion automatique des classes de stockage des données de votre bucket.

    4. Dans la section Choisir comment contrôler l'accès aux objets, sélectionnez non pour appliquer la protection contre l'accès public, puis sélectionnez un modèle de contrôle des accès pour les objets de votre bucket.

    5. Dans la section Choisir comment protéger les données d'objet, procédez comme suit:

      • Sélectionnez l'une des options sous Protection des données que vous souhaitez définir pour votre bucket.
      • Pour choisir comment vos données d'objet seront chiffrées, cliquez sur la flèche d'expansion intitulée Chiffrement des données, puis sélectionnez une méthode de chiffrement des données.
  5. Cliquez sur Créer.

Configurer une clé API WAF Signal Sciences

  1. Connectez-vous à l'interface utilisateur Web du WAF Signal Sciences.
  2. Accédez à My Profile > API Access Tokens (Mon profil > Jetons d'accès API).
  3. Cliquez sur Ajouter un jeton d'accès à l'API.
  4. Indiquez un nom unique et descriptif (par exemple, Google SecOps).
  5. Cliquez sur Créer un jeton d'accès à l'API.
  6. Copiez et enregistrez le jeton dans un emplacement sécurisé.
  7. Cliquez sur Je comprends pour terminer de créer le jeton.

Déployez un script sur un hôte Linux pour extraire les journaux de Signal Sciences et les stocker dans Google Cloud

  1. Connectez-vous à l'hôte Linux à l'aide de SSH.
  2. Installez la bibliothèque Python pour stocker le fichier JSON du WAF Signal Sciences dans un bucket Cloud Storage:

    pip install google-cloud-storage
    
  3. Définissez cette variable d'environnement pour appeler le fichier JSON contenant les identifiants à partir de Google Cloud:

    export GOOGLE_APPLICATION_CREDENTIALS="path/to/your/service-account-key.json"
    
  4. Configurez les variables d'environnement suivantes, car ces informations ne doivent pas être codées en dur:

    export SIGSCI_EMAIL=<Signal_Sciences_account_email>
    export SIGSCI_TOKEN=<Signal_Sciences_API_token>
    export SIGSCI_CORP=<Corporation_name_in_Signal_Sciences>
    
  5. Exécutez le script suivant :

    import sys
    import requests
    import os
    import calendar
    import json
    from datetime import datetime, timedelta
    from google.cloud import storage
    
    # Check if all necessary environment variables are set
    
    if 'SIGSCI_EMAIL' not in os.environ or 'SIGSCI_TOKEN' not in os.environ or 'SIGSCI_CORP' not in os.environ:
    print("ERROR: You need to define SIGSCI_EMAIL, SIGSCI_TOKEN, and SIGSCI_CORP environment variables.")
    print("Please fix and run again. Existing...")
    sys.exit(1)  # Exit if environment variables are not set
    
    # Define the Google Cloud Storage bucket name and output file name
    
    bucket_name = 'Your_GCS_Bucket'  # Replace with your GCS bucket name
    output_file_name = 'signal_sciences_logs.json'
    
    # Initialize Google Cloud Storage client
    
    storage_client = storage.Client()
    
    # Function to upload data to Google Cloud Storage
    
    def upload_to_gcs(bucket_name, data, destination_blob_name):
        bucket = storage_client.bucket(bucket_name)
        blob = bucket.blob(destination_blob_name)
        blob.upload_from_string(data, content_type='application/json')
        print(f"Data uploaded to {destination_blob_name} in bucket {bucket_name}")
    
    # Signal Sciences API information
    
    api_host = 'https://dashboard.signalsciences.net'
    # email = '[email protected]'  # Signal Sciences account email
    # token = 'XXXXXXXX-XXXX-XXX-XXXX-XXXXXXXXXXXX'  # API token for authentication
    # corp_name = 'Domain'  # Corporation name in Signal Sciences
    # site_names = ['testenv']  # Replace with your actual site names
    
    # List of comma-delimited sites that you want to extract data from
    
    site_names = [ 'site123', 'site345' ]        # Define all sites to pull logs from
    
    email = os.environ.get('SIGSCI_EMAIL')       # Signal Sciences account email
    token = os.environ.get('SIGSCI_TOKEN')       # API token for authentication
    corp_name = os.environ.get('SIGSCI_CORP')    # Corporation name in Signal Sciences
    
    # Calculate the start and end timestamps for the previous hour in UTC
    
    until_time = datetime.utcnow().replace(minute=0, second=0, microsecond=0)
    from_time = until_time - timedelta(hours=1)
    until_time = calendar.timegm(until_time.utctimetuple())
    from_time = calendar.timegm(from_time.utctimetuple())
    
    # Prepare HTTP headers for the API request
    
    headers = {
        'Content-Type': 'application/json',
        'x-api-user': email,
        'x-api-token': token
    }
    
    # Collect logs for each site
    
    collected_logs = []
    
    for site_name in site_names:
        url = f"{api_host}/api/v0/corps/{corp_name}/sites/{site_name}/feed/requests?from={from_time}&until={until_time}"
        while True:
            response = requests.get(url, headers=headers)
            if response.status_code != 200:
                print(f"Error fetching logs: {response.text}", file=sys.stderr)
                break
    
            # Parse the JSON response
    
            data = response.json()
            collected_logs.extend(data['data'])  # Add the log messages to our list
    
            # Pagination: check if there is a next page
    
            next_url = data.get('next', {}).get('uri')
            if not next_url:
                break
            url = api_host + next_url
    
    # Convert the collected logs to a newline-delimited JSON string
    
    json_data = '\n'.join(json.dumps(log) for log in collected_logs)
    
    # Save the newline-delimited JSON data to a GCS bucket
    
    upload_to_gcs(bucket_name, json_data, output_file_name)
    

    Configurer des flux

Il existe deux points d'entrée différents pour configurer des flux dans la plate-forme Google SecOps:

  • Paramètres du SIEM > Flux
  • Hub de contenu > Packs de contenu

Configurez les flux dans Paramètres du SIEM > Flux.

Pour configurer un flux, procédez comme suit:

  1. Accédez à Paramètres du SIEM > Flux.
  2. Cliquez sur Ajouter un flux.
  3. Sur la page suivante, cliquez sur Configurer un flux.
  4. Dans le champ Nom du flux, saisissez un nom pour le flux (par exemple, Signal Sciences WAF Logs).
  5. Sélectionnez Google Cloud Storage comme Type de source.
  6. Sélectionnez Signal Sciences WAF comme type de journal.
  7. Cliquez sur Obtenir un compte de service comme compte de service Chronicle.
  8. Cliquez sur Suivant.
  9. Spécifiez les valeurs des paramètres d'entrée suivants:

    • URI du bucket Storage: Google Cloud URL du bucket Storage au format gs://my-bucket/<value>.
    • L'URI est: sélectionnez Répertoire incluant des sous-répertoires.
    • Options de suppression de la source: sélectionnez l'option de suppression en fonction de vos préférences.

  10. Cliquez sur Suivant.

  11. Vérifiez la configuration de votre nouveau flux dans l'écran Finaliser, puis cliquez sur Envoyer.

Configurer des flux depuis le Centre de contenu

Indiquez les valeurs des champs suivants:

  • URI du bucket Storage: Google Cloud URL du bucket Storage au format gs://my-bucket/<value>.
  • L'URI est: sélectionnez Répertoire incluant des sous-répertoires.
  • Options de suppression de la source: sélectionnez l'option de suppression en fonction de vos préférences.

Options avancées

  • Nom du flux: valeur préremplie qui identifie le flux.
  • Source Type (Type de source) : méthode utilisée pour collecter les journaux dans Google SecOps.
  • Espace de noms de l'asset: espace de noms associé au flux.
  • Libellés d'ingestion: libellés appliqués à tous les événements de ce flux.

Tableau de mappage UDM

Champ de journal Mappage UDM Logique
CLIENT-IP target.ip Extrait du champ d'en-tête CLIENT-IP.
CLIENT-IP target.port Extrait du champ d'en-tête CLIENT-IP.
Connexion security_result.about.labels La valeur est extraite du champ Connection du journal brut et mappée sur security_result.about.labels.
Content-Length security_result.about.labels La valeur est extraite du champ Content-Length du journal brut et mappée sur security_result.about.labels.
Content-Type security_result.about.labels La valeur est extraite du champ Content-Type du journal brut et mappée sur security_result.about.labels.
créé metadata.event_timestamp La valeur est extraite du champ created du journal brut et mappée sur metadata.event_timestamp.
details.headersIn security_result.about.resource.attribute.labels La valeur est extraite du champ details.headersIn du journal brut et mappée sur security_result.about.resource.attribute.labels.
details.headersOut security_result.about.resource.attribute.labels La valeur est extraite du champ details.headersOut du journal brut et mappée sur security_result.about.resource.attribute.labels.
details.id principal.process.pid La valeur est extraite du champ details.id du journal brut et mappée sur principal.process.pid.
details.method network.http.method La valeur est extraite du champ details.method du journal brut et mappée sur network.http.method.
details.protocol network.application_protocol La valeur est extraite du champ details.protocol du journal brut et mappée sur network.application_protocol.
details.remoteCountryCode principal.location.country_or_region La valeur est extraite du champ details.remoteCountryCode du journal brut et mappée sur principal.location.country_or_region.
details.remoteHostname target.hostname La valeur est extraite du champ details.remoteHostname du journal brut et mappée sur target.hostname.
details.remoteIP target.ip La valeur est extraite du champ details.remoteIP du journal brut et mappée sur target.ip.
details.responseCode network.http.response_code La valeur est extraite du champ details.responseCode du journal brut et mappée sur network.http.response_code.
details.responseSize network.received_bytes La valeur est extraite du champ details.responseSize du journal brut et mappée sur network.received_bytes.
details.serverHostname principal.hostname La valeur est extraite du champ details.serverHostname du journal brut et mappée sur principal.hostname.
details.serverName principal.asset.network_domain La valeur est extraite du champ details.serverName du journal brut et mappée sur principal.asset.network_domain.
details.tags security_result.detection_fields La valeur est extraite du champ details.tags du journal brut et mappée sur security_result.detection_fields.
details.tlsCipher network.tls.cipher La valeur est extraite du champ details.tlsCipher du journal brut et mappée sur network.tls.cipher.
details.tlsProtocol network.tls.version La valeur est extraite du champ details.tlsProtocol du journal brut et mappée sur network.tls.version.
details.userAgent network.http.user_agent La valeur est extraite du champ details.userAgent du journal brut et mappée sur network.http.user_agent.
details.uri network.http.referral_url La valeur est extraite du champ details.uri du journal brut et mappée sur network.http.referral_url.
eventType metadata.product_event_type La valeur est extraite du champ eventType du journal brut et mappée sur metadata.product_event_type.
headersIn security_result.about.labels La valeur est extraite du champ headersIn du journal brut et mappée sur security_result.about.labels.
headersOut security_result.about.labels La valeur est extraite du champ headersOut du journal brut et mappée sur security_result.about.labels.
id principal.process.pid La valeur est extraite du champ id du journal brut et mappée sur principal.process.pid.
message metadata.description La valeur est extraite du champ message du journal brut et mappée sur metadata.description.
méthode network.http.method La valeur est extraite du champ method du journal brut et mappée sur network.http.method.
ModuleVersion metadata.ingestion_labels La valeur est extraite du champ ModuleVersion du journal brut et mappée sur metadata.ingestion_labels.
msgData.actions security_result.action La valeur est extraite du champ msgData.actions du journal brut et mappée sur security_result.action.
msgData.changes target.resource.attribute.labels La valeur est extraite du champ msgData.changes du journal brut et mappée sur target.resource.attribute.labels.
msgData.conditions security_result.description La valeur est extraite du champ msgData.conditions du journal brut et mappée sur security_result.description.
msgData.detailLink network.http.referral_url La valeur est extraite du champ msgData.detailLink du journal brut et mappée sur network.http.referral_url.
msgData.name target.resource.name La valeur est extraite du champ msgData.name du journal brut et mappée sur target.resource.name.
msgData.reason security_result.summary La valeur est extraite du champ msgData.reason du journal brut et mappée sur security_result.summary.
msgData.sites network.http.user_agent La valeur est extraite du champ msgData.sites du journal brut et mappée sur network.http.user_agent.
protocol network.application_protocol La valeur est extraite du champ protocol du journal brut et mappée sur network.application_protocol.
remoteCountryCode principal.location.country_or_region La valeur est extraite du champ remoteCountryCode du journal brut et mappée sur principal.location.country_or_region.
remoteHostname target.hostname La valeur est extraite du champ remoteHostname du journal brut et mappée sur target.hostname.
remoteIP target.ip La valeur est extraite du champ remoteIP du journal brut et mappée sur target.ip.
responseCode network.http.response_code La valeur est extraite du champ responseCode du journal brut et mappée sur network.http.response_code.
responseSize network.received_bytes La valeur est extraite du champ responseSize du journal brut et mappée sur network.received_bytes.
serverHostname principal.hostname La valeur est extraite du champ serverHostname du journal brut et mappée sur principal.hostname.
serverName principal.asset.network_domain La valeur est extraite du champ serverName du journal brut et mappée sur principal.asset.network_domain.
tags security_result.detection_fields La valeur est extraite du champ tags du journal brut et mappée sur security_result.detection_fields.
timestamp metadata.event_timestamp La valeur est extraite du champ timestamp du journal brut et mappée sur metadata.event_timestamp.
tlsCipher network.tls.cipher La valeur est extraite du champ tlsCipher du journal brut et mappée sur network.tls.cipher.
tlsProtocol network.tls.version La valeur est extraite du champ tlsProtocol du journal brut et mappée sur network.tls.version.
URI target.url La valeur est extraite du champ URI du journal brut et mappée sur target.url.
userAgent network.http.user_agent La valeur est extraite du champ userAgent du journal brut et mappée sur network.http.user_agent.
uri network.http.referral_url La valeur est extraite du champ uri du journal brut et mappée sur network.http.referral_url.
X-ARR-SSL network.tls.client.certificate.issuer La valeur est extraite du champ d'en-tête X-ARR-SSL à l'aide des filtres grok et kv.
metadata.event_type Le type d'événement est déterminé par l'analyseur en fonction de la présence d'informations sur la cible et le principal. Si la cible et le principal sont tous deux présents, le type d'événement est NETWORK_HTTP. Si seul le principal est présent, le type d'événement est STATUS_UPDATE. Sinon, le type d'événement est GENERIC_EVENT.
metadata.log_type La valeur est codée en dur sur SIGNAL_SCIENCES_WAF.
metadata.product_name La valeur est codée en dur sur Signal Sciences WAF.
metadata.vendor_name La valeur est codée en dur sur Signal Sciences.
principal.asset.hostname La valeur est extraite du champ principal.hostname.
target.asset.hostname La valeur est extraite du champ target.hostname.
target.asset.ip La valeur est extraite du champ target.ip.
target.user.user_display_name La valeur est extraite du champ message_data à l'aide d'un filtre grok.
target.user.userid La valeur est extraite du champ message_data à l'aide d'un filtre grok.

Vous avez encore besoin d'aide ? Obtenez des réponses de membres de la communauté et de professionnels Google SecOps.