Mengumpulkan log JSON Kolaborasi Box
Dokumen ini menjelaskan cara menyerap log JSON Kolaborasi Box ke Google Security Operations menggunakan AWS S3 menggunakan jadwal Lambda dan EventBridge. Parser memproses log peristiwa Box dalam format JSON, memetakannya ke model data terpadu (UDM). Alat ini mengekstrak kolom yang relevan dari log mentah, melakukan transformasi data seperti mengganti nama dan menggabungkan, serta memperkaya data dengan informasi perantara sebelum menghasilkan data peristiwa terstruktur.
Sebelum memulai
- Instance Google SecOps
- Akses istimewa ke Box (Konsol Admin + Developer)
- Akses istimewa ke AWS (S3, IAM, Lambda, EventBridge) di Region yang sama dengan tempat Anda berencana menyimpan log
Mengonfigurasi Konsol Developer Box (Kredensial Klien)
- Login ke Box Developer Console.
- Buat Aplikasi Kustom dengan Autentikasi Server (Pemberian Kredensial Klien).
- Tetapkan Akses Aplikasi = Akses Aplikasi + Perusahaan.
- Di Application Scopes, aktifkan Manage enterprise properties.
- Di Konsol Admin > Aplikasi > Pengelola Aplikasi Kustom, Beri otorisasi aplikasi dengan ID Klien.
- Salin dan simpan Client ID dan * Client Secret di lokasi yang aman.
- Buka Konsol Admin > Akun & Penagihan > Informasi Akun.
- Salin dan simpan ID Perusahaan di lokasi yang aman.
Mengonfigurasi bucket AWS S3 dan IAM untuk Google SecOps
- Buat bucket Amazon S3 dengan mengikuti panduan pengguna ini: Membuat bucket
- Simpan Name dan Region bucket untuk referensi di masa mendatang (misalnya,
box-collaboration-logs
). - Buat pengguna dengan mengikuti panduan pengguna ini: Membuat pengguna IAM.
- Pilih Pengguna yang dibuat.
- Pilih tab Kredensial keamanan.
- Klik Create Access Key di bagian Access Keys.
- Pilih Layanan pihak ketiga sebagai Kasus penggunaan.
- Klik Berikutnya.
- Opsional: tambahkan tag deskripsi.
- Klik Create access key.
- Klik Download CSV file untuk menyimpan Access Key dan Secret Access Key untuk digunakan nanti.
- Klik Selesai.
- Pilih tab Izin.
- Klik Tambahkan izin di bagian Kebijakan izin.
- Pilih Tambahkan izin.
- Pilih Lampirkan kebijakan secara langsung
- Telusuri dan pilih kebijakan AmazonS3FullAccess.
- Klik Berikutnya.
- Klik Add permissions.
Mengonfigurasi kebijakan dan peran IAM untuk upload S3
- Di konsol AWS, buka IAM > Policies > Create policy > JSON tab.
Masukkan kebijakan berikut:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPutBoxObjects", "Effect": "Allow", "Action": ["s3:PutObject"], "Resource": "arn:aws:s3:::box-collaboration-logs/*" }, { "Sid": "AllowGetStateObject", "Effect": "Allow", "Action": ["s3:GetObject"], "Resource": "arn:aws:s3:::box-collaboration-logs/box/collaboration/state.json" } ] }
- Ganti
box-collaboration-logs
jika Anda memasukkan nama bucket yang berbeda.
- Ganti
Klik Berikutnya > Buat kebijakan.
Buka IAM > Roles > Create role > AWS service > Lambda.
Lampirkan kebijakan yang baru dibuat.
Beri nama peran
WriteBoxToS3Role
, lalu klik Buat peran.
Buat fungsi Lambda
- Di Konsol AWS, buka Lambda > Functions > Create function.
- Klik Buat dari awal.
Berikan detail konfigurasi berikut:
Setelan Nilai Nama box_collaboration_to_s3
Runtime Python 3.13 Arsitektur x86_64 Peran eksekusi WriteBoxToS3Role
Setelah fungsi dibuat, buka tab Code, hapus stub, lalu masukkan kode berikut (
box_collaboration_to_s3.py
):#!/usr/bin/env python3 # Lambda: Pull Box Enterprise Events to S3 (no transform) import os, json, time, urllib.parse from urllib.request import Request, urlopen from urllib.error import HTTPError, URLError import boto3 TOKEN_URL = "https://api.box.com/oauth2/token" EVENTS_URL = "https://api.box.com/2.0/events" CID = os.environ["BOX_CLIENT_ID"] CSECRET = os.environ["BOX_CLIENT_SECRET"] ENT_ID = os.environ["BOX_ENTERPRISE_ID"] STREAM_TYPE = os.environ.get("STREAM_TYPE", "admin_logs_streaming") LIMIT = int(os.environ.get("LIMIT", "500")) BUCKET = os.environ["S3_BUCKET"] PREFIX = os.environ.get("S3_PREFIX", "box/collaboration/") STATE_KEY = os.environ.get("STATE_KEY", "box/collaboration/state.json") s3 = boto3.client("s3") def get_state(): try: obj = s3.get_object(Bucket=BUCKET, Key=STATE_KEY) data = json.loads(obj["Body"].read()) return data.get("stream_position") except Exception: return None def put_state(pos): body = json.dumps({"stream_position": pos}, separators=(",", ":")).encode("utf-8") s3.put_object(Bucket=BUCKET, Key=STATE_KEY, Body=body, ContentType="application/json") def get_token(): body = urllib.parse.urlencode({ "grant_type": "client_credentials", "client_id": CID, "client_secret": CSECRET, "box_subject_type": "enterprise", "box_subject_id": ENT_ID, }).encode() req = Request(TOKEN_URL, data=body, method="POST") req.add_header("Content-Type", "application/x-www-form-urlencoded") with urlopen(req, timeout=30) as r: tok = json.loads(r.read().decode()) return tok["access_token"] def fetch_events(token, stream_position=None, timeout=60, max_retries=5): params = {"stream_type": STREAM_TYPE, "limit": LIMIT, "stream_position": stream_position or "now"} qs = urllib.parse.urlencode(params) attempt, backoff = 0, 1.0 while True: try: req = Request(f"{EVENTS_URL}?{qs}", method="GET") req.add_header("Authorization", f"Bearer {token}") with urlopen(req, timeout=timeout) as r: return json.loads(r.read().decode()) except HTTPError as e: if e.code == 429 and attempt < max_retries: ra = e.headers.get("Retry-After") delay = int(ra) if (ra and ra.isdigit()) else int(backoff) time.sleep(max(1, delay)); attempt += 1; backoff *= 2; continue if 500 <= e.code <= 599 and attempt < max_retries: time.sleep(backoff); attempt += 1; backoff *= 2; continue raise except URLError: if attempt < max_retries: time.sleep(backoff); attempt += 1; backoff *= 2; continue raise def write_chunk(data): ts = time.strftime("%Y/%m/%d/%H%M%S", time.gmtime()) key = f"{PREFIX}/{ts}-box-events.json" s3.put_object(Bucket=BUCKET, Key=key, Body=json.dumps(data, separators=(",", ":")).encode("utf-8"), ContentType="application/json") return key def lambda_handler(event=None, context=None): token = get_token() pos = get_state() total, idx = 0, 0 while True: page = fetch_events(token, pos) entries = page.get("entries") or [] if not entries: next_pos = page.get("next_stream_position") or pos if next_pos and next_pos != pos: put_state(next_pos) break # уникальный ключ ts = time.strftime("%Y/%m/%d/%H%M%S", time.gmtime()) key = f"{PREFIX}/{ts}-box-events-{idx:03d}.json" s3.put_object(Bucket=BUCKET, Key=key, Body=json.dumps(page, separators=(",", ":")).encode("utf-8"), ContentType="application/json") idx += 1 total += len(entries) pos = page.get("next_stream_position") or pos if pos: put_state(pos) if len(entries) < LIMIT: break return {"ok": True, "written": total, "next_stream_position": pos}
Buka Configuration > Environment variables > Edit > Add new environment variable.
Masukkan variabel lingkungan berikut, ganti dengan nilai Anda:
Kunci Contoh S3_BUCKET
box-collaboration-logs
S3_PREFIX
box/collaboration/
STATE_KEY
box/collaboration/state.json
BOX_CLIENT_ID
Masukkan ID Klien Box BOX_CLIENT_SECRET
Masukkan Rahasia Klien Box BOX_ENTERPRISE_ID
Masukkan ID Box Enterprise STREAM_TYPE
admin_logs_streaming
LIMIT
500
Setelah fungsi dibuat, tetap buka halamannya (atau buka Lambda > Functions > your-function).
Pilih tab Configuration
Di panel Konfigurasi umum, klik Edit.
Ubah Waktu tunggu menjadi 10 menit (600 detik), lalu klik Simpan.
Jadwalkan fungsi Lambda (EventBridge Scheduler)
- Buka Amazon EventBridge > Scheduler > Create schedule.
- Berikan detail konfigurasi berikut:
- Jadwal berulang: Tarif (
15 min
). - Target: fungsi Lambda Anda.
- Name:
box-collaboration-schedule-15min
.
- Jadwal berulang: Tarif (
- Klik Buat jadwal.
Mengonfigurasi feed di Google SecOps untuk menyerap log Box
- Buka Setelan SIEM > Feed.
- Klik Tambahkan Feed Baru.
- Di kolom Nama feed, masukkan nama untuk feed (misalnya,
Box Collaboration
). - Pilih Amazon S3 V2 sebagai Jenis sumber.
- Pilih Box sebagai Jenis log.
- Klik Berikutnya.
- Tentukan nilai untuk parameter input berikut:
- URI S3: URI bucket (formatnya harus:
s3://box-collaboration-logs/box/collaboration/
). Gantibox-collaboration-logs
: Gunakan nama bucket yang sebenarnya. - Opsi penghapusan sumber: Pilih opsi penghapusan sesuai preferensi Anda.
- Usia File Maksimum: Menyertakan file yang diubah dalam beberapa hari terakhir. Defaultnya adalah 180 Hari.
- ID Kunci Akses: Kunci akses pengguna dengan akses ke bucket S3.
- Kunci Akses Rahasia: Kunci rahasia pengguna dengan akses ke bucket S3.
- Namespace aset: Namespace aset.
- Label penyerapan: Label yang akan diterapkan ke peristiwa dari feed ini.
- URI S3: URI bucket (formatnya harus:
- Klik Berikutnya.
- Tinjau konfigurasi feed baru Anda di layar Selesaikan, lalu klik Kirim.
Tabel Pemetaan UDM
Kolom log | Pemetaan UDM | Logika |
---|---|---|
additional_details.ekm_id | additional.fields | Nilai yang diambil dari additional_details.ekm_id |
additional_details.service_id | additional.fields | Nilai diambil dari additional_details.service_id |
additional_details.service_name | additional.fields | Nilai diambil dari additional_details.service_name |
additional_details.shared_link_id | additional.fields | Nilai diambil dari additional_details.shared_link_id |
additional_details.size | target.file.size | Nilai diambil dari additional_details.size |
additional_details.version_id | additional.fields | Nilai diambil dari additional_details.version_id |
created_at | metadata.event_timestamp | Nilai diambil dari created_at |
created_by.id | principal.user.userid | Nilai diambil dari created_by.id |
created_by.login | principal.user.email_addresses | Nilai diambil dari created_by.login |
created_by.name | principal.user.user_display_name | Nilai diambil dari created_by.name |
event_id | metadata.product_log_id | Nilai diambil dari event_id |
event_type | metadata.product_event_type | Nilai diambil dari event_type |
ip_address | principal.ip | Nilai diambil dari ip_address |
source.item_id | target.file.product_object_id | Nilai yang diambil dari source.item_id |
source.item_name | target.file.full_path | Nilai yang diambil dari source.item_name |
source.item_type | Tidak dipetakan | |
source.login | target.user.email_addresses | Nilai yang diambil dari source.login |
source.name | target.user.user_display_name | Nilai diambil dari source.name |
source.owned_by.id | target.user.userid | Nilai diambil dari source.owned_by.id |
source.owned_by.login | target.user.email_addresses | Nilai yang diambil dari source.owned_by.login |
source.owned_by.name | target.user.user_display_name | Nilai diambil dari source.owned_by.name |
source.parent.id | Tidak dipetakan | |
source.parent.name | Tidak dipetakan | |
source.parent.type | Tidak dipetakan | |
source.type | Tidak dipetakan | |
jenis | metadata.log_type | Nilai diambil dari jenis |
metadata.vendor_name | Nilai yang di-hardcode | |
metadata.product_name | Nilai yang di-hardcode | |
security_result.action | Diperoleh dari event_type. Jika event_type adalah FAILED_LOGIN, maka BLOCK, jika event_type adalah USER_LOGIN, maka ALLOW, jika tidak, UNSPECIFIED. | |
extensions.auth.type | Diperoleh dari event_type. Jika event_type adalah USER_LOGIN atau ADMIN_LOGIN, maka MACHINE, jika tidak, UNSPECIFIED. | |
extensions.auth.mechanism | Diperoleh dari event_type. Jika event_type adalah USER_LOGIN atau ADMIN_LOGIN, maka USERNAME_PASSWORD, jika tidak, UNSPECIFIED. |
Perlu bantuan lain? Dapatkan jawaban dari anggota Komunitas dan profesional Google SecOps.