Coletar registros do Symantec WSS

Compatível com:

Este documento explica como ingerir registros do Symantec Web Security Service (WSS) no Google Security Operations usando o Amazon S3. Primeiro, o analisador tenta analisar a mensagem de registro como JSON. Se isso não funcionar, ele usará uma série de padrões grok cada vez mais específicos para extrair campos do texto bruto e, por fim, mapear os dados extraídos para o modelo de dados unificado (UDM, na sigla em inglês).

Antes de começar

Verifique se você tem os pré-requisitos a seguir:

  • Uma instância do Google SecOps.
  • Acesso privilegiado ao Symantec Web Security Service.
  • Acesso privilegiado à AWS (S3, Identity and Access Management (IAM), Lambda, EventBridge).

Coletar os pré-requisitos do Symantec WSS (IDs, chaves de API, IDs da organização, tokens)

  1. Faça login no portal do Symantec Web Security Service como administrador.
  2. Acesse Conta > Credenciais da API.
  3. Clique em Adicionar.
  4. Informe os seguintes detalhes de configuração:
    • Nome da API: digite um nome descritivo (por exemplo, Google SecOps Integration).
    • Descrição: insira uma descrição para as credenciais da API.
  5. Clique em Salvar e copie as credenciais de API geradas com segurança.
  6. Registre o URL do portal do WSS e o endpoint da API de sincronização.
  7. Copie e salve em um local seguro os seguintes detalhes:
    • WSS_API_USERNAME.
    • WSS_API_PASSWORD.
    • WSS_SYNC_URL.

Configurar o bucket do AWS S3 e o IAM para o Google SecOps

  1. Crie um bucket do Amazon S3 seguindo este guia do usuário: Como criar um bucket
  2. Salve o Nome e a Região do bucket para referência futura (por exemplo, symantec-wss-logs).
  3. Crie um usuário seguindo este guia: Como criar um usuário do IAM.
  4. Selecione o usuário criado.
  5. Selecione a guia Credenciais de segurança.
  6. Clique em Criar chave de acesso na seção Chaves de acesso.
  7. Selecione Serviço de terceiros como Caso de uso.
  8. Clique em Próxima.
  9. Opcional: adicione uma tag de descrição.
  10. Clique em Criar chave de acesso.
  11. Clique em Fazer o download do arquivo CSV para salvar a chave de acesso e a chave de acesso secreta para referência futura.
  12. Clique em Concluído.
  13. Selecione a guia Permissões.
  14. Clique em Adicionar permissões na seção Políticas de permissões.
  15. Selecione Adicionar permissões.
  16. Selecione Anexar políticas diretamente.
  17. Pesquise a política AmazonS3FullAccess.
  18. Selecione a política.
  19. Clique em Próxima.
  20. Clique em Adicionar permissões

Configurar a política e o papel do IAM para uploads do S3

  1. No console da AWS, acesse IAM > Políticas.
  2. Clique em Criar política > guia JSON.
  3. Copie e cole a política a seguir.
  4. JSON da política (substitua symantec-wss-logs se você tiver inserido um nome de bucket diferente):

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "AllowPutObjects",
          "Effect": "Allow",
          "Action": "s3:PutObject",
          "Resource": "arn:aws:s3:::symantec-wss-logs/*"
        },
        {
          "Sid": "AllowGetStateObject",
          "Effect": "Allow",
          "Action": "s3:GetObject",
          "Resource": "arn:aws:s3:::symantec-wss-logs/symantec/wss/state.json"
        }
      ]
    }
    
  5. Clique em Próxima > Criar política.

  6. Acesse IAM > Funções > Criar função > Serviço da AWS > Lambda.

  7. Anexe a política recém-criada.

  8. Nomeie a função como SymantecWssToS3Role e clique em Criar função.

Criar a função Lambda

  1. No console da AWS, acesse Lambda > Functions > Create function.
  2. Clique em Criar do zero.
  3. Informe os seguintes detalhes de configuração:

    Configuração Valor
    Nome symantec_wss_to_s3
    Ambiente de execução Python 3.13
    Arquitetura x86_64
    Função de execução SymantecWssToS3Role
  4. Depois que a função for criada, abra a guia Código, exclua o stub e cole o código a seguir (symantec_wss_to_s3.py).

    #!/usr/bin/env python3
    # Lambda: Pull Symantec WSS logs and store raw payloads to S3
    # - Time window via millisecond timestamps for WSS Sync API.
    # - Preserves vendor-native format (CSV/JSON/ZIP).
    # - Retries with exponential backoff; unique S3 keys to avoid overwrites.
    
    import os, json, time, uuid
    from urllib.request import Request, urlopen
    from urllib.error import URLError, HTTPError
    
    import boto3
    
    S3_BUCKET   = os.environ["S3_BUCKET"]
    S3_PREFIX   = os.environ.get("S3_PREFIX", "symantec/wss/")
    STATE_KEY   = os.environ.get("STATE_KEY", "symantec/wss/state.json")
    WINDOW_SEC  = int(os.environ.get("WINDOW_SECONDS", "3600"))  # default 1h
    HTTP_TIMEOUT= int(os.environ.get("HTTP_TIMEOUT", "60"))
    WSS_SYNC_URL = os.environ.get("WSS_SYNC_URL", "https://portal.threatpulse.com/reportpod/logs/sync")
    API_USERNAME = os.environ["WSS_API_USERNAME"]
    API_PASSWORD = os.environ["WSS_API_PASSWORD"]
    TOKEN_PARAM  = os.environ.get("WSS_TOKEN_PARAM", "none")
    MAX_RETRIES = int(os.environ.get("MAX_RETRIES", "3"))
    USER_AGENT  = os.environ.get("USER_AGENT", "symantec-wss-to-s3/1.0")
    
    s3 = boto3.client("s3")
    
    def _load_state():
        try:
            obj = s3.get_object(Bucket=S3_BUCKET, Key=STATE_KEY)
            return json.loads(obj["Body"].read())
        except Exception:
            return {}
    
    def _save_state(st):
        s3.put_object(
            Bucket=S3_BUCKET,
            Key=STATE_KEY,
            Body=json.dumps(st, separators=(",", ":")).encode("utf-8"),
            ContentType="application/json",
        )
    
    def _ms_timestamp(ts: float) -> int:
        """Convert Unix timestamp to milliseconds for WSS API"""
        return int(ts * 1000)
    
    def _fetch_wss_logs(start_ms: int, end_ms: int) -> tuple[bytes, str, str]:
        # WSS Sync API parameters
        params = f"startDate={start_ms}&endDate={end_ms}&token={TOKEN_PARAM}"
        url = f"{WSS_SYNC_URL}?{params}"
    
        attempt = 0
        while True:
            req = Request(url, method="GET")
            req.add_header("User-Agent", USER_AGENT)
            req.add_header("X-APIUsername", API_USERNAME)
            req.add_header("X-APIPassword", API_PASSWORD)
    
            try:
                with urlopen(req, timeout=HTTP_TIMEOUT) as r:
                    blob = r.read()
                    content_type = r.headers.get("Content-Type", "application/octet-stream")
                    content_encoding = r.headers.get("Content-Encoding", "")
                    return blob, content_type, content_encoding
            except (HTTPError, URLError) as e:
                attempt += 1
                print(f"HTTP error on attempt {attempt}: {e}")
                if attempt > MAX_RETRIES:
                    raise
                # exponential backoff with jitter
                time.sleep(min(60, 2 ** attempt) + (time.time() % 1))
    
    def _determine_extension(content_type: str, content_encoding: str) -> str:
        """Determine file extension based on content type and encoding"""
        if "zip" in content_type.lower():
            return ".zip"
        if "gzip" in content_type.lower() or content_encoding.lower() == "gzip":
            return ".gz"
        if "json" in content_type.lower():
            return ".json"
        if "csv" in content_type.lower():
            return ".csv"
        return ".bin"
    
    def _put_wss_data(blob: bytes, content_type: str, content_encoding: str, from_ts: float, to_ts: float) -> str:
        # Create unique S3 key for WSS data
        ts_path = time.strftime("%Y/%m/%d", time.gmtime(to_ts))
        uniq = f"{int(time.time()*1e6)}_{uuid.uuid4().hex[:8]}"
        ext = _determine_extension(content_type, content_encoding)
        key = f"{S3_PREFIX}{ts_path}/symantec_wss_{int(from_ts)}_{int(to_ts)}_{uniq}{ext}"
    
        s3.put_object(
            Bucket=S3_BUCKET, 
            Key=key, 
            Body=blob, 
            ContentType=content_type,
            Metadata={
                'source': 'symantec-wss',
                'from_timestamp': str(int(from_ts)),
                'to_timestamp': str(int(to_ts)),
                'content_encoding': content_encoding
            }
        )
        return key
    
    def lambda_handler(event=None, context=None):
        st = _load_state()
        now = time.time()
        from_ts = float(st.get("last_to_ts") or (now - WINDOW_SEC))
        to_ts = now
    
        # Convert to milliseconds for WSS API
        start_ms = _ms_timestamp(from_ts)
        end_ms = _ms_timestamp(to_ts)
    
        print(f"Fetching Symantec WSS logs from {start_ms} to {end_ms}")
    
        blob, content_type, content_encoding = _fetch_wss_logs(start_ms, end_ms)
    
        print(f"Retrieved {len(blob)} bytes with content-type: {content_type}")
        if content_encoding:
            print(f"Content encoding: {content_encoding}")
    
        key = _put_wss_data(blob, content_type, content_encoding, from_ts, to_ts)
    
        st["last_to_ts"] = to_ts
        st["last_successful_run"] = now
        _save_state(st)
    
        return {
            "statusCode": 200,
            "body": {
                "success": True, 
                "s3_key": key, 
                "content_type": content_type,
                "content_encoding": content_encoding,
                "from_timestamp": from_ts,
                "to_timestamp": to_ts,
                "bytes_retrieved": len(blob)
            }
        }
    
    if __name__ == "__main__":
        print(lambda_handler())
    
  5. Acesse Configuração > Variáveis de ambiente.

  6. Clique em Editar > Adicionar nova variável de ambiente.

  7. Insira as variáveis de ambiente fornecidas na tabela a seguir, substituindo os valores de exemplo pelos seus valores.

    Variáveis de ambiente

    Chave Valor de exemplo
    S3_BUCKET symantec-wss-logs
    S3_PREFIX symantec/wss/
    STATE_KEY symantec/wss/state.json
    WINDOW_SECONDS 3600
    HTTP_TIMEOUT 60
    MAX_RETRIES 3
    USER_AGENT symantec-wss-to-s3/1.0
    WSS_SYNC_URL https://portal.threatpulse.com/reportpod/logs/sync
    WSS_API_USERNAME your-api-username (da etapa 2)
    WSS_API_PASSWORD your-api-password (da etapa 2)
    WSS_TOKEN_PARAM none
  8. Depois que a função for criada, permaneça na página dela ou abra Lambda > Functions > sua-função.

  9. Selecione a guia Configuração.

  10. No painel Configuração geral, clique em Editar.

  11. Mude Tempo limite para 5 minutos (300 segundos) e clique em Salvar.

Criar uma programação do EventBridge

  1. Acesse Amazon EventBridge > Scheduler > Criar programação.
  2. Informe os seguintes detalhes de configuração:
    • Programação recorrente: Taxa (1 hour).
    • Destino: sua função Lambda symantec_wss_to_s3.
    • Nome: symantec-wss-1h.
  3. Clique em Criar programação.

(Opcional) Criar um usuário e chaves do IAM somente leitura para o Google SecOps

  1. No console da AWS, acesse IAM > Usuários.
  2. Clique em Add users.
  3. Informe os seguintes detalhes de configuração:
    • Usuário: insira secops-reader.
    • Tipo de acesso: selecione Chave de acesso – Acesso programático.
  4. Clique em Criar usuário.
  5. Anexe a política de leitura mínima (personalizada): Usuários > secops-reader > Permissões > Adicionar permissões > Anexar políticas diretamente > Criar política.
  6. JSON:

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": ["s3:GetObject"],
          "Resource": "arn:aws:s3:::symantec-wss-logs/*"
        },
        {
          "Effect": "Allow",
          "Action": ["s3:ListBucket"],
          "Resource": "arn:aws:s3:::symantec-wss-logs"
        }
      ]
    }
    
  7. Name = secops-reader-policy.

  8. Clique em Criar política > pesquisar/selecionar > Próxima > Adicionar permissões.

  9. Crie uma chave de acesso para secops-reader: Credenciais de segurança > Chaves de acesso.

  10. Clique em Criar chave de acesso.

  11. Baixe o CSV. Cole esses valores no feed.

Configurar um feed no Google SecOps para ingerir registros do Symantec WSS

  1. Acesse Configurações do SIEM > Feeds.
  2. Clique em + Adicionar novo feed.
  3. No campo Nome do feed, insira um nome para o feed (por exemplo, Symantec WSS logs).
  4. Selecione Amazon S3 V2 como o Tipo de origem.
  5. Selecione Symantec WSS como o Tipo de registro.
  6. Clique em Próxima.
  7. Especifique valores para os seguintes parâmetros de entrada:
    • URI do S3: s3://symantec-wss-logs/symantec/wss/
    • Opções de exclusão de fontes: selecione a opção de exclusão de acordo com sua preferência.
    • Idade máxima do arquivo: inclui arquivos modificados no último número de dias. O padrão é de 180 dias.
    • ID da chave de acesso: chave de acesso do usuário com acesso ao bucket do S3.
    • Chave de acesso secreta: chave secreta do usuário com acesso ao bucket do S3.
    • Namespace do recurso: o namespace do recurso.
    • Rótulos de ingestão: o rótulo aplicado aos eventos deste feed.
  8. Clique em Próxima.
  9. Revise a nova configuração do feed na tela Finalizar e clique em Enviar.

Tabela de mapeamento do UDM

Campo de registro Mapeamento do UDM Lógica
category_id read_only_udm.metadata.product_event_type Se category_id for 1, read_only_udm.metadata.product_event_type será definido como Security. Se category_id for 5, read_only_udm.metadata.product_event_type será definido como Policy.
collector_device_ip read_only_udm.principal.ip, read_only_udm.principal.asset.ip Valor do campo "collector_device_ip"
connection.bytes_download read_only_udm.network.received_bytes Valor do campo "connection.bytes_download" convertido em número inteiro.
connection.bytes_upload read_only_udm.network.sent_bytes Valor do campo "connection.bytes_upload" convertido em número inteiro.
connection.dst_ip read_only_udm.target.ip Valor do campo "connection.dst_ip"
connection.dst_location.country read_only_udm.target.location.country_or_region Valor do campo "connection.dst_location.country"
connection.dst_name read_only_udm.target.hostname Valor do campo "connection.dst_name"
connection.dst_port read_only_udm.target.port Valor do campo "connection.dst_port" convertido em número inteiro
connection.http_status read_only_udm.network.http.response_code Valor do campo "connection.http_status" convertido em número inteiro
connection.http_user_agent read_only_udm.network.http.user_agent Valor do campo "connection.http_user_agent".
connection.src_ip read_only_udm.principal.ip, read_only_udm.src.ip Valor do campo "connection.src_ip". Se src_ip ou collector_device_ip não estiver vazio, ele será mapeado para read_only_udm.src.ip
connection.tls.version read_only_udm.network.tls.version_protocol Valor do campo "connection.tls.version"
connection.url.host read_only_udm.target.hostname Valor do campo "connection.url.host"
connection.url.method read_only_udm.network.http.method Valor do campo "connection.url.method"
connection.url.path read_only_udm.target.url Valor do campo "connection.url.path"
connection.url.text read_only_udm.target.url Valor do campo "connection.url.text"
cs_connection_negotiated_cipher read_only_udm.network.tls.cipher Valor do campo cs_connection_negotiated_cipher
cs_icap_status read_only_udm.security_result.description Valor do campo cs_icap_status
device_id read_only_udm.target.resource.id, read_only_udm.target.resource.product_object_id Valor do campo device_id
device_ip read_only_udm.intermediary.ip, read_only_udm.intermediary.asset.ip Valor do campo "device_ip"
device_time read_only_udm.metadata.collected_timestamp, read_only_udm.metadata.event_timestamp Valor do campo "device_time" convertido em string. Se "when" estiver vazio, ele será mapeado para "read_only_udm.metadata.event_timestamp".
nome do host read_only_udm.principal.hostname, read_only_udm.principal.asset.hostname Valor do campo de nome do host
log_time read_only_udm.metadata.event_timestamp Valor do campo "log_time" convertido em carimbo de data/hora. Se "when" e "device_time" estiverem vazios, o mapeamento será para "read_only_udm.metadata.event_timestamp".
msg_desc read_only_udm.metadata.description Valor do campo msg_desc
os_details read_only_udm.target.asset.platform_software.platform, read_only_udm.target.asset.platform_software.platform_version Valor do campo "os_details". Se os_details não estiver vazio, ele será analisado para extrair os_name e os_ver. Se os_name contiver Windows, read_only_udm.target.asset.platform_software.platform será definido como WINDOWS. os_ver é mapeado para read_only_udm.target.asset.platform_software.platform_version
product_data.cs(Referer) read_only_udm.network.http.referral_url Valor do campo product_data.cs(Referer)
product_data.r-supplier-country read_only_udm.principal.location.country_or_region Valor do campo product_data.r-supplier-country
product_data.s-supplier-ip read_only_udm.intermediary.ip, read_only_udm.intermediary.asset.ip Valor do campo product_data.s-supplier-ip
product_data.x-bluecoat-application-name read_only_udm.target.application Valor do campo "product_data.x-bluecoat-application-name"
product_data.x-bluecoat-transaction-uuid read_only_udm.metadata.product_log_id Valor do campo "product_data.x-bluecoat-transaction-uuid".
product_data.x-client-agent-sw read_only_udm.observer.platform_version Valor do campo product_data.x-client-agent-sw
product_data.x-client-agent-type read_only_udm.observer.application Valor do campo product_data.x-client-agent-type
product_data.x-client-device-id read_only_udm.target.resource.type, read_only_udm.target.resource.id, read_only_udm.target.resource.product_object_id Se não estiver vazio, read_only_udm.target.resource.type será definido como DEVICE. O valor do campo "product_data.x-client-device-id" é mapeado para "read_only_udm.target.resource.id" e "read_only_udm.target.resource.product_object_id".
product_data.x-client-device-name read_only_udm.src.hostname, read_only_udm.src.asset.hostname Valor do campo product_data.x-client-device-name
product_data.x-cs-client-ip-country read_only_udm.target.location.country_or_region Valor do campo product_data.x-cs-client-ip-country
product_data.x-cs-connection-negotiated-cipher read_only_udm.network.tls.cipher Valor do campo "product_data.x-cs-connection-negotiated-cipher".
product_data.x-cs-connection-negotiated-ssl-version read_only_udm.network.tls.version_protocol Valor do campo "product_data.x-cs-connection-negotiated-ssl-version".
product_data.x-exception-id read_only_udm.security_result.summary Valor do campo product_data.x-exception-id
product_data.x-rs-certificate-hostname read_only_udm.network.tls.client.server_name Valor do campo product_data.x-rs-certificate-hostname
product_data.x-rs-certificate-hostname-categories read_only_udm.security_result.category_details Valor do campo product_data.x-rs-certificate-hostname-categories
product_data.x-rs-certificate-observed-errors read_only_udm.network.tls.server.certificate.issuer Valor do campo product_data.x-rs-certificate-observed-errors
product_data.x-rs-certificate-validate-status read_only_udm.network.tls.server.certificate.subject Valor do campo product_data.x-rs-certificate-validate-status
product_name read_only_udm.metadata.product_name Valor do campo "product_name"
product_ver read_only_udm.metadata.product_version Valor do campo "product_ver"
proxy_connection.src_ip read_only_udm.intermediary.ip, read_only_udm.intermediary.asset.ip Valor do campo proxy_connection.src_ip
received_bytes read_only_udm.network.received_bytes Valor do campo "received_bytes" convertido em número inteiro
ref_uid read_only_udm.metadata.product_log_id Valor do campo ref_uid
s_action read_only_udm.metadata.description Valor do campo s_action
sent_bytes read_only_udm.network.sent_bytes Valor do campo "sent_bytes" convertido em número inteiro
severity_id read_only_udm.security_result.severity Se severity_id for 1 ou 2, read_only_udm.security_result.severity será definido como LOW. Se severity_id for 3 ou 4, read_only_udm.security_result.severity será definido como MEDIUM. Se severity_id for 5 ou 6, read_only_udm.security_result.severity será definido como HIGH.
supplier_country read_only_udm.principal.location.country_or_region Valor do campo "supplier_country"
target_ip read_only_udm.target.ip, read_only_udm.target.asset.ip Valor do campo "target_ip"
user.full_name read_only_udm.principal.user.user_display_name Valor do campo user.full_name
user.name read_only_udm.principal.user.user_display_name Valor do campo user.name
user_name read_only_udm.principal.user.user_display_name Valor do campo "user_name"
uuid read_only_udm.metadata.product_log_id Valor do campo UUID
quando read_only_udm.metadata.event_timestamp Valor de "when" convertido em carimbo de data/hora
read_only_udm.metadata.event_type Definido como NETWORK_UNCATEGORIZED se o nome do host estiver vazio e connection.dst_ip não estiver. Definido como SCAN_NETWORK se o nome de host não estiver vazio. Defina como NETWORK_CONNECTION se has_principal e has_target forem true. Defina como STATUS_UPDATE se "has_principal" for true e "has_target" for false. Defina como GENERIC_EVENT se has_principal e has_target forem false.
read_only_udm.metadata.log_type Sempre definido como SYMANTEC_WSS
read_only_udm.metadata.vendor_name Sempre definido como SYMANTEC
read_only_udm.security_result.action Defina como ALLOW se product_data.sc-filter_result for OBSERVED ou PROXIED. Defina como BLOCK se product_data.sc-filter_result for DENIED
read_only_udm.security_result.action_details Valor do campo product_data.sc-filter_result
read_only_udm.target.resource.type Defina como DEVICE se product_data.x-client-device-id não estiver vazio

Precisa de mais ajuda? Receba respostas de membros da comunidade e profissionais do Google SecOps.