收集 Signal Sciences WAF 記錄
本文說明如何使用 Google Cloud 儲存空間,將 Signal Sciences WAF 記錄檔擷取至 Google Security Operations。剖析器會將 Signal Sciences 記錄從 JSON 格式轉換為 Chronicle 的統一資料模型 (UDM)。這個外掛程式會處理兩種主要訊息結構:「RPC.PreRequest/PostRequest」訊息會使用 Grok 模式剖析,其他訊息則會以 JSON 物件處理,並擷取相關欄位,然後對應至 UDM 結構定義。
事前準備
請確認您已完成下列事前準備事項:
- Google SecOps 執行個體
- 在 Google Cloud 環境中設定並啟用虛擬私有雲流量記錄
- Signal Sciences WAF 的特殊存取權
建立 Google Cloud 儲存空間 bucket
- 登入 Google Cloud 主控台。
前往「Cloud Storage Buckets」(Cloud Storage bucket) 頁面。
點選「建立」。
在「建立 bucket」頁面中,輸入 bucket 資訊。完成下列每個步驟後,請按一下「繼續」前往下一個步驟:
在「開始使用」部分執行下列操作:
- 輸入符合值區名稱規定的專屬名稱 (例如 vpcflow-logs)。
- 如要啟用階層命名空間,請按一下展開箭頭,展開「為檔案導向和資料密集型工作負載提供最理想的儲存空間」部分,然後選取「為這個值區啟用階層結構式命名空間」。
- 如要新增值區標籤,請按一下展開箭頭,展開「標籤」部分。
- 按一下「新增標籤」,然後指定標籤的鍵和值。
在「Choose where to store your data」(選擇資料的儲存位置) 專區中執行下列操作:
- 選取「位置類型」。
- 使用位置類型選單選取位置,永久儲存 bucket 中的物件資料。
- 如要設定跨 bucket 複製作業,請展開「設定跨 bucket 複製作業」部分。
在「為資料選擇儲存空間級別」部分,選取值區的預設儲存空間級別,或選取「Autoclass」,讓系統自動管理值區資料的儲存空間級別。
在「選取如何控制物件的存取權」部分,選取「否」以強制禁止公開存取,並為值區物件選取存取權控管模型。
在「選擇保護物件資料的方式」部分,執行下列操作:
- 選取「資料保護」下要為值區設定的任何選項。
- 如要選擇物件資料的加密方式,請按一下標示為「資料加密」的展開箭頭,然後選取「資料加密方法」。
點選「建立」。
設定 Signal Sciences WAF API 金鑰
- 登入 Signal Sciences WAF 網頁版 UI。
- 依序前往「My Profile」>「API Access Tokens」。
- 按一下「新增 API 存取權杖」。
- 提供容易識別且不重複的名稱 (例如
Google SecOps
)。 - 按一下「建立 API 存取權杖」。
- 複製權杖並儲存在安全的位置。
- 按一下「我瞭解」,完成權杖建立程序。
在 Linux 主機上部署指令碼,從 Signal Sciences 提取記錄並儲存在 Google Cloud中
- 使用 SSH 登入 Linux 主機。
安裝 Python 程式庫,將 Signal Sciences WAF JSON 儲存至 Cloud Storage 值區:
pip install google-cloud-storage
設定這個環境變數,從 Google Cloud呼叫含有憑證的 JSON 檔案:
export GOOGLE_APPLICATION_CREDENTIALS="path/to/your/service-account-key.json"
設定下列環境變數,因為這項資訊不得硬式編碼:
export SIGSCI_EMAIL=<Signal_Sciences_account_email> export SIGSCI_TOKEN=<Signal_Sciences_API_token> export SIGSCI_CORP=<Corporation_name_in_Signal_Sciences>
執行下列指令碼:
import sys import requests import os import calendar import json from datetime import datetime, timedelta from google.cloud import storage # Check if all necessary environment variables are set if 'SIGSCI_EMAIL' not in os.environ or 'SIGSCI_TOKEN' not in os.environ or 'SIGSCI_CORP' not in os.environ: print("ERROR: You need to define SIGSCI_EMAIL, SIGSCI_TOKEN, and SIGSCI_CORP environment variables.") print("Please fix and run again. Existing...") sys.exit(1) # Exit if environment variables are not set # Define the Google Cloud Storage bucket name and output file name bucket_name = 'Your_GCS_Bucket' # Replace with your GCS bucket name output_file_name = 'signal_sciences_logs.json' # Initialize Google Cloud Storage client storage_client = storage.Client() # Function to upload data to Google Cloud Storage def upload_to_gcs(bucket_name, data, destination_blob_name): bucket = storage_client.bucket(bucket_name) blob = bucket.blob(destination_blob_name) blob.upload_from_string(data, content_type='application/json') print(f"Data uploaded to {destination_blob_name} in bucket {bucket_name}") # Signal Sciences API information api_host = 'https://dashboard.signalsciences.net' # email = 'user@domain.com' # Signal Sciences account email # token = 'XXXXXXXX-XXXX-XXX-XXXX-XXXXXXXXXXXX' # API token for authentication # corp_name = 'Domain' # Corporation name in Signal Sciences # site_names = ['testenv'] # Replace with your actual site names # List of comma-delimited sites that you want to extract data from site_names = [ 'site123', 'site345' ] # Define all sites to pull logs from email = os.environ.get('SIGSCI_EMAIL') # Signal Sciences account email token = os.environ.get('SIGSCI_TOKEN') # API token for authentication corp_name = os.environ.get('SIGSCI_CORP') # Corporation name in Signal Sciences # Calculate the start and end timestamps for the previous hour in UTC until_time = datetime.utcnow().replace(minute=0, second=0, microsecond=0) from_time = until_time - timedelta(hours=1) until_time = calendar.timegm(until_time.utctimetuple()) from_time = calendar.timegm(from_time.utctimetuple()) # Prepare HTTP headers for the API request headers = { 'Content-Type': 'application/json', 'x-api-user': email, 'x-api-token': token } # Collect logs for each site collected_logs = [] for site_name in site_names: url = f"{api_host}/api/v0/corps/{corp_name}/sites/{site_name}/feed/requests?from={from_time}&until={until_time}" while True: response = requests.get(url, headers=headers) if response.status_code != 200: print(f"Error fetching logs: {response.text}", file=sys.stderr) break # Parse the JSON response data = response.json() collected_logs.extend(data['data']) # Add the log messages to our list # Pagination: check if there is a next page next_url = data.get('next', {}).get('uri') if not next_url: break url = api_host + next_url # Convert the collected logs to a newline-delimited JSON string json_data = '\n'.join(json.dumps(log) for log in collected_logs) # Save the newline-delimited JSON data to a GCS bucket upload_to_gcs(bucket_name, json_data, output_file_name)
設定動態饋給
在 Google SecOps 平台中,有兩種不同的進入點可設定動態饋給:
- 「SIEM 設定」>「動態消息」
- 內容中心 > 內容包
依序前往「SIEM 設定」>「動態饋給」,設定動態饋給
如要設定動態消息,請按照下列步驟操作:
- 依序前往「SIEM 設定」>「動態消息」。
- 按一下「新增動態消息」。
- 在下一個頁面中,按一下「設定單一動態饋給」。
- 在「動態饋給名稱」欄位中輸入動態饋給名稱 (例如
Signal Sciences WAF Logs
)。 - 選取「Google Cloud Storage」做為「來源類型」。
- 選取「Signal Sciences WAF」做為「記錄類型」。
- 按一下「Chronicle 服務帳戶」的「取得服務帳戶」。
- 點選「下一步」。
指定下列輸入參數的值:
- 儲存空間值區 URI: Google Cloud
gs://my-bucket/<value>
格式的儲存空間值區 URL。 - 「URI Is A」(URI 為):選取「Directory which includes subdirectories」(包含子目錄的目錄)。
來源刪除選項:根據偏好選取刪除選項。
- 儲存空間值區 URI: Google Cloud
點選「下一步」。
在「Finalize」畫面上檢查新的動態饋給設定,然後按一下「Submit」。
從內容中心設定動態饋給
為下列欄位指定值:
- 儲存空間值區 URI: Google Cloud
gs://my-bucket/<value>
格式的儲存空間值區 URL。 - 「URI Is A」(URI 為):選取「Directory which includes subdirectories」(包含子目錄的目錄)。
來源刪除選項:根據偏好選取刪除選項。
進階選項
- 動態饋給名稱:系統預先填入的值,用於識別動態饋給。
- 來源類型:將記錄收集到 Google SecOps 的方法。
- 資產命名空間:與動態饋給相關聯的命名空間。
- 擷取標籤:套用至這個動態饋給所有事件的標籤。
UDM 對應表
記錄欄位 | UDM 對應 | 邏輯 |
---|---|---|
CLIENT-IP | target.ip | 從 CLIENT-IP 標頭欄位擷取。 |
CLIENT-IP | target.port | 從 CLIENT-IP 標頭欄位擷取。 |
連線 | security_result.about.labels | 這個值取自原始記錄的 Connection 欄位,並對應至 security_result.about.labels 。 |
Content-Length | security_result.about.labels | 這個值取自原始記錄的 Content-Length 欄位,並對應至 security_result.about.labels 。 |
Content-Type | security_result.about.labels | 這個值取自原始記錄的 Content-Type 欄位,並對應至 security_result.about.labels 。 |
已建立 | metadata.event_timestamp | 這個值取自原始記錄的 created 欄位,並對應至 metadata.event_timestamp 。 |
details.headersIn | security_result.about.resource.attribute.labels | 這個值取自原始記錄的 details.headersIn 欄位,並對應至 security_result.about.resource.attribute.labels 。 |
details.headersOut | security_result.about.resource.attribute.labels | 這個值取自原始記錄的 details.headersOut 欄位,並對應至 security_result.about.resource.attribute.labels 。 |
details.id | principal.process.pid | 這個值取自原始記錄的 details.id 欄位,並對應至 principal.process.pid 。 |
details.method | network.http.method | 這個值取自原始記錄的 details.method 欄位,並對應至 network.http.method 。 |
details.protocol | network.application_protocol | 這個值取自原始記錄的 details.protocol 欄位,並對應至 network.application_protocol 。 |
details.remoteCountryCode | principal.location.country_or_region | 這個值取自原始記錄的 details.remoteCountryCode 欄位,並對應至 principal.location.country_or_region 。 |
details.remoteHostname | target.hostname | 這個值取自原始記錄的 details.remoteHostname 欄位,並對應至 target.hostname 。 |
details.remoteIP | target.ip | 這個值取自原始記錄的 details.remoteIP 欄位,並對應至 target.ip 。 |
details.responseCode | network.http.response_code | 這個值取自原始記錄的 details.responseCode 欄位,並對應至 network.http.response_code 。 |
details.responseSize | network.received_bytes | 這個值取自原始記錄的 details.responseSize 欄位,並對應至 network.received_bytes 。 |
details.serverHostname | principal.hostname | 這個值取自原始記錄的 details.serverHostname 欄位,並對應至 principal.hostname 。 |
details.serverName | principal.asset.network_domain | 這個值取自原始記錄的 details.serverName 欄位,並對應至 principal.asset.network_domain 。 |
details.tags | security_result.detection_fields | 這個值取自原始記錄的 details.tags 欄位,並對應至 security_result.detection_fields 。 |
details.tlsCipher | network.tls.cipher | 這個值取自原始記錄的 details.tlsCipher 欄位,並對應至 network.tls.cipher 。 |
details.tlsProtocol | network.tls.version | 這個值取自原始記錄的 details.tlsProtocol 欄位,並對應至 network.tls.version 。 |
details.userAgent | network.http.user_agent | 這個值取自原始記錄的 details.userAgent 欄位,並對應至 network.http.user_agent 。 |
details.uri | network.http.referral_url | 這個值取自原始記錄的 details.uri 欄位,並對應至 network.http.referral_url 。 |
eventType | metadata.product_event_type | 這個值取自原始記錄的 eventType 欄位,並對應至 metadata.product_event_type 。 |
headersIn | security_result.about.labels | 這個值取自原始記錄的 headersIn 欄位,並對應至 security_result.about.labels 。 |
headersOut | security_result.about.labels | 這個值取自原始記錄的 headersOut 欄位,並對應至 security_result.about.labels 。 |
id | principal.process.pid | 這個值取自原始記錄的 id 欄位,並對應至 principal.process.pid 。 |
訊息 | metadata.description | 這個值取自原始記錄的 message 欄位,並對應至 metadata.description 。 |
方法 | network.http.method | 這個值取自原始記錄的 method 欄位,並對應至 network.http.method 。 |
ModuleVersion | metadata.ingestion_labels | 這個值取自原始記錄的 ModuleVersion 欄位,並對應至 metadata.ingestion_labels 。 |
msgData.actions | security_result.action | 這個值取自原始記錄的 msgData.actions 欄位,並對應至 security_result.action 。 |
msgData.changes | target.resource.attribute.labels | 這個值取自原始記錄的 msgData.changes 欄位,並對應至 target.resource.attribute.labels 。 |
msgData.conditions | security_result.description | 這個值取自原始記錄的 msgData.conditions 欄位,並對應至 security_result.description 。 |
msgData.detailLink | network.http.referral_url | 這個值取自原始記錄的 msgData.detailLink 欄位,並對應至 network.http.referral_url 。 |
msgData.name | target.resource.name | 這個值取自原始記錄的 msgData.name 欄位,並對應至 target.resource.name 。 |
msgData.reason | security_result.summary | 這個值取自原始記錄的 msgData.reason 欄位,並對應至 security_result.summary 。 |
msgData.sites | network.http.user_agent | 這個值取自原始記錄的 msgData.sites 欄位,並對應至 network.http.user_agent 。 |
通訊協定 | network.application_protocol | 這個值取自原始記錄的 protocol 欄位,並對應至 network.application_protocol 。 |
remoteCountryCode | principal.location.country_or_region | 這個值取自原始記錄的 remoteCountryCode 欄位,並對應至 principal.location.country_or_region 。 |
remoteHostname | target.hostname | 這個值取自原始記錄的 remoteHostname 欄位,並對應至 target.hostname 。 |
remoteIP | target.ip | 這個值取自原始記錄的 remoteIP 欄位,並對應至 target.ip 。 |
responseCode | network.http.response_code | 這個值取自原始記錄的 responseCode 欄位,並對應至 network.http.response_code 。 |
responseSize | network.received_bytes | 這個值取自原始記錄的 responseSize 欄位,並對應至 network.received_bytes 。 |
serverHostname | principal.hostname | 這個值取自原始記錄的 serverHostname 欄位,並對應至 principal.hostname 。 |
serverName | principal.asset.network_domain | 這個值取自原始記錄的 serverName 欄位,並對應至 principal.asset.network_domain 。 |
標記 | security_result.detection_fields | 這個值取自原始記錄的 tags 欄位,並對應至 security_result.detection_fields 。 |
時間戳記 | metadata.event_timestamp | 這個值取自原始記錄的 timestamp 欄位,並對應至 metadata.event_timestamp 。 |
tlsCipher | network.tls.cipher | 這個值取自原始記錄的 tlsCipher 欄位,並對應至 network.tls.cipher 。 |
tlsProtocol | network.tls.version | 這個值取自原始記錄的 tlsProtocol 欄位,並對應至 network.tls.version 。 |
URI | target.url | 這個值取自原始記錄的 URI 欄位,並對應至 target.url 。 |
userAgent | network.http.user_agent | 這個值取自原始記錄的 userAgent 欄位,並對應至 network.http.user_agent 。 |
uri | network.http.referral_url | 這個值取自原始記錄的 uri 欄位,並對應至 network.http.referral_url 。 |
X-ARR-SSL | network.tls.client.certificate.issuer | 系統會使用 grok 和 kv 篩選器,從 X-ARR-SSL 標頭欄位擷取值。 |
metadata.event_type | 剖析器會根據目標和主體資訊是否存在,判斷事件類型。如果目標和主體都存在,事件類型為 NETWORK_HTTP 。如果只有主體,事件類型為 STATUS_UPDATE 。否則事件類型為 GENERIC_EVENT 。 |
|
metadata.log_type | 值會硬式編碼為 SIGNAL_SCIENCES_WAF 。 |
|
metadata.product_name | 值會硬式編碼為 Signal Sciences WAF 。 |
|
metadata.vendor_name | 值會硬式編碼為 Signal Sciences 。 |
|
principal.asset.hostname | 這個值取自 principal.hostname 欄位。 |
|
target.asset.hostname | 這個值取自 target.hostname 欄位。 |
|
target.asset.ip | 這個值取自 target.ip 欄位。 |
|
target.user.user_display_name | 系統會使用 grok 篩選器,從 message_data 欄位擷取值。 |
|
target.user.userid | 系統會使用 grok 篩選器,從 message_data 欄位擷取值。 |
還有其他問題嗎?向社群成員和 Google SecOps 專業人員尋求答案。