Recolha registos do WAF da Signal Sciences
Este documento explica como carregar registos do WAF da Signal Sciences para o Google Security Operations através do Google Cloud armazenamento. O analisador transforma os registos da Signal Sciences do respetivo formato JSON no modelo de dados unificado (UDM) do Chronicle. Processa duas estruturas de mensagens principais: as mensagens "RPC.PreRequest/PostRequest" são analisadas através de padrões Grok, enquanto outras mensagens são processadas como objetos JSON, extraindo campos relevantes e mapeando-os para o esquema UDM.
Antes de começar
Certifique-se de que tem os seguintes pré-requisitos:
- Instância do Google SecOps
- O VPC Flow está configurado e ativo no seu Google Cloud ambiente
- Acesso privilegiado à WAF da Signal Sciences
Crie um Google Cloud contentor de armazenamento
- Inicie sessão na Google Cloud consola.
Aceda à página Contentores do Cloud Storage.
Clique em Criar.
Na página Criar um depósito, introduza as informações do depósito. Após cada um dos passos seguintes, clique em Continuar para avançar para o passo seguinte:
Na secção Começar, faça o seguinte:
- Introduza um nome exclusivo que cumpra os requisitos do nome do contentor (por exemplo, vpcflow-logs).
- Para ativar o espaço de nomes hierárquico, clique na seta de expansão para expandir a secção Otimizar para cargas de trabalho orientadas para ficheiros e com grande volume de dados e, de seguida, selecione Ativar espaço de nomes hierárquico neste contentor.
- Para adicionar uma etiqueta de grupo, clique na seta de expansão para expandir a secção Etiquetas.
- Clique em Adicionar etiqueta e especifique uma chave e um valor para a etiqueta.
Na secção Escolha onde quer armazenar os seus dados, faça o seguinte:
- Selecione um Tipo de localização.
- Use o menu do tipo de localização para selecionar uma Localização onde os dados de objetos no seu contentor vão ser armazenados permanentemente.
- Para configurar a replicação entre contentores, expanda a secção Configurar replicação entre contentores.
Na secção Escolha uma classe de armazenamento para os seus dados, selecione uma classe de armazenamento predefinida para o contentor ou selecione Autoclass para a gestão automática da classe de armazenamento dos dados do seu contentor.
Na secção Escolha como controlar o acesso a objetos, selecione não para aplicar a prevenção de acesso público e selecione um modelo de controlo de acesso para os objetos do seu contentor.
Na secção Escolha como proteger os dados de objetos, faça o seguinte:
- Selecione qualquer uma das opções em Proteção de dados que quer definir para o seu contentor.
- Para escolher como os dados de objetos vão ser encriptados, clique na seta de expansão com a etiqueta Encriptação de dados e selecione um Método de encriptação de dados.
Clique em Criar.
Configure uma chave da API WAF da Signal Sciences
- Inicie sessão na IU Web da Signal Sciences WAF.
- Aceda a O meu perfil > Tokens de acesso à API.
- Clique em Adicionar token de acesso à API.
- Indique um nome descritivo e exclusivo (por exemplo,
Google SecOps
). - Clique em Criar token de acesso à API.
- Copie e guarde o token num local seguro.
- Clique em Compreendo para concluir a criação do token.
Implemente um script num anfitrião Linux para extrair registos do Signal Sciences e armazená-los no Google Cloud
- Inicie sessão no anfitrião Linux através do SSH.
Instale a biblioteca Python para armazenar o JSON do WAF do Signal Sciences num contentor do Cloud Storage:
pip install google-cloud-storage
Defina esta variável de ambiente para chamar o ficheiro JSON que tem as credenciais de Google Cloud:
export GOOGLE_APPLICATION_CREDENTIALS="path/to/your/service-account-key.json"
Configure as seguintes variáveis de ambiente, uma vez que estas informações não podem ser codificadas:
export SIGSCI_EMAIL=<Signal_Sciences_account_email> export SIGSCI_TOKEN=<Signal_Sciences_API_token> export SIGSCI_CORP=<Corporation_name_in_Signal_Sciences>
Execute o seguinte guião:
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 = 'user@domain.com' # 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)
Configure feeds
Para configurar um feed, siga estes passos:
- Aceda a Definições do SIEM > Feeds.
- Clique em Adicionar novo feed.
- Na página seguinte, clique em Configurar um único feed.
- No campo Nome do feed, introduza um nome para o feed (por exemplo,
Signal Sciences WAF Logs
). - Selecione Google Cloud Storage como o Tipo de origem.
- Selecione Signal Sciences WAF como o Tipo de registo.
- Clique em Obter conta de serviço como a conta de serviço do Chronicle.
- Clicar em Seguinte.
Especifique valores para os seguintes parâmetros de entrada:
- URI do contentor de armazenamento: Google Cloud URL do contentor de armazenamento no formato
gs://my-bucket/<value>
. - O URI é um: selecione Diretório que inclui subdiretórios.
Opções de eliminação de origens: selecione a opção de eliminação de acordo com a sua preferência.
- URI do contentor de armazenamento: Google Cloud URL do contentor de armazenamento no formato
Clicar em Seguinte.
Reveja a nova configuração do feed no ecrã Finalizar e, de seguida, clique em Enviar.
Tabela de mapeamento do UDM
Campo de registo | Mapeamento de UDM | Lógica |
---|---|---|
CLIENT-IP | target.ip | Extraído do campo de cabeçalho CLIENT-IP . |
CLIENT-IP | target.port | Extraído do campo de cabeçalho CLIENT-IP . |
Connection | security_result.about.labels | O valor é retirado do campo Connection do registo não processado e mapeado para security_result.about.labels . |
Content-Length | security_result.about.labels | O valor é retirado do campo Content-Length do registo não processado e mapeado para security_result.about.labels . |
Tipo de conteúdo | security_result.about.labels | O valor é retirado do campo Content-Type do registo não processado e mapeado para security_result.about.labels . |
criado | metadata.event_timestamp | O valor é retirado do campo created do registo não processado e mapeado para metadata.event_timestamp . |
details.headersIn | security_result.about.resource.attribute.labels | O valor é retirado do campo details.headersIn do registo não processado e mapeado para security_result.about.resource.attribute.labels . |
details.headersOut | security_result.about.resource.attribute.labels | O valor é retirado do campo details.headersOut do registo não processado e mapeado para security_result.about.resource.attribute.labels . |
details.id | principal.process.pid | O valor é retirado do campo details.id do registo não processado e mapeado para principal.process.pid . |
details.method | network.http.method | O valor é retirado do campo details.method do registo não processado e mapeado para network.http.method . |
details.protocol | network.application_protocol | O valor é retirado do campo details.protocol do registo não processado e mapeado para network.application_protocol . |
details.remoteCountryCode | principal.location.country_or_region | O valor é retirado do campo details.remoteCountryCode do registo não processado e mapeado para principal.location.country_or_region . |
details.remoteHostname | target.hostname | O valor é retirado do campo details.remoteHostname do registo não processado e mapeado para target.hostname . |
details.remoteIP | target.ip | O valor é retirado do campo details.remoteIP do registo não processado e mapeado para target.ip . |
details.responseCode | network.http.response_code | O valor é retirado do campo details.responseCode do registo não processado e mapeado para network.http.response_code . |
details.responseSize | network.received_bytes | O valor é retirado do campo details.responseSize do registo não processado e mapeado para network.received_bytes . |
details.serverHostname | principal.hostname | O valor é retirado do campo details.serverHostname do registo não processado e mapeado para principal.hostname . |
details.serverName | principal.asset.network_domain | O valor é retirado do campo details.serverName do registo não processado e mapeado para principal.asset.network_domain . |
details.tags | security_result.detection_fields | O valor é retirado do campo details.tags do registo não processado e mapeado para security_result.detection_fields . |
details.tlsCipher | network.tls.cipher | O valor é retirado do campo details.tlsCipher do registo não processado e mapeado para network.tls.cipher . |
details.tlsProtocol | network.tls.version | O valor é retirado do campo details.tlsProtocol do registo não processado e mapeado para network.tls.version . |
details.userAgent | network.http.user_agent | O valor é retirado do campo details.userAgent do registo não processado e mapeado para network.http.user_agent . |
details.uri | network.http.referral_url | O valor é retirado do campo details.uri do registo não processado e mapeado para network.http.referral_url . |
eventType | metadata.product_event_type | O valor é retirado do campo eventType do registo não processado e mapeado para metadata.product_event_type . |
headersIn | security_result.about.labels | O valor é retirado do campo headersIn do registo não processado e mapeado para security_result.about.labels . |
headersOut | security_result.about.labels | O valor é retirado do campo headersOut do registo não processado e mapeado para security_result.about.labels . |
id | principal.process.pid | O valor é retirado do campo id do registo não processado e mapeado para principal.process.pid . |
mensagem | metadata.description | O valor é retirado do campo message do registo não processado e mapeado para metadata.description . |
método | network.http.method | O valor é retirado do campo method do registo não processado e mapeado para network.http.method . |
ModuleVersion | metadata.ingestion_labels | O valor é retirado do campo ModuleVersion do registo não processado e mapeado para metadata.ingestion_labels . |
msgData.actions | security_result.action | O valor é retirado do campo msgData.actions do registo não processado e mapeado para security_result.action . |
msgData.changes | target.resource.attribute.labels | O valor é retirado do campo msgData.changes do registo não processado e mapeado para target.resource.attribute.labels . |
msgData.conditions | security_result.description | O valor é retirado do campo msgData.conditions do registo não processado e mapeado para security_result.description . |
msgData.detailLink | network.http.referral_url | O valor é retirado do campo msgData.detailLink do registo não processado e mapeado para network.http.referral_url . |
msgData.name | target.resource.name | O valor é retirado do campo msgData.name do registo não processado e mapeado para target.resource.name . |
msgData.reason | security_result.summary | O valor é retirado do campo msgData.reason do registo não processado e mapeado para security_result.summary . |
msgData.sites | network.http.user_agent | O valor é retirado do campo msgData.sites do registo não processado e mapeado para network.http.user_agent . |
protocolo | network.application_protocol | O valor é retirado do campo protocol do registo não processado e mapeado para network.application_protocol . |
remoteCountryCode | principal.location.country_or_region | O valor é retirado do campo remoteCountryCode do registo não processado e mapeado para principal.location.country_or_region . |
remoteHostname | target.hostname | O valor é retirado do campo remoteHostname do registo não processado e mapeado para target.hostname . |
remoteIP | target.ip | O valor é retirado do campo remoteIP do registo não processado e mapeado para target.ip . |
responseCode | network.http.response_code | O valor é retirado do campo responseCode do registo não processado e mapeado para network.http.response_code . |
responseSize | network.received_bytes | O valor é retirado do campo responseSize do registo não processado e mapeado para network.received_bytes . |
serverHostname | principal.hostname | O valor é retirado do campo serverHostname do registo não processado e mapeado para principal.hostname . |
serverName | principal.asset.network_domain | O valor é retirado do campo serverName do registo não processado e mapeado para principal.asset.network_domain . |
etiquetas | security_result.detection_fields | O valor é retirado do campo tags do registo não processado e mapeado para security_result.detection_fields . |
timestamp | metadata.event_timestamp | O valor é retirado do campo timestamp do registo não processado e mapeado para metadata.event_timestamp . |
tlsCipher | network.tls.cipher | O valor é retirado do campo tlsCipher do registo não processado e mapeado para network.tls.cipher . |
tlsProtocol | network.tls.version | O valor é retirado do campo tlsProtocol do registo não processado e mapeado para network.tls.version . |
URI | target.url | O valor é retirado do campo URI do registo não processado e mapeado para target.url . |
userAgent | network.http.user_agent | O valor é retirado do campo userAgent do registo não processado e mapeado para network.http.user_agent . |
uri | network.http.referral_url | O valor é retirado do campo uri do registo não processado e mapeado para network.http.referral_url . |
X-ARR-SSL | network.tls.client.certificate.issuer | O valor é extraído do campo do cabeçalho X-ARR-SSL através de filtros grok e kv. |
metadata.event_type | O tipo de evento é determinado pelo analisador com base na presença de informações principais e de destino. Se o destino e o principal estiverem presentes, o tipo de evento é NETWORK_HTTP . Se apenas o principal estiver presente, o tipo de evento é STATUS_UPDATE . Caso contrário, o tipo de evento é GENERIC_EVENT . |
|
metadata.log_type | O valor está codificado como SIGNAL_SCIENCES_WAF . |
|
metadata.product_name | O valor está codificado como Signal Sciences WAF . |
|
metadata.vendor_name | O valor está codificado como Signal Sciences . |
|
principal.asset.hostname | O valor é retirado do campo principal.hostname . |
|
target.asset.hostname | O valor é retirado do campo target.hostname . |
|
target.asset.ip | O valor é retirado do campo target.ip . |
|
target.user.user_display_name | O valor é extraído do campo message_data através de um filtro grok. |
|
target.user.userid | O valor é extraído do campo message_data através de um filtro grok. |
Precisa de mais ajuda? Receba respostas de membros da comunidade e profissionais da Google SecOps.