收集 Workday 稽核記錄
本文說明如何使用 AWS S3,將 Workday 稽核記錄擷取至 Google Security Operations。剖析器會先根據 CSV 資料的模式分析,從記錄中找出特定事件類型。然後,系統會根據識別出的類型擷取並建構相關欄位,將這些欄位對應至統一資料模型 (UDM),以進行一致的安全性分析。
事前準備
請確認您已完成下列事前準備事項:
- Google SecOps 執行個體
- AWS 的特殊存取權
- Workday 特殊存取權
為 Google SecOps 設定 AWS S3 值區和 IAM
- 按照這份使用者指南建立 Amazon S3 值區:建立值區。
- 儲存 bucket 的「名稱」和「區域」,以供日後參考 (例如
workday-audit-logs
)。 - 請按照這份使用者指南建立使用者:建立 IAM 使用者。
- 選取建立的「使用者」。
- 選取「安全憑證」分頁標籤。
- 在「Access Keys」部分中,按一下「Create Access Key」。
- 選取「第三方服務」做為「用途」。
- 點選「下一步」。
- 選用:新增說明標記。
- 按一下「建立存取金鑰」。
- 按一下「下載 CSV 檔案」,儲存「存取金鑰」和「私密存取金鑰」,以供日後參考。
- 按一下 [完成]。
- 選取「權限」分頁標籤。
- 在「權限政策」部分中,按一下「新增權限」。
- 選取「新增權限」。
- 選取「直接附加政策」。
- 搜尋並選取 AmazonS3FullAccess 政策。
- 點選「下一步」。
- 按一下「Add permissions」。
建立 Workday Integration System User (ISU)
- 在 Workday 中搜尋「Create Integration System User」(建立整合系統使用者) > 點選「OK」(確定)。
- 填入「User Name」(使用者名稱) (例如
audit_s3_user
)。 - 按一下「確定」。
- 依序前往「相關動作」>「安全性」>「重設密碼」,即可重設密碼。
- 選取「維持密碼規則」,避免密碼過期。
- 搜尋「Create Security Group」>「Integration System Security Group (Unconstrained)」。
- 提供名稱 (例如
ISU_Audit_S3
),並將 ISU 新增至「整合系統使用者」。 - 搜尋「Domain Security Policies for Functional Area > System」。
- 在「稽核記錄」部分,依序選取「動作」>「編輯權限」。
- 在「僅限取得」下方,新增
ISU_Audit_S3
群組。 - 依序點選「確定」「啟用待處理的安全政策變更」。
設定 Workday 自訂報表
- 在 Workday 中搜尋「Create Custom Report」(建立自訂報表)。
- 提供下列設定詳細資料:
- 名稱:輸入不重複的名稱 (例如
Audit_Trail_BP_JSON
)。 - 類型:選取「進階」。
- 資料來源:選取「稽核追蹤記錄 - 業務程序」。
- 按一下「確定」。
- 選用:新增「業務流程類型」或「生效日期」篩選器。
- 名稱:輸入不重複的名稱 (例如
- 前往「輸出」分頁。
- 選取「啟用為 Web 服務」和「針對效能進行最佳化」,然後選取「JSON 格式」。
- 依序按一下「確定」>「完成」。
- 開啟報表,然後依序按一下「共用」> 新增
ISU_Audit_S3
並授予「檢視」權限 >「確定」。 - 依序前往「相關動作」>「網路服務」>「查看網址」。
- 複製 JSON 網址 (例如
https://wd-services1.workday.com/ccx/service/customreport2/<tenant>/<user>/Audit_Trail_BP_JSON?format=json
)。
設定 S3 上傳的身分與存取權管理政策和角色
政策 JSON (如果您輸入的 bucket 名稱不同,請替換
workday-audit-logs
):{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPutWorkdayObjects", "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::workday-audit-logs/*" } ] }
依序前往 AWS 管理控制台 > IAM > 政策 > 建立政策 > JSON 分頁。
複製並貼上政策。
依序點選「Next」>「Create policy」。
依序前往「IAM」>「Roles」>「Create role」>「AWS service」>「Lambda」。
附加新建立的政策。
為角色命名
WriteWorkdayToS3Role
,然後按一下「建立角色」。
建立 Lambda 函式
設定 | 值 |
---|---|
名稱 | workday_audit_to_s3 |
執行階段 | Python 3.13 |
架構 | x86_64 |
執行角色 | WriteWorkdayToS3Role |
建立函式後,開啟「程式碼」分頁,刪除存根並貼上下列程式碼 (
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()
依序前往「Configuration」>「Environment variables」>「Edit」>「Add new environment variable」。
輸入下列環境變數,並將 換成您的值。
環境變數
鍵 範例值 WD_USER
audit_s3_user
WD_PASS
Wrokday-Password
WD_URL
https://.../Audit_Trail_BP_JSON?format=json
S3_BUCKET_NAME
workday-audit-logs
建立函式後,請留在函式頁面 (或依序開啟「Lambda」>「Functions」>「your‑function」)。
選取「設定」分頁標籤。
在「一般設定」面板中,按一下「編輯」。
將「Timeout」(逾時間隔) 變更為「5 minutes (300 seconds)」(5 分鐘 (300 秒)),然後按一下「Save」(儲存)。
排定 Lambda 函式 (EventBridge 排程器)
- 依序前往「Configuration」>「Triggers」>「Add trigger」>「EventBridge Scheduler」>「Create rule」。
- 提供下列設定詳細資料:
- 名稱:
daily-workday-audit export
。 - 排程模式:Cron 運算式。
- 運算式:
20 2 * * ? *
(每天世界標準時間 02:20 執行)。
- 名稱:
- 其餘設定保留預設值,然後點選「建立」。
在 Google SecOps 中設定動態饋給,擷取 Workday 稽核記錄
- 依序前往「SIEM 設定」>「動態饋給」。
- 按一下「+ 新增動態消息」。
- 在「動態饋給名稱」欄位中輸入動態饋給名稱 (例如
Workday Audit Logs
)。 - 選取「Amazon S3 V2」做為「來源類型」。
- 選取「Workday Audit」(Workday 稽核) 做為「Log type」(記錄類型)。
- 按一下「取得服務帳戶」。
- 點選「下一步」。
- 指定下列輸入參數的值:
- S3 URI:值區 URI
s3://workday-audit-logs/
。- 請將
workday-audit-logs
替換為實際值區名稱。
- 請將
- 來源刪除選項:根據偏好設定選取刪除選項。
- 檔案存在時間上限:包含在過去天數內修改的檔案。預設值為 180 天。
- 存取金鑰 ID:具有 S3 值區存取權的使用者存取金鑰。
- 存取密鑰:具有 S3 bucket 存取權的使用者私密金鑰。
- 資產命名空間:資產命名空間。
- 擷取標籤:要套用至這個動態饋給事件的標籤。
- S3 URI:值區 URI
- 點選「下一步」。
- 在「Finalize」畫面上檢查新的動態饋給設定,然後按一下「Submit」。
UDM 對應表
記錄欄位 | UDM 對應 | 邏輯 |
---|---|---|
Account |
metadata.event_type | 如果「帳戶」欄位不為空白,「metadata.event_type」欄位會設為「USER_RESOURCE_UPDATE_CONTENT」。 |
Account |
principal.user.primaryId | 系統會使用 grok 模式從「帳戶」欄位擷取使用者 ID,並對應至 principal.user.primaryId 。 |
Account |
principal.user.primaryName | 系統會使用 grok 模式從「帳戶」欄位擷取使用者顯示名稱,並對應至「principal.user.primaryName」。 |
ActivityCategory |
metadata.event_type | 如果「ActivityCategory」欄位為「READ」,「metadata.event_type」欄位會設為「RESOURCE_READ」。如果是「WRITE」,則會設為「RESOURCE_WRITTEN」。 |
ActivityCategory |
metadata.product_event_type | 直接從「ActivityCategory」欄位對應。 |
AffectedGroups |
target.user.group_identifiers | 直接從「AffectedGroups」欄位對應。 |
Area |
target.resource.attribute.labels.area.value | 直接從「區域」欄位對應。 |
AuthType |
extensions.auth.auth_details | 直接從「AuthType」欄位對應。 |
AuthType |
extensions.auth.type | 根據特定值,從「AuthType」欄位對應至 UDM 中定義的不同驗證類型。 |
CFIPdeConexion |
src.domain.name | 如果「CFIPdeConexion」欄位不是有效的 IP 位址,系統會將其對應至「src.domain.name」。 |
CFIPdeConexion |
target.ip | 如果「CFIPdeConexion」欄位是有效的 IP 位址,則會對應至「target.ip」。 |
ChangedRelationship |
metadata.description | 直接從「ChangedRelationship」欄位對應。 |
ClassOfInstance |
target.resource.attribute.labels.class_instance.value | 直接從「ClassOfInstance」欄位對應。 |
column18 |
about.labels.utub.value | 直接從「column18」欄位對應。 |
CreatedBy |
principal.user.userid | 系統會使用 grok 模式從「CreatedBy」欄位擷取使用者 ID,並對應至「principal.user.userid」。 |
CreatedBy |
principal.user.user_display_name | 系統會使用 grok 模式從「CreatedBy」欄位擷取使用者顯示名稱,並對應至「principal.user.user_display_name」。 |
Domain |
about.domain.name | 直接從「網域」欄位對應。 |
EffectiveDate |
@timestamp | 轉換為「yyyy-MM-dd HH:mm:ss.SSSZ」格式後,會剖析為「@timestamp」。 |
EntryMoment |
@timestamp | 轉換為「ISO8601」格式後,剖析為「@timestamp」。 |
EventType |
security_result.description | 直接從「EventType」欄位對應。 |
Form |
target.resource.name | 直接從「表單」欄位對應。 |
InstancesAdded |
about.resource.attribute.labels.instances_added.value | 直接從「InstancesAdded」欄位對應。 |
InstancesAdded |
target.user.attribute.roles.instances_added.name | 直接從「InstancesAdded」欄位對應。 |
InstancesRemoved |
about.resource.attribute.labels.instances_removed.value | 直接從「InstancesRemoved」欄位對應。 |
InstancesRemoved |
target.user.attribute.roles.instances_removed.name | 直接從「InstancesRemoved」欄位對應。 |
IntegrationEvent |
target.resource.attribute.labels.integration_event.value | 直接從「IntegrationEvent」欄位對應。 |
IntegrationStatus |
security_result.action_details | 直接從「IntegrationStatus」欄位對應。 |
IntegrationSystem |
target.resource.name | 直接從「IntegrationSystem」欄位對應。 |
IP |
src.domain.name | 如果「IP」欄位不是有效的 IP 位址,系統會將其對應至「src.domain.name」。 |
IP |
src.ip | 如果「IP」欄位是有效的 IP 位址,則會對應至「src.ip」。 |
IsDeviceManaged |
additional.fields.additional1.value.string_value | 如果「IsDeviceManaged」欄位為「N」,則值會設為「Successful」。否則會設為「發生登入失敗情形」。 |
IsDeviceManaged |
additional.fields.additional2.value.string_value | 如果「IsDeviceManaged」欄位為「N」,則值會設為「Successful」。否則會設為「憑證無效」。 |
IsDeviceManaged |
additional.fields.additional3.value.string_value | 如果「IsDeviceManaged」欄位為「N」,則值會設為「Successful」。否則會設為「帳戶已鎖定」。 |
IsDeviceManaged |
security_result.action_details | 直接對應至「IsDeviceManaged」欄位。 |
OutputFiles |
about.file.full_path | 直接從「OutputFiles」欄位對應。 |
Person |
principal.user.primaryId | 如果「Person」欄位開頭為「INT」,系統會使用 grok 模式擷取使用者 ID,並對應至「principal.user.primaryId」。 |
Person |
principal.user.primaryName | 如果「Person」欄位開頭為「INT」,系統會使用 grok 模式擷取使用者顯示名稱,並對應至「principal.user.primaryName」。 |
Person |
principal.user.user_display_name | 如果「Person」欄位開頭不是「INT」,則會直接對應至「principal.user.user_display_name」。 |
Person |
metadata.event_type | 如果「Person」欄位不為空白,「metadata.event_type」欄位會設為「USER_RESOURCE_UPDATE_CONTENT」。 |
ProcessedTransaction |
target.resource.attribute.creation_time | 轉換為「dd/MM/yyyy HH:mm:ss,SSS (ZZZ)」、「dd/MM/yyyy, HH:mm:ss,SSS (ZZZ)」或「MM/dd/yyyy, HH:mm:ss.SSS A ZZZ」格式後,會剖析為「target.resource.attribute.creation_time」。 |
ProgramBy |
principal.user.userid | 直接從「ProgramBy」欄位對應。 |
RecurrenceEndDate |
principal.resource.attribute.last_update_time | 轉換為「yyyy-MM-dd」格式後,剖析為「principal.resource.attribute.last_update_time」。 |
RecurrenceStartDate |
principal.resource.attribute.creation_time | 轉換為「yyyy-MM-dd」格式後,剖析為「principal.resource.attribute.creation_time」。 |
RequestName |
metadata.description | 直接從「RequestName」欄位對應。 |
ResponseMessage |
security_result.summary | 直接從「ResponseMessage」欄位對應。 |
RestrictedToEnvironment |
security_result.about.hostname | 直接從「RestrictedToEnvironment」欄位對應。 |
RevokedSecurity |
security_result.outcomes.outcomes.value | 直接對應至「RevokedSecurity」欄位。 |
RunFrequency |
principal.resource.attribute.labels.run_frequency.value | 直接從「RunFrequency」欄位對應。 |
ScheduledProcess |
principal.resource.name | 直接從「ScheduledProcess」欄位對應。 |
SecuredTaskExecuted |
target.resource.name | 直接從「SecuredTaskExecuted」欄位對應。 |
SecureTaskExecuted |
metadata.event_type | 如果「SecureTaskExecuted」欄位包含「Create」,「metadata.event_type」欄位會設為「USER_RESOURCE_CREATION」。 |
SecureTaskExecuted |
target.resource.name | 直接從「SecureTaskExecuted」欄位對應。 |
SentTime |
@timestamp | 轉換為「ISO8601」格式後,剖析為「@timestamp」。 |
SessionId |
network.session_id | 直接從「SessionId」欄位對應。 |
ShareBy |
target.user.userid | 直接從「ShareBy」欄位對應。 |
SignOffTime |
additional.fields.additional4.value.string_value | 「AuthFailMessage」欄位值會放在「additional.fields」陣列中,並以「Enterprise Interface Builder」做為鍵。 |
SignOffTime |
metadata.description | 直接從「AuthFailMessage」欄位對應。 |
SignOffTime |
metadata.event_type | 如果「SignOffTime」欄位空白,「metadata.event_type」欄位會設為「USER_LOGIN」。否則會設為「USER_LOGOUT」。 |
SignOffTime |
principal.user.attribute.last_update_time | 轉換為「ISO8601」格式後,剖析為「principal.user.attribute.last_update_time」。 |
SignOnIp |
src.domain.name | 如果「SignOnIp」欄位不是有效的 IP 位址,系統會將其對應至「src.domain.name」。 |
SignOnIp |
src.ip | 如果「SignOnIp」欄位是有效的 IP 位址,則會對應至「src.ip」。 |
Status |
metadata.product_event_type | 直接從「狀態」欄位對應。 |
SystemAccount |
principal.user.email_addresses | 系統會使用 grok 模式從「SystemAccount」欄位擷取電子郵件地址,並對應至「principal.user.email_addresses」。 |
SystemAccount |
principal.user.primaryId | 系統會使用 grok 模式從「SystemAccount」欄位擷取使用者 ID,並對應至「principal.user.primaryId」。 |
SystemAccount |
principal.user.primaryName | 系統會使用 grok 模式從「SystemAccount」欄位擷取使用者顯示名稱,並對應至「principal.user.primaryName」。 |
SystemAccount |
src.user.userid | 系統會使用 grok 模式從「SystemAccount」欄位擷取次要使用者 ID,並對應至「src.user.userid」。 |
SystemAccount |
src.user.user_display_name | 系統會使用 grok 模式從「SystemAccount」欄位擷取次要使用者顯示名稱,並對應至「src.user.user_display_name」。 |
SystemAccount |
target.user.userid | 系統會使用 grok 模式從「SystemAccount」欄位擷取目標使用者 ID,並對應至「target.user.userid」。 |
Target |
target.user.user_display_name | 直接從「目標」欄位對應。 |
Template |
about.resource.name | 直接從「範本」欄位對應。 |
Tenant |
target.asset.hostname | 直接從「租戶」欄位對應。 |
TlsVersion |
network.tls.version | 直接對應至「TlsVersion」欄位。 |
Transaction |
security_result.action_details | 直接從「交易」欄位對應。 |
TransactionType |
security_result.summary | 直接從「TransactionType」欄位對應。 |
TypeForm |
target.resource.resource_subtype | 直接從「TypeForm」欄位對應。 |
UserAgent |
network.http.parsed_user_agent | 使用「useragent」篩選器從「UserAgent」欄位剖析。 |
UserAgent |
network.http.user_agent | 直接從「UserAgent」欄位對應。 |
WorkdayAccount |
target.user.user_display_name | 系統會使用 grok 模式從「WorkdayAccount」欄位擷取使用者顯示名稱,並對應至「target.user.user_display_name」。 |
WorkdayAccount |
target.user.userid | 系統會使用 grok 模式從「WorkdayAccount」欄位擷取使用者 ID,並對應至「target.user.userid」。 |
additional.fields.additional1.key | 設為「FailedSignOn」。 | |
additional.fields.additional2.key | 設為「InvalidCredentials」。 | |
additional.fields.additional3.key | 設為「AccountLocked」。 | |
additional.fields.additional4.key | 設為「Enterprise Interface Builder」。 | |
metadata.event_type | 一開始設為「GENERIC_EVENT」,然後根據涉及其他欄位的邏輯更新。 | |
metadata.event_type | 針對特定事件類型設為「USER_CHANGE_PERMISSIONS」。 | |
metadata.event_type | 針對特定事件類型,請設為「RESOURCE_WRITTEN」。 | |
metadata.log_type | 硬式編碼為「WORKDAY_AUDIT」。 | |
metadata.product_name | 硬式編碼為「Enterprise Interface Builder」。 | |
metadata.vendor_name | 硬式編碼為「Workday」。 | |
principal.asset.category | 如果「DeviceType」欄位為「Phone」,請設為「Phone」。 | |
principal.resource.resource_type | 如果「ScheduledProcess」欄位不為空白,則會硬式編碼為「TASK」。 | |
security_result.action | 根據「FailedSignOn」、「IsDeviceManaged」、「InvalidCredentials」和「AccountLocked」欄位的值,設為「ALLOW」或「FAIL」。 | |
security_result.summary | 根據「FailedSignOn」、「IsDeviceManaged」、「InvalidCredentials」和「AccountLocked」欄位的值,設為「Successful」或特定錯誤訊息。 | |
target.resource.resource_type | 針對特定事件類型硬式編碼為「TASK」。 | |
target.resource.resource_type | 如果「TypeForm」欄位不為空白,則會硬式編碼為「DATASET」。 | |
message |
principal.user.email_addresses | 使用 grok 模式從「message」欄位擷取電子郵件地址,並在符合特定模式時,將其合併至「principal.user.email_addresses」。 |
message |
src.user.userid | 如果「event.idm.read_only_udm.principal.user.userid」欄位與從「message」欄位擷取的「user_target」相符,則清除該欄位。 |
message |
src.user.user_display_name | 如果「event.idm.read_only_udm.principal.user.userid」欄位與從「message」欄位擷取的「user_target」相符,則清除該欄位。 |
message |
target.user.userid | 使用 grok 模式從「message」欄位擷取 userid,並在符合特定模式時將其對應至「target.user.userid」。 |
還有其他問題嗎?向社群成員和 Google SecOps 專業人員尋求答案。