Collecter les journaux du WAF Signal Sciences
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
- Connectez-vous à la console Google Cloud .
Accédez à la page Buckets Cloud Storage.
Cliquez sur Créer.
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:
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é.
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.
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.
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.
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.
Cliquez sur Créer.
Configurer une clé API WAF Signal Sciences
- Connectez-vous à l'interface utilisateur Web du WAF Signal Sciences.
- Accédez à My Profile > API Access Tokens (Mon profil > Jetons d'accès API).
- Cliquez sur Ajouter un jeton d'accès à l'API.
- Indiquez un nom unique et descriptif (par exemple,
Google SecOps
). - Cliquez sur Créer un jeton d'accès à l'API.
- Copiez et enregistrez le jeton dans un emplacement sécurisé.
- 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
- Connectez-vous à l'hôte Linux à l'aide de SSH.
Installez la bibliothèque Python pour stocker le fichier JSON du WAF Signal Sciences dans un bucket Cloud Storage:
pip install google-cloud-storage
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"
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>
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:
- Accédez à Paramètres du SIEM > Flux.
- Cliquez sur Ajouter un flux.
- Sur la page suivante, cliquez sur Configurer un flux.
- Dans le champ Nom du flux, saisissez un nom pour le flux (par exemple,
Signal Sciences WAF Logs
). - Sélectionnez Google Cloud Storage comme Type de source.
- Sélectionnez Signal Sciences WAF comme type de journal.
- Cliquez sur Obtenir un compte de service comme compte de service Chronicle.
- Cliquez sur Suivant.
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.
- URI du bucket Storage: Google Cloud URL du bucket Storage au format
Cliquez sur Suivant.
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.