Recopila registros de auditoría de Workday

Compatible con:

En este documento, se explica cómo transferir registros de auditoría de Workday a Google Security Operations con AWS S3. Primero, el analizador identifica el tipo de evento específico en los registros según el análisis de patrones de los datos CSV. Luego, extrae y estructura los campos pertinentes según el tipo identificado, y los asigna a un modelo de datos unificado (UDM) para realizar un análisis de seguridad coherente.

Antes de comenzar

Asegúrate de cumplir con los siguientes requisitos previos:

  • Instancia de Google SecOps
  • Acceso privilegiado a AWS
  • Acceso con privilegios a Workday

Configura el bucket de AWS S3 y el IAM para Google SecOps

  1. Crea un bucket de Amazon S3 siguiendo esta guía del usuario: Crea un bucket.
  2. Guarda el Nombre y la Región del bucket para futuras referencias (por ejemplo, workday-audit-logs).
  3. Crea un usuario siguiendo esta guía del usuario: Cómo crear un usuario de IAM.
  4. Selecciona el usuario creado.
  5. Selecciona la pestaña Credenciales de seguridad.
  6. Haz clic en Crear clave de acceso en la sección Claves de acceso.
  7. Selecciona Servicio de terceros como Caso de uso.
  8. Haz clic en Siguiente.
  9. Opcional: Agrega una etiqueta de descripción.
  10. Haz clic en Crear clave de acceso.
  11. Haz clic en Descargar archivo CSV para guardar la clave de acceso y la clave de acceso secreta para consultarlas en el futuro.
  12. Haz clic en Listo.
  13. Selecciona la pestaña Permisos.
  14. Haz clic en Agregar permisos en la sección Políticas de permisos.
  15. Selecciona Agregar permisos.
  16. Selecciona Adjuntar políticas directamente.
  17. Busca y selecciona la política AmazonS3FullAccess.
  18. Haz clic en Siguiente.
  19. Haz clic en Agregar permisos.

Crea un usuario del sistema de integración (ISU) de Workday

  1. En Workday, busca Create Integration System User > OK.
  2. Completa el Nombre de usuario (por ejemplo, audit_s3_user).
  3. Haz clic en Aceptar.
  4. Para restablecer la contraseña, ve a Acciones relacionadas > Seguridad > Restablecer contraseña.
  5. Selecciona Mantener reglas de contraseñas para evitar que la contraseña venza.
  6. Busca Create Security Group > Integration System Security Group (Unconstrained).
  7. Proporciona un nombre (por ejemplo, ISU_Audit_S3) y agrega el ISU a Integration System Users.
  8. Busca Políticas de seguridad del dominio para el área funcional > Sistema.
  9. En Registro de auditoría, selecciona Acciones > Editar permisos.
  10. En Get Only, agrega el grupo ISU_Audit_S3.
  11. Haz clic en Aceptar > Activar cambios pendientes en la política de seguridad.

Configura el informe personalizado de Workday

  1. En Workday, busca Create Custom Report.
  2. Proporciona los siguientes detalles de configuración:
    • Nombre: Ingresa un nombre único (por ejemplo, Audit_Trail_BP_JSON).
    • Tipo: Selecciona Avanzado.
    • Fuente de datos: Selecciona Registro de auditoría: Proceso comercial.
    • Haz clic en Aceptar.
    • Opcional: Agrega filtros en Tipo de proceso comercial o Fecha de entrada en vigencia.
  3. Ve a la pestaña Salida.
  4. Selecciona Enable as Web Service, Optimized for Performance y JSON Format.
  5. Haz clic en Aceptar > Listo.
  6. Abre el informe y haz clic en Compartir > agregar ISU_Audit_S3 con permiso de visualización > Aceptar.
  7. Ve a Acciones relacionadas > Servicio web > Ver URLs.
  8. Copia la URL de JSON (por ejemplo, https://wd-services1.workday.com/ccx/service/customreport2/<tenant>/<user>/Audit_Trail_BP_JSON?format=json).

Configura la política y el rol de IAM para las cargas de S3

  1. JSON de la política (reemplaza workday-audit-logs si ingresaste un nombre de bucket diferente):

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "AllowPutWorkdayObjects",
          "Effect": "Allow",
          "Action": "s3:PutObject",
          "Resource": "arn:aws:s3:::workday-audit-logs/*"
        }
      ]
    }
    
  2. Ve a la consola de AWS > IAM > Políticas > Crear política > pestaña JSON.

  3. Copia y pega la política.

  4. Haz clic en Siguiente > Crear política.

  5. Ve a IAM > Roles > Crear rol > Servicio de AWS > Lambda.

  6. Adjunta la política recién creada.

  7. Asigna el nombre WriteWorkdayToS3Role al rol y haz clic en Crear rol.

Crea la función Lambda

Configuración Valor
Nombre workday_audit_to_s3
Tiempo de ejecución Python 3.13
Arquitectura x86_64
Rol de ejecución WriteWorkdayToS3Role
  1. Después de crear la función, abre la pestaña Code, borra el código auxiliar y pega el siguiente código (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. Ve a Configuración > Variables de entorno > Editar > Agregar nueva variable de entorno.

  3. Ingresa las siguientes variables de entorno y reemplaza los marcadores de posición por tus valores.

    Variables de entorno

    Clave Valores de ejemplo
    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. Después de crear la función, permanece en su página (o abre Lambda > Functions > tu‑función).

  5. Selecciona la pestaña Configuración.

  6. En el panel Configuración general, haz clic en Editar.

  7. Cambia Tiempo de espera a 5 minutos (300 segundos) y haz clic en Guardar.

Programa la función de Lambda (EventBridge Scheduler)

  1. Ve a Configuración > Activadores > Agregar activador > EventBridge Scheduler > Crear regla.
  2. Proporciona los siguientes detalles de configuración:
    • Nombre: daily-workday-audit export.
    • Patrón de programación: Expresión cron.
    • Expresión: 20 2 * * ? * (se ejecuta a diario a las 02:20 UTC).
  3. Deja el resto con la configuración predeterminada y haz clic en Crear.

Configura un feed en Google SecOps para transferir registros de auditoría de Workday

  1. Ve a SIEM Settings > Feeds.
  2. Haz clic en + Agregar feed nuevo.
  3. En el campo Nombre del feed, ingresa un nombre para el feed (por ejemplo, Workday Audit Logs).
  4. Selecciona Amazon S3 V2 como el Tipo de fuente.
  5. Selecciona Auditoría de jornada laboral como el Tipo de registro.
  6. Haz clic en Obtener una cuenta de servicio.
  7. Haz clic en Siguiente.
  8. Especifica valores para los siguientes parámetros de entrada:
    • URI de S3: Es el URI del bucket.
      • s3://workday-audit-logs/.
        • Reemplaza workday-audit-logs por el nombre real del bucket.
    • Opciones de borrado de la fuente: Selecciona la opción de borrado según tu preferencia.
    • Antigüedad máxima del archivo: Incluye los archivos modificados en la cantidad de días especificada. El valor predeterminado es 180 días.
    • ID de clave de acceso: Clave de acceso del usuario con acceso al bucket de S3.
    • Clave de acceso secreta: Clave secreta del usuario con acceso al bucket de S3.
    • Espacio de nombres del recurso: Es el espacio de nombres del recurso.
    • Etiquetas de transmisión: Es la etiqueta que se aplicará a los eventos de este feed.
  9. Haz clic en Siguiente.
  10. Revisa la nueva configuración del feed en la pantalla Finalizar y, luego, haz clic en Enviar.

Tabla de asignación de UDM

Campo de registro Asignación de UDM Lógica
Account metadata.event_type Si el campo "Account" no está vacío, el campo "metadata.event_type" se establece en "USER_RESOURCE_UPDATE_CONTENT".
Account principal.user.primaryId El ID de usuario se extrae del campo "Cuenta" con un patrón de Grok y se asigna a principal.user.primaryId.
Account principal.user.primaryName El nombre visible del usuario se extrae del campo "Cuenta" con un patrón de grok y se asigna a "principal.user.primaryName".
ActivityCategory metadata.event_type Si el campo "ActivityCategory" es "READ", el campo "metadata.event_type" se establece en "RESOURCE_READ". Si es "WRITE", se establece en "RESOURCE_WRITTEN".
ActivityCategory metadata.product_event_type Se asigna directamente desde el campo "ActivityCategory".
AffectedGroups target.user.group_identifiers Se asigna directamente desde el campo "AffectedGroups".
Area target.resource.attribute.labels.area.value Se asigna directamente desde el campo "Área".
AuthType extensions.auth.auth_details Se asigna directamente desde el campo "AuthType".
AuthType extensions.auth.type Se asigna desde el campo "AuthType" a diferentes tipos de autenticación definidos en el UDM según valores específicos.
CFIPdeConexion src.domain.name Si el campo "CFIPdeConexion" no es una dirección IP válida, se asigna a "src.domain.name".
CFIPdeConexion target.ip Si el campo "CFIPdeConexion" es una dirección IP válida, se asigna a "target.ip".
ChangedRelationship metadata.description Se asigna directamente desde el campo "ChangedRelationship".
ClassOfInstance target.resource.attribute.labels.class_instance.value Se asigna directamente desde el campo "ClassOfInstance".
column18 about.labels.utub.value Se asigna directamente desde el campo "column18".
CreatedBy principal.user.userid El ID de usuario se extrae del campo "CreatedBy" con un patrón grok y se asigna a "principal.user.userid".
CreatedBy principal.user.user_display_name El nombre visible del usuario se extrae del campo "CreatedBy" con un patrón grok y se asigna a "principal.user.user_display_name".
Domain about.domain.name Se asigna directamente desde el campo "Dominio".
EffectiveDate @timestamp Se analizó como "@timestamp" después de convertirse al formato "aaaa-MM-dd HH:mm:ss.SSSZ".
EntryMoment @timestamp Se analizó como "@timestamp" después de la conversión al formato "ISO8601".
EventType security_result.description Se asigna directamente desde el campo "EventType".
Form target.resource.name Se asigna directamente desde el campo "Formulario".
InstancesAdded about.resource.attribute.labels.instances_added.value Se asigna directamente desde el campo "InstancesAdded".
InstancesAdded target.user.attribute.roles.instances_added.name Se asigna directamente desde el campo "InstancesAdded".
InstancesRemoved about.resource.attribute.labels.instances_removed.value Se asigna directamente desde el campo "InstancesRemoved".
InstancesRemoved target.user.attribute.roles.instances_removed.name Se asigna directamente desde el campo "InstancesRemoved".
IntegrationEvent target.resource.attribute.labels.integration_event.value Se asigna directamente desde el campo "IntegrationEvent".
IntegrationStatus security_result.action_details Se asigna directamente desde el campo "IntegrationStatus".
IntegrationSystem target.resource.name Se asigna directamente desde el campo "IntegrationSystem".
IP src.domain.name Si el campo "IP" no es una dirección IP válida, se asigna a "src.domain.name".
IP src.ip Si el campo "IP" es una dirección IP válida, se asigna a "src.ip".
IsDeviceManaged additional.fields.additional1.value.string_value Si el campo "IsDeviceManaged" es "N", el valor se establece en "Successful". De lo contrario, se establece como "Se produjo un acceso fallido".
IsDeviceManaged additional.fields.additional2.value.string_value Si el campo "IsDeviceManaged" es "N", el valor se establece en "Successful". De lo contrario, se establece como "Invalid Credentials".
IsDeviceManaged additional.fields.additional3.value.string_value Si el campo "IsDeviceManaged" es "N", el valor se establece en "Successful". De lo contrario, se establece como "Cuenta bloqueada".
IsDeviceManaged security_result.action_details Se asigna directamente desde el campo "IsDeviceManaged".
OutputFiles about.file.full_path Se asigna directamente desde el campo "OutputFiles".
Person principal.user.primaryId Si el campo "Persona" comienza con "INT", el ID de usuario se extrae con un patrón de Grok y se asigna a "principal.user.primaryId".
Person principal.user.primaryName Si el campo "Persona" comienza con "INT", el nombre visible del usuario se extrae con un patrón de Grok y se asigna a "principal.user.primaryName".
Person principal.user.user_display_name Si el campo "Persona" no comienza con "INT", se asigna directamente a "principal.user.user_display_name".
Person metadata.event_type Si el campo "Person" no está vacío, el campo "metadata.event_type" se establece en "USER_RESOURCE_UPDATE_CONTENT".
ProcessedTransaction target.resource.attribute.creation_time Se analiza como "target.resource.attribute.creation_time" después de convertirse al formato "dd/MM/aaaa HH:mm:ss,SSS (ZZZ)", "dd/MM/aaaa, HH:mm:ss,SSS (ZZZ)" o "MM/dd/aaaa, HH:mm:ss.SSS A ZZZ".
ProgramBy principal.user.userid Se asigna directamente desde el campo "ProgramBy".
RecurrenceEndDate principal.resource.attribute.last_update_time Se analizó como "principal.resource.attribute.last_update_time" después de la conversión al formato "aaaa-MM-dd".
RecurrenceStartDate principal.resource.attribute.creation_time Se analiza y se convierte a "principal.resource.attribute.creation_time" después de convertirlo al formato "aaaa-MM-dd".
RequestName metadata.description Se asigna directamente desde el campo "RequestName".
ResponseMessage security_result.summary Se asigna directamente desde el campo "ResponseMessage".
RestrictedToEnvironment security_result.about.hostname Se asigna directamente desde el campo "RestrictedToEnvironment".
RevokedSecurity security_result.outcomes.outcomes.value Se asigna directamente desde el campo "RevokedSecurity".
RunFrequency principal.resource.attribute.labels.run_frequency.value Se asigna directamente desde el campo "RunFrequency".
ScheduledProcess principal.resource.name Se asigna directamente desde el campo "ScheduledProcess".
SecuredTaskExecuted target.resource.name Se asigna directamente desde el campo "SecuredTaskExecuted".
SecureTaskExecuted metadata.event_type Si el campo "SecureTaskExecuted" contiene "Create", el campo "metadata.event_type" se establece en "USER_RESOURCE_CREATION".
SecureTaskExecuted target.resource.name Se asigna directamente desde el campo "SecureTaskExecuted".
SentTime @timestamp Se analizó como "@timestamp" después de la conversión al formato "ISO8601".
SessionId network.session_id Se asigna directamente desde el campo "SessionId".
ShareBy target.user.userid Se asigna directamente desde el campo "ShareBy".
SignOffTime additional.fields.additional4.value.string_value El valor del campo "AuthFailMessage" se coloca dentro del array "additional.fields" con la clave "Enterprise Interface Builder".
SignOffTime metadata.description Se asigna directamente desde el campo "AuthFailMessage".
SignOffTime metadata.event_type Si el campo "SignOffTime" está vacío, el campo "metadata.event_type" se establece en "USER_LOGIN". De lo contrario, se establece como "USER_LOGOUT".
SignOffTime principal.user.attribute.last_update_time Se analiza como "principal.user.attribute.last_update_time" después de convertirse al formato "ISO8601".
SignOnIp src.domain.name Si el campo "SignOnIp" no es una dirección IP válida, se asigna a "src.domain.name".
SignOnIp src.ip Si el campo "SignOnIp" es una dirección IP válida, se asigna a "src.ip".
Status metadata.product_event_type Se asigna directamente desde el campo "Estado".
SystemAccount principal.user.email_addresses La dirección de correo electrónico se extrae del campo "SystemAccount" con un patrón de Grok y se asigna a "principal.user.email_addresses".
SystemAccount principal.user.primaryId El ID de usuario se extrae del campo "SystemAccount" con un patrón grok y se asigna a "principal.user.primaryId".
SystemAccount principal.user.primaryName El nombre visible del usuario se extrae del campo "SystemAccount" con un patrón de Grok y se asigna a "principal.user.primaryName".
SystemAccount src.user.userid El ID de usuario secundario se extrae del campo "SystemAccount" con un patrón de Grok y se asigna a "src.user.userid".
SystemAccount src.user.user_display_name El nombre visible del usuario secundario se extrae del campo "SystemAccount" con un patrón de Grok y se asigna a "src.user.user_display_name".
SystemAccount target.user.userid El ID de usuario objetivo se extrae del campo "SystemAccount" con un patrón de Grok y se asigna a "target.user.userid".
Target target.user.user_display_name Se asigna directamente desde el campo "Objetivo".
Template about.resource.name Se asigna directamente desde el campo "Plantilla".
Tenant target.asset.hostname Se asigna directamente desde el campo "Tenant".
TlsVersion network.tls.version Se asigna directamente desde el campo "TlsVersion".
Transaction security_result.action_details Se asigna directamente desde el campo "Transacción".
TransactionType security_result.summary Se asigna directamente desde el campo "TransactionType".
TypeForm target.resource.resource_subtype Se asigna directamente desde el campo "TypeForm".
UserAgent network.http.parsed_user_agent Se analizó a partir del campo "UserAgent" con el filtro "useragent".
UserAgent network.http.user_agent Se asigna directamente desde el campo "UserAgent".
WorkdayAccount target.user.user_display_name El nombre visible del usuario se extrae del campo "WorkdayAccount" con un patrón de Grok y se asigna a "target.user.user_display_name".
WorkdayAccount target.user.userid El ID de usuario se extrae del campo "WorkdayAccount" con un patrón de Grok y se asigna a "target.user.userid".
additional.fields.additional1.key Se establece en "FailedSignOn".
additional.fields.additional2.key Se establece en "InvalidCredentials".
additional.fields.additional3.key Se establece en "AccountLocked".
additional.fields.additional4.key Establece el valor en "Enterprise Interface Builder".
metadata.event_type Se establece en "GENERIC_EVENT" inicialmente y, luego, se actualiza según la lógica que involucra otros campos.
metadata.event_type Se establece en "USER_CHANGE_PERMISSIONS" para tipos de eventos específicos.
metadata.event_type Se establece en "RESOURCE_WRITTEN" para tipos de eventos específicos.
metadata.log_type Se codifica como "WORKDAY_AUDIT".
metadata.product_name Está codificado como "Enterprise Interface Builder".
metadata.vendor_name Está codificado como "Día laboral".
principal.asset.category Se establece en "Teléfono" si el campo "DeviceType" es "Teléfono".
principal.resource.resource_type Se codifica como "TASK" si el campo "ScheduledProcess" no está vacío.
security_result.action Se establece en "ALLOW" o "FAIL" según los valores de los campos "FailedSignOn", "IsDeviceManaged", "InvalidCredentials" y "AccountLocked".
security_result.summary Se establece en "Correcto" o en mensajes de error específicos según los valores de los campos "FailedSignOn", "IsDeviceManaged", "InvalidCredentials" y "AccountLocked".
target.resource.resource_type Se codifica como "TASK" para tipos de eventos específicos.
target.resource.resource_type Se codifica como "DATASET" si el campo "TypeForm" no está vacío.
message principal.user.email_addresses Extrae la dirección de correo electrónico del campo "message" con un patrón de Grok y la combina en "principal.user.email_addresses" si coincide con un patrón específico.
message src.user.userid Borra el campo si el campo "event.idm.read_only_udm.principal.user.userid" coincide con el "user_target" extraído del campo "message".
message src.user.user_display_name Borra el campo si el campo "event.idm.read_only_udm.principal.user.userid" coincide con el "user_target" extraído del campo "message".
message target.user.userid Extrae el ID de usuario del campo "message" con un patrón de Grok y lo asigna a "target.user.userid" si coincide con un patrón específico.

¿Necesitas más ayuda? Obtén respuestas de miembros de la comunidad y profesionales de Google SecOps.