איסוף יומני אבטחה של ServiceNow
במאמר הזה מוסבר איך לשלוח אירועי אבטחה של ServiceNow אל Google Security Operations באמצעות פיד של webhook. הכלי לניתוח של ServiceNow Security מיועד לאירועי אבטחה של ServiceNow Instance Security Center, כמו ניסיונות כניסה שנכשלו, כניסות ל-SNC, כניסות של אדמינים, התחזות והעלאת רמת האבטחה (ראו אירועי אבטחה של ServiceNow Instance Security Center). שילוב עם ServiceNow מפרסם כל אירוע כ-JSON לנקודת קצה של webhook ב-Google SecOps, שבה מנתח התחביר מבצע נורמליזציה של האירוע למודל הנתונים המאוחד (UDM).
הכלי לניתוח מזהה חמישה ערכי אירועים: Failed Login, Admin Login, SNC Login, Impersonation ו-Security Elevation. כל ערך תואם לאירוע רשום ב-ServiceNow בתור sysevent. המסמך הזה יוצר פיד webhook, Script Include ששולח POST לפיד וארבע פעולות Script שמופעלות ברשומות sysevent התואמות וקוראות ל-Script Include.
אחרי ההגדרה החד-פעמית הזו, ServiceNow מעביר כל אירוע אבטחה חדש ל-Google SecOps באופן אוטומטי. לא נדרשת פעולה נוספת מצד האדמין.
לפני שמתחילים
חשוב לוודא שמתקיימות דרישות הסף הבאות:
- מופע של Google SecOps.
- מופע של ServiceNow עם חשבון משתמש שיש לו תפקיד
adminאו הרשאות ל-System Definition > Script Includes, System Properties ו-System Policy > Events > Script Actions ו-Registry. - הפעלה קיימת של מרכז האבטחה של ServiceNow Instance. המוצר ISC נמצא בסוף תקופת המכירות שלו, ואי אפשר להפעיל אותו במופעים חדשים.
- גישה עם הרשאות למסוף Google Google Cloud (ליצירת מפתח API).
יצירת פיד של ווּבּהוּק ב-Google SecOps
בקטעים הבאים מוסבר איך ליצור פיד של webhook ב-Google SecOps.
יצירת הפיד
- עוברים אל SIEM Settings > Feeds (הגדרות SIEM > פידים).
- לוחצים על הוספת פיד חדש.
- בדף הבא, לוחצים על הגדרת פיד יחיד.
- בשדה שם הפיד, מזינים שם לפיד (לדוגמה,
ServiceNow Security Events). - בוחרים באפשרות Webhook בתור סוג המקור.
- בוחרים באפשרות ServiceNow Security בתור סוג היומן.
- לוחצים על הבא.
מציינים ערכים לפרמטרים הבאים של הקלט:
- תו מפריד לפיצול: מזינים
\nכשהשילוב שולח יותר מאירוע JSON אחד לכל בקשה. אם כל בקשה מכילה אירוע אחד, משאירים את השדה ריק. - מרחב השמות של הנכס: מרחב השמות של הנכס.
- תוויות להעברה: התווית שמוחלת על האירועים מהפיד הזה.
- תו מפריד לפיצול: מזינים
לוחצים על הבא.
בודקים את ההגדרות של הפיד החדש במסך סיום ולוחצים על שליחה.
יצירה ושמירה של המפתח הסודי
- בדף הפרטים של הפיד, לוחצים על יצירת מפתח סודי.
מעתיקים ושומרים את המפתח הסודי במקום מאובטח.
קבלת כתובת ה-URL של נקודת הקצה של הפיד
- עוברים לכרטיסייה פרטים של הפיד.
בקטע Endpoint Information, מעתיקים את Feed endpoint URL. הפורמט של כתובת ה-URL הוא:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreateאו, אם משתמשים בנקודת קצה אזורית:
https://<REGION>-malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreateשומרים את כתובת ה-URL הזו לשלב מאוחר יותר.
לוחצים על סיום.
יצירת מפתח API Google Cloud
- נכנסים אל דף האישורים במסוף Google Google Cloud .
- בוחרים את הפרויקט שמשויך למופע Google SecOps.
- לוחצים על Create credentials > API key.
- לוחצים על Edit API key כדי להגביל את המפתח.
- בשדה שם, מזינים שם תיאורי (לדוגמה,
ServiceNow Webhook API Key). בקטע הגבלות על ממשקי API:
- בוחרים באפשרות הגבלת המקש.
- ברשימה Select APIs (בחירת ממשקי API), בוחרים באפשרות Google SecOps API.
לוחצים על Save.
מעתיקים את מפתח ה-API ושומרים אותו במקום מאובטח.
אחסון פרטי הכניסה של Google SecOps ב-ServiceNow
מאחסנים את כתובת ה-URL של נקודת הקצה ואת פרטי הכניסה כמאפייני מערכת של ServiceNow, כדי שקוד השילוב לא יכיל סודות.
- ב-ServiceNow, עוברים אל System Properties > sys_properties.list.
לוחצים על New (חדש) ומזינים את פרטי ההגדרה הבאים:
- Name (שם):
x_chronicle.endpoint_url - ערך: מדביקים את כתובת ה-URL של נקודת הקצה של הפיד.
- Type (סוג):
string
- Name (שם):
לוחצים על שליחה.
לוחצים על New (חדש) ומזינים את פרטי ההגדרה הבאים:
- Name (שם):
x_chronicle.api_key - ערך: מדביקים את מפתח ה-API של Google Cloud .
- Type (סוג):
password2
- Name (שם):
לוחצים על שליחה.
לוחצים על New (חדש) ומזינים את פרטי ההגדרה הבאים:
- Name (שם):
x_chronicle.secret_key - ערך: מדביקים את מפתח הסוד של פיד Google SecOps.
- Type (סוג):
password2
- Name (שם):
לוחצים על שליחה.
יצירת סקריפט webhook utility Script Include
הסקריפט הזה כולל פרסום של מטען ייעודי (payload) בפורמט JSON לנקודת הקצה של ה-Webhook של Google SecOps באמצעות ה-API המוגבל להיקף של ServiceNow sn_ws.RESTMessageV2. מפתח ה-API והמפתח הסודי נשלחים ככותרות HTTP.
- עוברים אל System Definition > Script Includes.
- לוחצים על חדש.
מזינים את פרטי ההגדרה הבאים:
- Name (שם):
ChronicleWebhookUtil - שם ה-API:
ChronicleWebhookUtil - Client callable: לא מסומן
- פעיל: מסומן
- Name (שם):
בשדה Script (סקריפט), מזינים את הקוד הבא:
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, עוברים אל System Policy > Events > Registry (
sysevent_register.list). מסננים את העמודה שם האירוע לכל אחת מהרשומות הבאות ומוודאים שקיימת רשומה:
loginlogin.failedimpersonation.startsecurity.elevated_role.enabled
אם חסר רשומה, לוחצים על חדש, מגדירים את השדה שם האירוע לערך החסר, מגדירים את הטבלה ל-
Global [global]ולוחצים על שליחה.
יצירת פעולות הסקריפט
כל פעולת סקריפט נרשמת לשם אירוע אחד ב-ServiceNow. כש-ServiceNow מוסיף רשומה תואמת לתור sysevent, הפעולה Script Action מופעלת, קוראת את פרמטרים האירוע וקוראת ל-ChronicleWebhookUtil כדי לפרסם את מטען הייעודי (payload).
הניתוח ממופה לאירועים הבאים ב-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 שלכם. פותחים את רשומת הרישום של כל אירוע ומוודאים שהתיאור של Parameters תואם לסקריפט שמתואר בהמשך. אם המרשם שלכם שונה, צריך לשנות את ההפניות parm1 או parm2.
יצירת פעולת סקריפט של התחברות שנכשלה
- עוברים אל מדיניות המערכת > אירועים > פעולות סקריפט.
- לוחצים על חדש.
מזינים את פרטי ההגדרה הבאים:
- Name (שם):
Chronicle - Failed Login - שם האירוע:
login.failed - פעיל: מסומן
- Name (שם):
בשדה Script (סקריפט), מזינים את הקוד הבא:
(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 מוגדר בהתאם.
- עוברים אל מדיניות המערכת > אירועים > פעולות סקריפט.
- לוחצים על חדש.
מזינים את פרטי ההגדרה הבאים:
- Name (שם):
Chronicle - Login - שם האירוע:
login - פעיל: מסומן
- Name (שם):
בשדה Script (סקריפט), מזינים את הקוד הבא:
(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);לוחצים על שליחה.
יצירת פעולת סקריפט להתחזות
- עוברים אל מדיניות המערכת > אירועים > פעולות סקריפט.
- לוחצים על חדש.
מזינים את פרטי ההגדרה הבאים:
- Name (שם):
Chronicle - Impersonation - שם האירוע:
impersonation.start - פעיל: מסומן
- Name (שם):
בשדה Script (סקריפט), מזינים את הקוד הבא:
(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);לוחצים על שליחה.
יצירת פעולת סקריפט להעלאת רמת האבטחה
- עוברים אל מדיניות המערכת > אירועים > פעולות סקריפט.
- לוחצים על חדש.
מזינים את פרטי ההגדרה הבאים:
- Name (שם):
Chronicle - Security Elevation - שם האירוע:
security.elevated_role.enabled - פעיל: מסומן
- Name (שם):
בשדה Script (סקריפט), מזינים את הקוד הבא:
(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.
בשדה Run script (הפעלת סקריפט), מזינים אחת מהשורות הבאות ולוחצים על Run script (הפעלת סקריפט):
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 ששונה מהכתובת הרגילה שלכם. - התחזות: מחשבון אדמין, להתחזות למשתמש אחר.
- העלאת רמת האבטחה: העלאה לתפקיד
security_adminממשתמש עם התפקיד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, מריצים חיפוש UDM עבור
metadata.vendor_name = "SERVICENOW"ו-metadata.product_name = "SERVICENOW_SECURITY". - מוודאים שהאירוע מופיע עם
metadata.event_typeשמוגדר ל-USER_LOGINאו ל-USER_CHANGE_PERMISSIONS, ושהשדהprincipal.user.useridמאוכלס.
הפניה לשיטות אימות
פידים של webhook ב-Google SecOps מקבלים את מפתח ה-API ואת המפתח הסודי ככותרות HTTP או כפרמטרים של שאילתת URL. במדריך הזה נעשה שימוש בשיטת הכותרות כי sn_ws.RESTMessageV2 תומך בכותרות בהתאמה אישית, וכך פרטי הכניסה לא מופיעים בכתובות URL וביומני גישה לשרת.
כותרות שמופיעות במדריך הזה:
-
X-goog-api-key: מפתח ה-API של Google SecOps API. Google Cloud -
X-Webhook-Access-Key: מפתח הסודי של הפיד שנוצר על ידי Google SecOps.
אם אי אפשר לשלוח כותרות מותאמות אישית בשילוב, צריך לצרף את פרטי הכניסה לכתובת ה-URL של נקודת הקצה:
<ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY>
מגבלות ושיטות מומלצות לשימוש ב-Webhook
| הגבלה | ערך |
|---|---|
| גודל בקשה מקסימלי | 4MB |
| מספר QPS מקסימלי (שאילתות לשנייה) | 15,000 |
| זמן קצוב לתפוגה של בקשה | 30 שניות |
| התנהגות של ניסיון חוזר | אוטומטי עם השהיה מעריכית לפני ניסיון חוזר (exponential backoff) |
כדי לשלוט בעוצמת הקול, כדאי לשלוח רק את אירועי האבטחה שהזיהויים שלכם דורשים, ולצרף כמה אירועים לכל בקשה באמצעות התו \n split delimiter אם המקור יוצר אירועים במקבצים.
מידע נוסף על פידים של 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.