收集 ServiceNow Security 日志
本文档介绍了如何使用 Webhook Feed 将 ServiceNow 安全事件发送到 Google Security Operations。ServiceNow Security 解析器专为 ServiceNow 实例安全中心安全事件而设计,例如登录失败、SNC 登录、管理员登录、模拟和安全权限提升(请参阅 ServiceNow 实例安全中心安全事件)。ServiceNow 集成会将每个事件以 JSON 格式发布到 Google SecOps webhook 端点,解析器会在该端点将其标准化为统一数据模型 (UDM)。
解析器可识别五个事件值:Failed Login、Admin Login、SNC Login、Impersonation 和 Security Elevation。每个值都对应于 sysevent 队列中已注册的 ServiceNow 事件。此文档创建了一个 webhook Feed、一个向该 Feed 执行 POST 操作的脚本包含,以及四个在匹配的 sysevent 记录上触发并调用该脚本包含的脚本操作。
完成此一次性设置后,ServiceNow 会自动将每个新的安全事件转发给 Google SecOps。管理员无需采取进一步行动。
准备工作
确保您满足以下前提条件:
- Google SecOps 实例。
- 一个 ServiceNow 实例,其中包含一个具有
admin角色的用户账号,或者具有以下权限:系统定义 > 脚本包含、系统属性、系统政策 > 事件 > 脚本操作和注册。 - 现有的 ServiceNow 实例安全中心激活。ISC 已停止销售,无法在新实例上激活。
- 对 Google Google Cloud 控制台的特权访问权限(用于创建 API 密钥)。
在 Google SecOps 中创建 Webhook Feed
以下部分介绍了如何在 Google SecOps 中创建 Webhook Feed。
创建 Feed
- 依次前往 SIEM 设置 > Feed。
- 点击添加新 Feed。
- 在下一页上,点击配置单个 Feed。
- 在 Feed 名称字段中,输入 Feed 的名称(例如
ServiceNow Security Events)。 - 选择 Webhook 作为来源类型。
- 选择 ServiceNow Security 作为日志类型。
- 点击下一步。
为以下输入参数指定值:
- 拆分定界符:当集成每次请求发送多个 JSON 事件时,请输入
\n。如果每个请求都包含单个事件,请留空。 - 资源命名空间:资源命名空间。
- 注入标签:应用于此 Feed 中事件的标签。
- 拆分定界符:当集成每次请求发送多个 JSON 事件时,请输入
点击下一步。
在最终确定界面中查看新的 Feed 配置,然后点击提交。
生成并保存密钥
- 在 Feed 详情页面上,点击生成密钥。
复制并保存密钥,将其保存在安全的位置。
获取 Feed 端点网址
- 前往相应 Feed 的详细信息标签页。
在端点信息部分,复制 Feed 端点网址。网址格式为:
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 密钥以限制密钥。
- 在名称字段中,输入一个描述性名称(例如
ServiceNow Webhook API Key)。 在 API 限制下:
- 选择限制密钥。
- 在选择 API 列表中,选择 Google SecOps API。
点击保存。
复制 API 密钥并将其保存在安全的位置。
在 ServiceNow 中存储 Google SecOps 凭据
将端点网址和凭据存储为 ServiceNow 系统属性,以便集成代码不包含任何密钥。
- 在 ServiceNow 中,依次前往系统属性 > sys_properties.list。
点击新建,然后提供以下配置详细信息:
- 名称:
x_chronicle.endpoint_url - 值:粘贴 Feed 端点网址。
- 类型:
string
- 名称:
点击提交。
点击新建,然后提供以下配置详细信息:
- 名称:
x_chronicle.api_key - 值:粘贴 Google Cloud API 密钥。
- 类型:
password2
- 名称:
点击提交。
点击新建,然后提供以下配置详细信息:
- 名称:
x_chronicle.secret_key - 值:粘贴 Google SecOps Feed 密钥。
- 类型:
password2
- 名称:
点击提交。
创建网络钩子实用工具 Script Include
此脚本包含使用 ServiceNow sn_ws.RESTMessageV2 范围 API 将 JSON 载荷发布到 Google SecOps Webhook 端点。API 密钥和 Secret 密钥会作为 HTTP 标头发送。
- 前往系统定义 > 脚本包含。
- 点击 New(新建)。
提供以下配置详细信息:
- 名称:
ChronicleWebhookUtil - API 名称:
ChronicleWebhookUtil - 客户端可调用:未选中
- 有效:已选中
- 名称:
在脚本字段中,输入以下代码:
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 开箱即用,可提供此集成使用的五种事件,但最好还是确认一下,因为如果缺少条目,系统不会生成任何错误,并且脚本操作永远不会运行。
- 在 ServiceNow 中,依次前往系统政策 > 事件 > 注册 (
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(新建)。
提供以下配置详细信息:
- 名称:
Chronicle - Failed Login - 事件名称:
login.failed - 有效:已选中
- 名称:
在脚本字段中,输入以下代码:
(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(新建)。
提供以下配置详细信息:
- 名称:
Chronicle - Login - 事件名称:
login - 有效:已选中
- 名称:
在脚本字段中,输入以下代码:
(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(新建)。
提供以下配置详细信息:
- 名称:
Chronicle - Impersonation - 事件名称:
impersonation.start - 有效:已选中
- 名称:
在脚本字段中,输入以下代码:
(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(新建)。
提供以下配置详细信息:
- 名称:
Chronicle - Security Elevation - 事件名称:
security.elevated_role.enabled - 有效:已选中
- 名称:
在脚本字段中,输入以下代码:
(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 中,依次前往系统定义 > 脚本 - 后台。
在运行脚本字段中,输入以下某一行,然后点击运行脚本:
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');等待最多一分钟,让 Event Manager 作业处理队列。
使用真实事件进行验证
在 ServiceNow 中生成以下任一已映射的安全性事件,以验证集成:
- 登录失败:在无痕式浏览器窗口中打开
/login.do,然后提交无效密码。 - 管理员登录:使用具有
admin角色的用户从与您通常使用的 IP 地址不同的 IP 地址登录。 - 模拟:从管理员账号模拟其他用户。
- 安全权限提升:从具有
admin角色的用户提升到security_admin角色。
确认转发
- 在 ServiceNow 中,依次前往系统政策 > 事件 > 事件日志 (
sysevent.list),并确认是否显示了具有相应事件名称的记录。 - 在 ServiceNow 中,依次前往系统日志 > 系统日志 > 全部,然后确认是否显示状态为 200、201 或 204 的
[Chronicle] Event sent: ...信息消息。[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 Feed 接受 API 密钥和密钥作为 HTTP 标头或网址查询参数。本指南使用标头方法,因为 sn_ws.RESTMessageV2 支持自定义标头,这样可避免凭据出现在网址和服务器访问日志中。
本指南中使用的标题:
X-goog-api-key:Google SecOps API 的 Google Cloud API 密钥。X-Webhook-Access-Key:由 Google SecOps 生成的 Feed 密钥。
如果集成无法发送自定义标头,请改为将凭据附加到端点网址:
<ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY>
Webhook 限制和最佳实践
| 限制 | 值 |
|---|---|
| 最大请求大小 | 4 MB |
| 最大 QPS(每秒查询次数) | 15000 |
| 请求超时 | 30 秒 |
| 重试行为 | 自动(使用指数退避算法) |
为了控制数据量,请仅发送检测所需的安全事件,并在来源以突发方式生成事件时,使用 \n 分隔符按请求批量发送多个事件。
如需详细了解 Google SecOps Feed,请参阅 Google SecOps Feed 文档。如需了解每种 Feed 类型的要求,请参阅按类型划分的 Feed 配置。
如果您在创建 Feed 时遇到问题,请与 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 专业人士的解答。