收集 ServiceNow Security 記錄
本文說明如何使用 Webhook 資訊提供,將 ServiceNow 安全性事件傳送至 Google Security Operations。ServiceNow Security 剖析器適用於 ServiceNow 執行個體安全中心安全性事件,例如登入失敗、SNC 登入、管理員登入、模擬和安全性提升 (請參閱「ServiceNow 執行個體安全中心安全性事件」)。ServiceNow 整合功能會將每個事件以 JSON 格式發布至 Google SecOps 網頁掛鉤端點,剖析器會將事件正規化為統合資料模型 (UDM)。
剖析器會辨識五個事件值:Failed Login、Admin Login、SNC Login、Impersonation 和 Security Elevation。每個值都對應至 sysevent 佇列中已註冊的 ServiceNow 事件。這份文件會建立 Webhook 動態消息、POST 到動態消息的指令碼包含項目,以及在相符的 sysevent 記錄上觸發並呼叫指令碼包含項目的四個指令碼動作。
完成這項一次性設定後,ServiceNow 會自動將每個新的安全性事件轉送至 Google SecOps。管理員不需採取進一步行動。
事前準備
請確認您已完成下列事前準備事項:
- Google SecOps 執行個體。
- ServiceNow 執行個體,以及具備
admin角色或權限的使用者帳戶,可存取「System Definition」(系統定義) >「Script Includes」(指令碼包含)、「System Properties」(系統屬性),以及「System Policy」(系統政策) >「Events」(事件) >「Script Actions」(指令碼動作) 和「Registry」(登錄)。 - 現有的 ServiceNow 執行個體安全中心啟用作業。ISC 已終止銷售,無法在新執行個體上啟用。
- Google Google Cloud 控制台的特殊權限 (用於建立 API 金鑰)。
在 Google SecOps 中建立 Webhook 動態饋給
以下各節說明如何在 Google SecOps 中建立 Webhook 資訊提供。
建立動態饋給
- 依序前往「SIEM 設定」>「動態饋給」。
- 按一下「新增動態消息」。
- 在下一個頁面中,按一下「設定單一動態饋給」。
- 在「動態饋給名稱」欄位中輸入動態饋給名稱 (例如
ServiceNow Security Events)。 - 選取「Webhook」做為「來源類型」。
- 選取「ServiceNow Security」做為「記錄類型」。
- 點選「下一步」。
指定下列輸入參數的值:
- 分割分隔符號:如果整合服務在每個要求中傳送多個 JSON 事件,請輸入
\n。如果每個要求都包含單一事件,請留空。 - 資產命名空間:資產命名空間。
- 擷取標籤:套用至這個動態饋給事件的標籤。
- 分割分隔符號:如果整合服務在每個要求中傳送多個 JSON 事件,請輸入
點選「下一步」。
在「Finalize」(完成) 畫面中檢查新的動態饋給設定,然後按一下「Submit」(提交)。
產生並儲存密鑰
- 在動態饋給詳細資料頁面中,按一下「產生密鑰」。
複製並儲存密鑰,並妥善存放在安全的位置。
取得動態消息端點網址
- 前往動態消息的「詳細資料」分頁。
在「端點資訊」部分,複製「動態消息端點網址」。網址格式為:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate或是區域端點:
https://<REGION>-malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate請儲存這個網址,稍後會用到。
按一下 [完成]。
建立 Google Cloud API 金鑰
- 前往 Google Google Cloud 控制台的「憑證」頁面。
- 選取與 Google SecOps 執行個體相關聯的專案。
- 依序按一下「建立憑證」>「API 金鑰」。
- 按一下「編輯 API 金鑰」即可限制金鑰。
- 在「Name」(名稱) 欄位中輸入描述性名稱 (例如
ServiceNow Webhook API Key)。 在「API 限制」下方:
- 選取「Restrict key」(限制金鑰)。
- 在「選取 API」清單中,選取「Google SecOps API」。
按一下 [儲存]。
複製 API 金鑰,並儲存在安全地點。
在 ServiceNow 中儲存 Google SecOps 憑證
將端點網址和憑證儲存為 ServiceNow 系統屬性,這樣整合程式碼就不會包含密鑰。
- 在 ServiceNow 中,依序前往「System Properties」(系統屬性) > sys_properties.list。
按一下「新增」,然後提供下列設定詳細資料:
- Name (名稱):
x_chronicle.endpoint_url - 值:貼上動態消息端點網址。
- 「類型」:
string
- Name (名稱):
按一下「提交」。
按一下「新增」,然後提供下列設定詳細資料:
- Name (名稱):
x_chronicle.api_key - 值:貼上 Google Cloud API 金鑰。
- 「類型」:
password2
- Name (名稱):
按一下「提交」。
按一下「新增」,然後提供下列設定詳細資料:
- Name (名稱):
x_chronicle.secret_key - 值:貼上 Google SecOps 摘要資訊的密鑰。
- 「類型」:
password2
- Name (名稱):
按一下「提交」。
建立 Webhook 公用程式指令碼包含項目
這個指令碼會使用 ServiceNow sn_ws.RESTMessageV2 範圍 API,將 JSON 酬載發布至 Google SecOps Webhook 端點。API 金鑰和私密金鑰會以 HTTP 標頭的形式傳送。
- 前往「系統定義」>「指令碼包含」。
- 點選「New」(新增)。
請提供下列設定詳細資料:
- Name (名稱):
ChronicleWebhookUtil - API 名稱:
ChronicleWebhookUtil - 用戶端可呼叫:取消勾選
- 有效:已勾選
- Name (名稱):
在「指令碼」欄位中,輸入下列程式碼:
var ChronicleWebhookUtil = Class.create(); ChronicleWebhookUtil.prototype = { initialize: function() { this.endpointURL = gs.getProperty('x_chronicle.endpoint_url'); this.apiKey = gs.getProperty('x_chronicle.api_key'); this.secretKey = gs.getProperty('x_chronicle.secret_key'); }, // payload: a plain object whose keys match the required JSON contract. sendEvent: function(payload) { try { if (!this.endpointURL || !this.apiKey || !this.secretKey) { gs.error('[Chronicle] Missing configuration. Check System Properties: x_chronicle.*'); return false; } var request = new sn_ws.RESTMessageV2(); request.setEndpoint(this.endpointURL); request.setHttpMethod('POST'); request.setRequestHeader('Content-Type', 'application/json'); request.setRequestHeader('X-goog-api-key', this.apiKey); request.setRequestHeader('X-Webhook-Access-Key', this.secretKey); request.setRequestBody(JSON.stringify(payload)); var response = request.execute(); var statusCode = response.getStatusCode(); if (statusCode == 200 || statusCode == 201 || statusCode == 204) { gs.info('[Chronicle] Event sent: ' + payload.event + ' | Status: ' + statusCode); return true; } gs.error('[Chronicle] Failed to send event: ' + payload.event + ' | Status: ' + statusCode + ' | Response: ' + response.getBody()); return false; } catch (ex) { gs.error('[Chronicle] Exception sending event: ' + ex.message); return false; } }, type: 'ChronicleWebhookUtil' };按一下「提交」。
驗證事件登錄
只有當事件名稱存在於「事件登錄」 (sysevent_register 表格) 中時,系統才會觸發指令碼動作。ServiceNow 會隨附這項整合功能使用的五個事件,但建議您確認,因為如果缺少項目,系統不會產生錯誤,且 Script Action 永遠不會執行。
- 在 ServiceNow 中,依序前往「System Policy」(系統政策) >「Events」(事件) >「Registry」(登錄) (
sysevent_register.list)。 針對下列每個項目篩選「事件名稱」欄,並確認有記錄:
loginlogin.failedimpersonation.startsecurity.elevated_role.enabled
如果缺少項目,請按一下「新增」,將「事件名稱」欄位設為缺少的值,將「資料表」設為
Global [global],然後按一下「提交」。
建立指令碼動作
每個指令碼動作都會訂閱一個 ServiceNow 事件名稱。當 ServiceNow 將相符記錄插入 sysevent 佇列時,系統會執行指令碼動作、讀取事件參數,並呼叫 ChronicleWebhookUtil 來 POST 酬載。
剖析器會對應至下列 ServiceNow 事件:
剖析器 event 值 |
ServiceNow 事件名稱 | 觸發條件 |
|---|---|---|
Failed Login |
login.failed |
登入嘗試失敗 (任何驗證方法)。 |
Admin Login |
login |
擁有 admin 角色的使用者成功登入。 |
SNC Login |
login |
擁有 snc_external 角色的使用者成功登入。 |
Impersonation |
impersonation.start |
管理員開始模擬另一位使用者。 |
Security Elevation |
security.elevated_role.enabled |
使用者提升為 security_admin 或其他高權限角色。 |
login 和 login.failed 將 parm1 設為使用者名稱,並將 parm2 設為來源 IP。其他事件會遵循相同的 parm1/parm2 慣例,但確切內容取決於 ServiceNow 版本。開啟每個事件的登錄項目,確認「參數」說明與稍後說明的指令碼相符。如果登錄檔不同,請調整 parm1 或 parm2 參照。
建立登入失敗指令碼動作
- 依序前往「系統政策」>「事件」>「指令碼動作」。
- 點選「New」(新增)。
請提供下列設定詳細資料:
- Name (名稱):
Chronicle - Failed Login - 活動名稱:
login.failed - 有效:已勾選
- Name (名稱):
在「指令碼」欄位中,輸入下列程式碼:
(function runAction(/*GlideRecord*/ current, /*GlideRecord*/ event) { new ChronicleWebhookUtil().sendEvent({ event: 'Failed Login', event_created: event.sys_created_on.getValue(), user: event.parm1.toString(), ip_address: event.parm2.toString() }); })(current, event);按一下「提交」。
建立登入指令碼動作
這項指令碼動作會在每次成功登入時觸發,且只會在登入使用者具有 admin 或 snc_external 角色時轉送記錄。event 欄位會相應設定。
- 依序前往「系統政策」>「事件」>「指令碼動作」。
- 點選「New」(新增)。
請提供下列設定詳細資料:
- Name (名稱):
Chronicle - Login - 活動名稱:
login - 有效:已勾選
- Name (名稱):
在「指令碼」欄位中,輸入下列程式碼:
(function runAction(/*GlideRecord*/ current, /*GlideRecord*/ event) { var userName = event.parm1.toString(); var ipAddress = event.parm2.toString(); var user = new GlideRecord('sys_user'); if (!user.get('user_name', userName)) { return; } function hasRole(roleName) { var role = new GlideRecord('sys_user_has_role'); role.addQuery('user', user.sys_id); role.addQuery('role.name', roleName); role.query(); return role.next(); } var eventValue; if (hasRole('snc_external')) { eventValue = 'SNC Login'; } else if (hasRole('admin')) { eventValue = 'Admin Login'; } else { return; } new ChronicleWebhookUtil().sendEvent({ event: eventValue, event_created: event.sys_created_on.getValue(), user: userName, ip_address: ipAddress }); })(current, event);按一下「提交」。
建立模擬指令碼動作
- 依序前往「系統政策」>「事件」>「指令碼動作」。
- 點選「New」(新增)。
請提供下列設定詳細資料:
- Name (名稱):
Chronicle - Impersonation - 活動名稱:
impersonation.start - 有效:已勾選
- Name (名稱):
在「指令碼」欄位中,輸入下列程式碼:
(function runAction(/*GlideRecord*/ current, /*GlideRecord*/ event) { new ChronicleWebhookUtil().sendEvent({ event: 'Impersonation', event_created: event.sys_created_on.getValue(), user: event.parm1.toString(), // impersonator snc_user: event.parm2.toString() // impersonated user }); })(current, event);按一下「提交」。
建立安全性提升指令碼動作
- 依序前往「系統政策」>「事件」>「指令碼動作」。
- 點選「New」(新增)。
請提供下列設定詳細資料:
- Name (名稱):
Chronicle - Security Elevation - 活動名稱:
security.elevated_role.enabled - 有效:已勾選
- Name (名稱):
在「指令碼」欄位中,輸入下列程式碼:
(function runAction(/*GlideRecord*/ current, /*GlideRecord*/ event) { new ChronicleWebhookUtil().sendEvent({ event: 'Security Elevation', event_created: event.sys_created_on.getValue(), user: event.parm1.toString() }); })(current, event);按一下「提交」。
驗證整合
請使用下列任一方法。
使用合成事件驗證
如要立即測試每個指令碼動作,不必等待實際的安全事件,請按照下列步驟,從背景指令碼將事件加入佇列:
- 在 ServiceNow 中,依序前往「System Definition」(系統定義) >「Scripts - Background」(指令碼 - 背景)。
在「執行指令碼」欄位中,輸入下列其中一行,然後按一下「執行指令碼」:
gs.eventQueue('login.failed', null, 'test.user', '203.0.113.45'); gs.eventQueue('login', null, 'admin', '203.0.113.45'); gs.eventQueue('impersonation.start', null, 'admin', 'test.user'); gs.eventQueue('security.elevated_role.enabled', null, 'admin', 'security_admin');等待最多一分鐘,讓事件管理工具工作處理佇列。
使用真實事件驗證
在 ServiceNow 中產生下列其中一個對應的安全事件,以驗證整合:
- 登入失敗:在私密瀏覽器視窗中開啟
/login.do,並提交無效密碼。 - 管理員登入:使用具備
admin角色的使用者,從與平常不同的 IP 位址登入。 - 模擬:從管理員帳戶模擬其他使用者。
- 安全性提升:從具有
admin角色的使用者提升至security_admin角色。
確認轉寄
- 在 ServiceNow 中,依序前往「System Policy」>「Events」>「Event Log」 (
sysevent.list),確認是否顯示相應事件名稱的記錄。 - 在 ServiceNow 中,依序前往「System Logs」(系統記錄) >「System Log」(系統記錄) >「All」(全部),確認是否顯示
[Chronicle] Event sent: ...資訊訊息,且狀態為 200、201 或 204。[Chronicle] Failed to send event或[Chronicle] Exception sending event訊息表示憑證或連線有問題。 - 在 Google SecOps 中,對
metadata.vendor_name = "SERVICENOW"和metadata.product_name = "SERVICENOW_SECURITY"執行 UDM 搜尋。 - 確認事件顯示的
metadata.event_type已設為USER_LOGIN或USER_CHANGE_PERMISSIONS,且principal.user.userid已填入。
驗證方法參考資料
Google SecOps Webhook 摘要會將 API 金鑰和私密金鑰視為 HTTP 標頭或網址查詢參數。本指南使用標頭方法,因為 sn_ws.RESTMessageV2 支援自訂標頭,可避免憑證出現在網址和伺服器存取記錄中。
本指南使用的標題:
X-goog-api-key:Google SecOps API 的 Google Cloud API 金鑰。X-Webhook-Access-Key:Google SecOps 產生的動態饋給私密金鑰。
如果整合無法傳送自訂標頭,請改為將憑證附加至端點網址:
<ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY>
Webhook 限制和最佳做法
| 限制 | 值 |
|---|---|
| 要求大小上限 | 4 MB |
| 每秒查詢次數 (QPS) 上限 | 15,000 |
| 要求逾時 | 30 秒 |
| 重試行為 | 自動執行指數輪詢 |
如要控制數量,請只傳送偵測作業需要的安全性事件,並在來源大量產生事件時,使用 \n 分隔符號,在每個要求中批次處理多個事件。
如要進一步瞭解 Google SecOps 動態饋給,請參閱 Google SecOps 動態饋給說明文件。如要瞭解各類動態饋給的規定,請參閱「依類型設定動態饋給」。
如果在建立動態饋給時遇到問題,請與 Google SecOps 支援團隊聯絡。
UDM 對應表
| 記錄欄位 | UDM 對應 | 邏輯 |
|---|---|---|
event |
extensions.auth.type |
已對應:"Failed Login", "SNC Login", "Admin Login", "Impersonation" → MACHINE |
event_created |
metadata.event_timestamp |
已剖析為 yyyy-MM-dd HH:mm:ss |
event_type |
metadata.event_type |
已重新命名/對應 |
event |
metadata.product_event_type |
當 event != `` 時對應 |
ip_address |
principal.ip |
已合併 |
user |
principal.user.userid |
當 user != `` 時對應 |
created_by |
target.user.userid |
當 created_by != `` 時對應 |
snc_user |
target.user.userid |
當 snc_user != `` 時對應 |
timestamp |
event1.idm.read_only_udm.metadata.event_timestamp |
從變更記錄對應 |
data.sys_id |
event1.idm.read_only_udm.metadata.product_log_id |
從變更記錄對應 |
data.message_data |
event1.idm.read_only_udm.metadata.description |
從變更記錄對應 |
data.source |
event1.idm.read_only_udm.principal.resource.name |
從變更記錄對應 |
source |
event1.idm.read_only_udm.additional.fields |
從變更記錄對應 |
data.sys_created_on |
event1.idm.read_only_udm.metadata.collected_timestamp |
從變更記錄對應 |
data.sys_created_by |
event1.idm.read_only_udm.principal.user.userid |
從變更記錄對應 |
data.user |
event1.idm.read_only_udm.principal.user.userid |
從變更記錄對應 |
source_instance |
event1.idm.read_only_udm.principal.namespace |
從變更記錄對應 |
data.role |
event1.idm.read_only_udm.target.resource.attribute.roles |
從變更記錄對應 |
data.granted_by |
event1.idm.read_only_udm.target.user.userid |
從變更記錄對應 |
data.level |
event1.idm.read_only_udm.security_result.severity |
從變更記錄對應 |
data.state |
event1.idm.read_only_udm.security_result.action_details |
從變更記錄對應 |
變更記錄
還有其他問題嗎?向社群成員和 Google SecOps 專業人員尋求答案。