Recolha registos de auditoria do Workday

Compatível com:

Este documento explica como carregar registos de auditoria do Workday para o Google Security Operations através do AWS S3. O analisador identifica primeiro o tipo de evento específico nos registos com base na análise de padrões dos dados CSV. Em seguida, extrai e estrutura os campos relevantes de acordo com o tipo identificado, mapeando-os para um modelo de dados unificado (UDM) para uma análise de segurança consistente.

Antes de começar

Certifique-se de que tem os seguintes pré-requisitos:

  • Instância do Google SecOps
  • Acesso privilegiado ao AWS
  • Acesso privilegiado ao Workday

Configure o contentor do AWS S3 e o IAM para o Google SecOps

  1. Crie um contentor do Amazon S3 seguindo este manual do utilizador: Criar um contentor.
  2. Guarde o nome e a região do contentor para referência futura (por exemplo, workday-audit-logs).
  3. Crie um utilizador seguindo este guia do utilizador: criar um utilizador do IAM.
  4. Selecione o utilizador criado.
  5. Selecione o separador Credenciais de segurança.
  6. Clique em Criar chave de acesso na secção Chaves de acesso.
  7. Selecione Serviço de terceiros como Exemplo de utilização.
  8. Clicar em Seguinte.
  9. Opcional: adicione a etiqueta de descrição.
  10. Clique em Criar chave de acesso.
  11. Clique em Transferir ficheiro CSV para guardar a chave de acesso e a chave de acesso secreta para referência futura.
  12. Clique em Concluído.
  13. Selecione o separador Autorizações.
  14. Clique em Adicionar autorizações na secção Políticas de autorizações.
  15. Selecione Adicionar autorizações.
  16. Selecione Anexar políticas diretamente.
  17. Pesquise e selecione a política AmazonS3FullAccess.
  18. Clicar em Seguinte.
  19. Clique em Adicionar autorizações.

Crie um utilizador do sistema de integração (ISU) do Workday

  1. No Workday, pesquise Create Integration System User > OK.
  2. Preencha o campo Nome de utilizador (por exemplo, audit_s3_user).
  3. Clique em OK.
  4. Reponha a palavra-passe acedendo a Ações relacionadas > Segurança > Repor palavra-passe.
  5. Selecione Manter regras de palavras-passe para evitar que a palavra-passe expire.
  6. Pesquise Criar grupo de segurança > Grupo de segurança do sistema de integração (sem restrições).
  7. Indique um nome (por exemplo, ISU_Audit_S3) e adicione o ISU a Integration System Users.
  8. Pesquise Políticas de segurança de domínio para a área funcional > Sistema.
  9. Para Rasto de auditoria, selecione Ações > Editar autorizações.
  10. Em Obter apenas, adicione o grupo ISU_Audit_S3.
  11. Clique em OK > Ativar alterações pendentes da política de segurança.

Configure o relatório personalizado do Workday

  1. No Workday, pesquise Create Custom Report.
  2. Indique os seguintes detalhes de configuração:
    • Nome: introduza um nome exclusivo (por exemplo, Audit_Trail_BP_JSON).
    • Tipo: selecione Avançadas.
    • Origem de dados: selecione Rasto de auditoria – Processo empresarial.
    • Clique em OK.
    • Opcional: adicione filtros em Tipo de processo empresarial ou Data de entrada em vigor.
  3. Aceda ao separador Saída.
  4. Selecione Ativar como serviço Web, Otimizado para o desempenho e selecione Formato JSON.
  5. Clique em OK > Concluído.
  6. Abra o relatório e clique em Partilhar > adicione ISU_Audit_S3 com autorização de visualização > OK.
  7. Aceda a Ações relacionadas > Serviço Web > Ver URLs.
  8. Copie o URL JSON (por exemplo, https://wd-services1.workday.com/ccx/service/customreport2/<tenant>/<user>/Audit_Trail_BP_JSON?format=json).

Configure a política e a função de IAM para carregamentos do S3

  1. JSON da política (substitua workday-audit-logs se tiver introduzido um nome de contentor diferente):

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "AllowPutWorkdayObjects",
          "Effect": "Allow",
          "Action": "s3:PutObject",
          "Resource": "arn:aws:s3:::workday-audit-logs/*"
        }
      ]
    }
    
  2. Aceda a AWS console > IAM > Policies > Create policy > separador JSON.

  3. Copie e cole a política.

  4. Clique em Seguinte > Criar política.

  5. Aceda a IAM > Funções > Criar função > Serviço AWS > Lambda.

  6. Anexe a política criada recentemente.

  7. Dê o nome WriteWorkdayToS3Role à função e clique em Criar função.

Crie a função Lambda

Definição Valor
Nome workday_audit_to_s3
Runtime Python 3.13
Arquitetura x86_64
Função de execução WriteWorkdayToS3Role
  1. Depois de criar a função, abra o separador Código, elimine o fragmento de código e cole o código abaixo (workday_audit_to_s3.py).

    #!/usr/bin/env python3
    
    import os, json, gzip, io, uuid, base64, datetime as dt, urllib.request, urllib.error
    import boto3
    
    WD_USER   = os.environ["WD_USER"]
    WD_PASS   = os.environ["WD_PASS"]
    WD_URL    = os.environ["WD_URL"]
    S3_BUCKET = os.environ["S3_BUCKET_NAME"]
    
    def fetch_report() -> bytes:
        credentials = f"{WD_USER}:{WD_PASS}".encode()
        auth_header = b"Basic " + base64.b64encode(credentials)
        req = urllib.request.Request(WD_URL, headers={"Authorization": auth_header.decode()})
        with urllib.request.urlopen(req, timeout=30) as r:
            return r.read()  # raw JSON bytes
    
    def upload(payload: bytes, ts: dt.datetime) -> None:
        key = f"{ts:%Y/%m/%d}/workday-audit-{uuid.uuid4()}.json.gz"
        buf = io.BytesIO()
        with gzip.GzipFile(fileobj=buf, mode="w") as gz:
            gz.write(payload)
        buf.seek(0)
        boto3.client("s3").upload_fileobj(buf, S3_BUCKET, key)
    
    def lambda_handler(event=None, context=None):
        now = dt.datetime.utcnow().replace(microsecond=0)
        data = fetch_report()
        upload(data, now)
        print(f"Uploaded Workday audit report ({len(data)} bytes raw)")
    
    if __name__ == "__main__":
        lambda_handler()
    
  2. Aceda a Configuração > Variáveis de ambiente > Editar > Adicionar nova variável de ambiente.

  3. Introduza as seguintes variáveis de ambiente, substituindo-as pelo seu valor.

    Variáveis de ambiente

    Chave Exemplos de valores
    WD_USER audit_s3_user
    WD_PASS Wrokday-Password
    WD_URL https://.../Audit_Trail_BP_JSON?format=json
    S3_BUCKET_NAME workday-audit-logs
  4. Depois de criar a função, permaneça na respetiva página (ou abra Lambda > Functions > your‑function).

  5. Selecione o separador Configuração.

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

  7. Altere Tempo limite para 5 minutos (300 segundos) e clique em Guardar.

Agende a função Lambda (EventBridge Scheduler)

  1. Aceda a Configuration > Triggers > Add trigger > EventBridge Scheduler > Create rule.
  2. Indique os seguintes detalhes de configuração:
    • Nome: daily-workday-audit export.
    • Padrão de agendamento: expressão cron.
    • Expressão: 20 2 * * ? * (executado diariamente às 02:20 UTC).
  3. Deixe o resto como predefinição e clique em Criar.

Configure um feed no Google SecOps para carregar registos de auditoria do Workday

  1. Aceda a Definições do SIEM > Feeds.
  2. Clique em + Adicionar novo feed.
  3. No campo Nome do feed, introduza um nome para o feed (por exemplo, Workday Audit Logs).
  4. Selecione Amazon S3 V2 como o Tipo de origem.
  5. Selecione Workday Audit como o Tipo de registo.
  6. Clique em Obter uma conta de serviço.
  7. Clicar em Seguinte.
  8. Especifique valores para os seguintes parâmetros de entrada:
    • URI do S3: o URI do contentor
      • s3://workday-audit-logs/.
        • Substitua workday-audit-logs pelo nome real do contentor.
    • Opções de eliminação de origens: selecione a opção de eliminação de acordo com a sua preferência.
    • Idade máxima do ficheiro: inclua ficheiros modificados no último número de dias. A predefinição é 180 dias.
    • ID da chave de acesso: chave de acesso do utilizador com acesso ao contentor do S3.
    • Chave de acesso secreta: chave secreta do utilizador com acesso ao contentor do S3.
    • Espaço de nomes do recurso: o espaço de nomes do recurso.
    • Etiquetas de carregamento: a etiqueta a aplicar aos eventos deste feed.
  9. Clicar em Seguinte.
  10. Reveja a nova configuração do feed no ecrã Finalizar e, de seguida, clique em Enviar.

Tabela de mapeamento da UDM

Campo de registo Mapeamento de UDM Lógica
Account metadata.event_type Se o campo "Account" não estiver vazio, o campo "metadata.event_type" é definido como "USER_RESOURCE_UPDATE_CONTENT".
Account principal.user.primaryId O ID do utilizador é extraído do campo "Conta" através de um padrão grok e mapeado para principal.user.primaryId.
Account principal.user.primaryName O nome a apresentar do utilizador é extraído do campo "Conta" através de um padrão grok e mapeado para "principal.user.primaryName".
ActivityCategory metadata.event_type Se o campo "ActivityCategory" for "READ", o campo "metadata.event_type" é definido como "RESOURCE_READ". Se for "WRITE", é definido como "RESOURCE_WRITTEN".
ActivityCategory metadata.product_event_type Mapeado diretamente a partir do campo "ActivityCategory".
AffectedGroups target.user.group_identifiers Mapeado diretamente a partir do campo "AffectedGroups".
Area target.resource.attribute.labels.area.value Mapeado diretamente a partir do campo "Área".
AuthType extensions.auth.auth_details Mapeado diretamente a partir do campo "AuthType".
AuthType extensions.auth.type Mapeado do campo "AuthType" para diferentes tipos de autenticação definidos no UDM com base em valores específicos.
CFIPdeConexion src.domain.name Se o campo "CFIPdeConexion" não for um endereço IP válido, é mapeado para "src.domain.name".
CFIPdeConexion target.ip Se o campo "CFIPdeConexion" for um endereço IP válido, é mapeado para "target.ip".
ChangedRelationship metadata.description Mapeado diretamente a partir do campo "ChangedRelationship".
ClassOfInstance target.resource.attribute.labels.class_instance.value Mapeado diretamente a partir do campo "ClassOfInstance".
column18 about.labels.utub.value Mapeado diretamente a partir do campo "column18".
CreatedBy principal.user.userid O userid é extraído do campo "CreatedBy" através de um padrão grok e mapeado para "principal.user.userid".
CreatedBy principal.user.user_display_name O nome a apresentar do utilizador é extraído do campo "CreatedBy" através de um padrão grok e mapeado para "principal.user.user_display_name".
Domain about.domain.name Mapeado diretamente a partir do campo "Domínio".
EffectiveDate @timestamp Analisado para "@timestamp" após a conversão para o formato "aaaa-MM-dd HH:mm:ss.SSSZ".
EntryMoment @timestamp Analisado para "@timestamp" após a conversão para o formato "ISO8601".
EventType security_result.description Mapeado diretamente a partir do campo "EventType".
Form target.resource.name Mapeado diretamente a partir do campo "Formulário".
InstancesAdded about.resource.attribute.labels.instances_added.value Mapeado diretamente a partir do campo "InstancesAdded".
InstancesAdded target.user.attribute.roles.instances_added.name Mapeado diretamente a partir do campo "InstancesAdded".
InstancesRemoved about.resource.attribute.labels.instances_removed.value Mapeado diretamente a partir do campo "InstancesRemoved".
InstancesRemoved target.user.attribute.roles.instances_removed.name Mapeado diretamente a partir do campo "InstancesRemoved".
IntegrationEvent target.resource.attribute.labels.integration_event.value Mapeado diretamente a partir do campo "IntegrationEvent".
IntegrationStatus security_result.action_details Mapeado diretamente a partir do campo "IntegrationStatus".
IntegrationSystem target.resource.name Mapeado diretamente a partir do campo "IntegrationSystem".
IP src.domain.name Se o campo "IP" não for um endereço IP válido, é mapeado para "src.domain.name".
IP src.ip Se o campo "IP" for um endereço IP válido, é mapeado para "src.ip".
IsDeviceManaged additional.fields.additional1.value.string_value Se o campo "IsDeviceManaged" for "N", o valor é definido como "Successful". Caso contrário, é definido como "Ocorreu uma tentativa de início de sessão falhada".
IsDeviceManaged additional.fields.additional2.value.string_value Se o campo "IsDeviceManaged" for "N", o valor é definido como "Successful". Caso contrário, é definido como "Credenciais inválidas".
IsDeviceManaged additional.fields.additional3.value.string_value Se o campo "IsDeviceManaged" for "N", o valor é definido como "Successful". Caso contrário, é definido como "Conta bloqueada".
IsDeviceManaged security_result.action_details Mapeado diretamente a partir do campo "IsDeviceManaged".
OutputFiles about.file.full_path Mapeado diretamente a partir do campo "OutputFiles".
Person principal.user.primaryId Se o campo "Person" começar por "INT", o userid é extraído através de um padrão grok e mapeado para "principal.user.primaryId".
Person principal.user.primaryName Se o campo "Person" começar por "INT", o nome a apresentar do utilizador é extraído através de um padrão grok e mapeado para "principal.user.primaryName".
Person principal.user.user_display_name Se o campo "Person" não começar por "INT", é mapeado diretamente para "principal.user.user_display_name".
Person metadata.event_type Se o campo "Person" não estiver vazio, o campo "metadata.event_type" é definido como "USER_RESOURCE_UPDATE_CONTENT".
ProcessedTransaction target.resource.attribute.creation_time Analisado para "target.resource.attribute.creation_time" após a conversão para o formato "dd/MM/aaaa HH:mm:ss,SSS (ZZZ)", "dd/MM/aaaa, HH:mm:ss,SSS (ZZZ)" ou "MM/dd/aaaa, HH:mm:ss.SSS A ZZZ".
ProgramBy principal.user.userid Mapeado diretamente a partir do campo "ProgramBy".
RecurrenceEndDate principal.resource.attribute.last_update_time Analisado para "principal.resource.attribute.last_update_time" após a conversão para o formato "aaaa-MM-dd".
RecurrenceStartDate principal.resource.attribute.creation_time Analisado para "principal.resource.attribute.creation_time" após a conversão para o formato "aaaa-MM-dd".
RequestName metadata.description Mapeado diretamente a partir do campo "RequestName".
ResponseMessage security_result.summary Mapeado diretamente a partir do campo "ResponseMessage".
RestrictedToEnvironment security_result.about.hostname Mapeado diretamente a partir do campo "RestrictedToEnvironment".
RevokedSecurity security_result.outcomes.outcomes.value Mapeado diretamente a partir do campo "RevokedSecurity".
RunFrequency principal.resource.attribute.labels.run_frequency.value Mapeado diretamente a partir do campo "RunFrequency".
ScheduledProcess principal.resource.name Mapeado diretamente a partir do campo "ScheduledProcess".
SecuredTaskExecuted target.resource.name Mapeado diretamente a partir do campo "SecuredTaskExecuted".
SecureTaskExecuted metadata.event_type Se o campo "SecureTaskExecuted" contiver "Create", o campo "metadata.event_type" é definido como "USER_RESOURCE_CREATION".
SecureTaskExecuted target.resource.name Mapeado diretamente a partir do campo "SecureTaskExecuted".
SentTime @timestamp Analisado para "@timestamp" após a conversão para o formato "ISO8601".
SessionId network.session_id Mapeado diretamente a partir do campo "SessionId".
ShareBy target.user.userid Mapeado diretamente a partir do campo "ShareBy".
SignOffTime additional.fields.additional4.value.string_value O valor do campo "AuthFailMessage" é colocado na matriz "additional.fields" com a chave "Enterprise Interface Builder".
SignOffTime metadata.description Mapeado diretamente a partir do campo "AuthFailMessage".
SignOffTime metadata.event_type Se o campo "SignOffTime" estiver vazio, o campo "metadata.event_type" é definido como "USER_LOGIN". Caso contrário, é definido como "USER_LOGOUT".
SignOffTime principal.user.attribute.last_update_time Analisado para "principal.user.attribute.last_update_time" após a conversão para o formato "ISO8601".
SignOnIp src.domain.name Se o campo "SignOnIp" não for um endereço IP válido, é mapeado para "src.domain.name".
SignOnIp src.ip Se o campo "SignOnIp" for um endereço IP válido, é mapeado para "src.ip".
Status metadata.product_event_type Mapeado diretamente a partir do campo "Estado".
SystemAccount principal.user.email_addresses O endereço de email é extraído do campo "SystemAccount" através de um padrão grok e mapeado para "principal.user.email_addresses".
SystemAccount principal.user.primaryId O ID do utilizador é extraído do campo "SystemAccount" através de um padrão grok e mapeado para "principal.user.primaryId".
SystemAccount principal.user.primaryName O nome a apresentar do utilizador é extraído do campo "SystemAccount" através de um padrão grok e mapeado para "principal.user.primaryName".
SystemAccount src.user.userid O ID do utilizador secundário é extraído do campo "SystemAccount" através de um padrão grok e mapeado para "src.user.userid".
SystemAccount src.user.user_display_name O nome a apresentar do utilizador secundário é extraído do campo "SystemAccount" através de um padrão grok e mapeado para "src.user.user_display_name".
SystemAccount target.user.userid O ID do utilizador de destino é extraído do campo "SystemAccount" através de um padrão grok e mapeado para "target.user.userid".
Target target.user.user_display_name Mapeado diretamente a partir do campo "Destino".
Template about.resource.name Mapeado diretamente a partir do campo "Modelo".
Tenant target.asset.hostname Mapeado diretamente a partir do campo "Inquilino".
TlsVersion network.tls.version Mapeado diretamente a partir do campo "TlsVersion".
Transaction security_result.action_details Mapeado diretamente a partir do campo "Transação".
TransactionType security_result.summary Mapeado diretamente a partir do campo "TransactionType".
TypeForm target.resource.resource_subtype Mapeado diretamente a partir do campo "TypeForm".
UserAgent network.http.parsed_user_agent Analisado a partir do campo "UserAgent" através do filtro "useragent".
UserAgent network.http.user_agent Mapeado diretamente a partir do campo "UserAgent".
WorkdayAccount target.user.user_display_name O nome a apresentar do utilizador é extraído do campo "WorkdayAccount" através de um padrão grok e mapeado para "target.user.user_display_name".
WorkdayAccount target.user.userid O ID do utilizador é extraído do campo "WorkdayAccount" através de um padrão grok e mapeado para "target.user.userid".
additional.fields.additional1.key Definido como "FailedSignOn".
additional.fields.additional2.key Definido como "InvalidCredentials".
additional.fields.additional3.key Definido como "AccountLocked".
additional.fields.additional4.key Definido como "Enterprise Interface Builder".
metadata.event_type Inicialmente, definido como "GENERIC_EVENT" e, em seguida, atualizado com base na lógica que envolve outros campos.
metadata.event_type Definido como "USER_CHANGE_PERMISSIONS" para tipos de eventos específicos.
metadata.event_type Definido como "RESOURCE_WRITTEN" para tipos de eventos específicos.
metadata.log_type Codificado de forma rígida para "WORKDAY_AUDIT".
metadata.product_name Codificado de forma rígida para "Enterprise Interface Builder".
metadata.vendor_name Codificado como "Workday".
principal.asset.category Definido como "Phone" se o campo "DeviceType" for "Phone".
principal.resource.resource_type Codificado como "TASK" se o campo "ScheduledProcess" não estiver vazio.
security_result.action Definido como "ALLOW" ou "FAIL" com base nos valores dos campos "FailedSignOn", "IsDeviceManaged", "InvalidCredentials" e "AccountLocked".
security_result.summary Definido como "Successful" ou mensagens de erro específicas com base nos valores dos campos "FailedSignOn", "IsDeviceManaged", "InvalidCredentials" e "AccountLocked".
target.resource.resource_type Codificado de forma rígida como "TASK" para tipos de eventos específicos.
target.resource.resource_type Codificado como "DATASET" se o campo "TypeForm" não estiver vazio.
message principal.user.email_addresses Extrai o endereço de email do campo "message" através de um padrão grok e junta-o a "principal.user.email_addresses" se for encontrada uma correspondência com um padrão específico.
message src.user.userid Limpa o campo se o campo "event.idm.read_only_udm.principal.user.userid" corresponder ao "user_target" extraído do campo "message".
message src.user.user_display_name Limpa o campo se o campo "event.idm.read_only_udm.principal.user.userid" corresponder ao "user_target" extraído do campo "message".
message target.user.userid Extrai o userid do campo "message" através de um padrão grok e mapeia-o para "target.user.userid" se for encontrada uma correspondência com um padrão específico.

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