# Bảo vệ dữ liệu và lọc thông tin nhạy cảm

> Những gì SDK và node tự xoá hoặc che, những gì không thể đảm bảo, và cách kiểm tra an toàn.

Báo cáo lỗi có thể mang theo những thứ bạn không hề định thu thập: mật khẩu trong một ô nhập liệu, token trong URL, email trong một câu thông báo. Belvador lọc ở hai lớp độc lập — trong ứng dụng của bạn và trên node của bạn — và giữ dữ liệu trong chính project Hetzner của bạn. Lọc giúp giảm rủi ro; không xoá được rủi ro.

## Dữ liệu của bạn đi đâu

| Nơi | Chứa gì | Ai đọc được |
|---|---|---|
| Ứng dụng của bạn | Event trước khi gửi; SDK lọc trước | Code của bạn |
| Edge của Belvador (`ingest.belvador.dev`) | Event đang trên đường đi. Nếu node mất kết nối, một bản đã niêm phong bằng khoá của node được giữ tạm tới 14 ngày | Chỉ node của bạn mở được dữ liệu đã niêm phong |
| Edge của Belvador (`edge.belvador.dev`) | Không lưu gì. Khi người trong workspace mở lỗi, trace hay log, edge chuyển dữ liệu từ node tới trình duyệt của họ trong bộ nhớ, sau khi kiểm vé ngắn hạn | Không ai — không giữ lại gì |
| **Node của bạn**, trong project Hetzner của bạn | Event, issue, tệp đính kèm, source map — được lọc lại trước khi lưu | Người trong workspace của bạn, qua ứng dụng |
| Phần điều khiển của Belvador | Thông tin tài khoản, workspace, project, cấu hình và số đếm | Belvador, để vận hành dịch vụ |
| Dịch vụ heartbeat của Belvador (`ping.belvador.dev`) | Chỉ cho [monitor ping URL](https://belvador.dev/vi/docs/monitors/heartbeat): dấu vân tay của từng ping URL, tín hiệu nào tới và lúc nào, hạn chờ và trạng thái sự cố — không bao giờ có body, header hay địa chỉ IP (output job gửi kèm đi về node của bạn, như một event) | Belvador, để vẫn báo khi mất ping ngay cả lúc node của bạn ngừng |
| Webhook của chính bạn | Nếu bạn thêm: metadata của cảnh báo (tên, mã, thời điểm, link dashboard), có chữ ký — xem [Webhook tự cấu hình](https://belvador.dev/vi/docs/alerts/webhooks) | Người vận hành địa chỉ đó |
| Backup trong Cloudflare R2 | Bản sao đã mã hoá của dữ liệu node | Người giữ khoá backup — xem [Backup](https://belvador.dev/vi/docs/node/backups) |

Event được giữ theo thời hạn lưu của gói (mặc định 30 ngày); số liệu tổng hợp theo ngày giữ 13 tháng.

## Lớp 1 — SDK, trước khi bất cứ thứ gì rời ứng dụng

SDK của Belvador (`@belvador/*`) luôn:

- **xoá** các header `Authorization`, `Proxy-Authorization`, `Cookie`, `Set-Cookie`, `X-Forwarded-For`, `X-Real-IP`, `X-Client-IP`, `CF-Connecting-IP`, `True-Client-IP` và `Forwarded`;
- **xoá** body request, cookie và dữ liệu môi trường trong thông tin request;
- **chỉ giữ `user.id`** trong dữ liệu người dùng (tối đa 128 ký tự) — email, tên và địa chỉ IP bị bỏ;
- **che** thành `[Filtered]` mọi giá trị nằm dưới tên nhạy cảm — tên chứa `password`, `passwd`, `pwd`, `secret`, `api_key`/`apikey`, `access_token`, `auth`, `credentials`, `credit_card`/`card_number`, `cvv`, `private_key`, `session`, `cookie`, `token`, `bearer`, `ssn` hoặc `dob` — trong dữ liệu lồng nhau, tag, ngữ cảnh, dữ liệu extra, breadcrumb và query string;
- **che trong văn bản**: thông tin đăng nhập trong URL, cặp `name=value` và chuỗi JSON có tên nhạy cảm, bearer token và các dòng `Authorization: <scheme> <giá trị>` khác, JWT, khoá có tiền tố của nhà cung cấp (Stripe, GitHub, GitLab, OpenAI, Anthropic, AWS access key ID, Slack, Google API key, Stripe webhook secret, npm, Supabase secret key và khoá ingest của Belvador; trừ Stripe, tất cả từ SDK 0.5.2), phần đường dẫn bí mật trong URL webhook của Slack, Discord và URL bot Telegram, số thẻ vượt qua kiểm tra Luhn, phần trước `@` của địa chỉ email kể cả khi bị mã hoá thành `jane%40example.com` (giữ tên miền), và phần cuối của địa chỉ IPv4 công khai.

Dữ liệu sâu quá 10 cấp hoặc quá 1.000 khoá sẽ không được duyệt tiếp. Hook `beforeSend` của bạn chạy **trước** bước lọc này.

Cách cài [Node.js](https://belvador.dev/vi/docs/sdk/node) và [Python](https://belvador.dev/vi/docs/sdk/python) thêm một hook `beforeSend` dùng cùng danh sách tên và các quy tắc văn bản tương tự.

## Lớp 2 — node của bạn, trước khi lưu

Node chạy bộ lọc riêng, viết tách khỏi SDK để một lỗi không thể làm tắt cả hai:

- cùng danh sách tên nhạy cảm, che thành `[Filtered]`;
- mẫu cho bearer token và các dòng `Authorization: <scheme> <giá trị>` khác, khoá bí mật Stripe, JWT, số thẻ và địa chỉ email (kể cả dạng mã hoá `%40`);
- từ node 0.1.31, cùng các khoá có tiền tố nhà cung cấp và đường dẫn webhook như SDK;
- xoá tên đăng nhập và mật khẩu trong URL kiểu `https://user:pass@db.example.com`;
- che địa chỉ IP: phần cuối của IPv4 về 0, IPv6 chỉ giữ 48 bit đầu. Trong văn bản tự do, ví dụ message của lỗi, IPv4 công khai cũng bị che như vậy; địa chỉ nội bộ, loopback, CGNAT và link-local (`10.0.3.17`, `127.0.0.1`) được giữ nguyên vì thường là máy chủ của chính bạn;
- ID người dùng được thay bằng mã băm có muối;
- xoá các header `Authorization`, `Cookie`, `Set-Cookie` và `Proxy-Authorization`.

Bộ lọc áp dụng cho giá trị exception, biến và đoạn code trong stack frame, breadcrumb, tag, ngữ cảnh, dữ liệu extra, message, thông tin người dùng và dữ liệu request.

### Bộ dò có sẵn

**Project → Quyền riêng tư → Bộ dò có sẵn** liệt kê các nhóm này; mặc định bật hết. Tắt một nhóm nghĩa là dữ liệu loại đó do SDK gửi sẽ được lưu nguyên và mọi người trong workspace đều thấy. Chỉ owner và admin đổi được; tắt một nhóm cần mã xác thực, owner nhận email và mọi thay đổi được ghi vào nhật ký.

## Lớp 3 — luật của project (trang Quyền riêng tư)

**Project → Quyền riêng tư** quyết định node không bao giờ lưu gì cho project đó. Node áp luật trước khi ghi bất cứ thứ gì — cho lỗi, span và log OpenTelemetry — từ agent node 0.1.36. Thay đổi đã lưu áp cho dữ liệu đến sau; dữ liệu đã lưu không bị viết lại.

Thứ tự luôn cố định:

1. **Danh sách cho phép** (chỉ ở chế độ Nghiêm ngặt). Nghiêm ngặt giữ mức tối thiểu mà một lỗi, span hay log cần — mức lỗi, loại lỗi, stack frame không có biến, release, môi trường, trace id — cộng các trường bạn liệt kê. Tiêu chuẩn giữ mọi thứ mà các bước sau không xoá.
2. **Luôn xoá**, dù project đặt gì (không tắt được, không đưa vào danh sách cho phép được): cookie, header xác thực, mã dùng một lần và mã xác minh, mật khẩu, secret và khoá riêng, body của request, response và tin nhắn, số thẻ (kiểm Luhn) và CVV, biến của stack frame.
3. **Luật của bạn**, theo thứ tự, tối đa 100.
4. **Bộ dò có sẵn** (lớp 2).

Mỗi luật gồm selector, loại và cách xử lý:

| Phần | Giá trị |
|---|---|
| Selector | Đường dẫn viết thường như `extra.phone`, `request.headers.x-api-key` hay `contexts.*.token`; `*` là một cấp, `**` là mọi độ sâu. Selector có kiểu: `$string` (mọi giá trị chữ), `$request`, `$user`, `$frame.vars`, `$log.attributes`, `$span.attributes`. Kết hợp bằng `&&`, `\|\|` và `!`. Nội dung của một log là `message`. |
| Loại | `anything` (toàn bộ giá trị), `pattern` (biểu thức chính quy), `creditcard`, `ip`, `email`, `phone_e164` (số quốc tế như `+84901234567`) |
| Cách | `remove` (xoá), `replace` (thay bằng chữ cố định tối đa 32 ký tự), `mask` (giữ 2 ký tự cuối), `hash` (mã băm có khoá riêng của project, 16 ký tự hex: cùng đầu vào cho cùng kết quả, nên vẫn đếm và tìm được) |

Pattern phải an toàn để chạy trên mọi event: tham chiếu ngược, look-around, lặp lồng nhau kiểu `(a+)+` và pattern dài hơn 256 ký tự bị từ chối khi lưu. Mỗi giá trị chữ được kiểm tối đa 16 KiB; nếu một payload chạy quá ngân sách thời gian, các trường còn lại mà luật chạm tới bị xoá và trang có đếm.

**Thử luật** chạy luật ngay trong trình duyệt trên mẫu bạn dán hoặc mẫu tổng hợp — mẫu không bao giờ được gửi đi. Kết quả là payload sau nhóm luôn xoá và luật của bạn (bộ dò có sẵn chạy sau đó, trên node), kèm luật nào khớp đường dẫn nào, không bao giờ hiện lại giá trị bị khớp. Mã băm hiện là `[hash]` vì chỉ node giữ khoá.

**Lưu** tạo một phiên bản mới; **Lịch sử phiên bản** cho biết ai đổi gì, lúc nào, kèm phần khác nhau. Thay đổi giữ lại nhiều dữ liệu hơn — xoá hoặc tắt một luật, chuyển về Tiêu chuẩn, thêm vào danh sách cho phép, tắt một bộ dò có sẵn — cần mã xác thực; owner nhận email và thay đổi được ghi vào nhật ký. Owner và admin sửa được; thành viên khác chỉ xem. Qua API và MCP, `privacy.get` đọc chính sách còn `privacy.set` luôn chờ một người duyệt.

**Trạng thái node.** Node báo phiên bản đang áp. Nếu node không có chính sách hợp lệ — ví dụ không xác minh được cấu hình — node chỉ lưu mức tối thiểu nghiêm ngặt và đếm các payload đó; trang hiện con số này.

### Cùng luật đó ngay trong app (tuỳ chọn)

Từ SDK 0.6.0, app có thể áp luật của project trước khi bất cứ thứ gì rời app, bằng đúng engine node đang chạy. **Sao chép dạng code** trên trang Quyền riêng tư sao chép chính sách đã lưu dưới dạng JSON; lưu nó trong app và truyền vào `init`:

```ts
import { init, privacyPolicy } from "@belvador/browser"; // mọi gói @belvador/* đều có
import policy from "./belvador-privacy.json";

init({ dsn: "https://<public-key>@ingest.belvador.dev/<project-id>", privacyPolicy: privacyPolicy(policy) });
```

Node vẫn áp chính sách hiện tại của project, nên app giữ bản cũ không bao giờ kém an toàn hơn — sao chép lại sau mỗi lần đổi nếu muốn lọc cả trong app. JSON không phải chính sách hợp lệ thì không được áp và báo [`BLV_PRIVACY_POLICY_INVALID`](https://belvador.dev/vi/docs/sdk/verify#blv-privacy-policy-invalid). Engine luật chỉ được đóng gói vào app có import `privacyPolicy`.

**Xem đúng thứ sẽ được gửi.** `preview: true` trong `init` (hoặc `BELVADOR_PREVIEW=1` trên máy chủ) in từng envelope — sau `beforeSend` của bạn, các luật này và bộ che của Belvador — ra console (stderr trên máy chủ) và không gửi gì. Dùng khi đang phát triển; tắt đi để gửi.

### Monitor và trace

Một check-in mang slug của monitor, trạng thái, thời lượng, môi trường, release, số lần thử, một mã lý do ngắn (nếu có) và trace ID — không có nội dung thông báo, dữ liệu request hay dữ liệu người dùng. Check-in được lưu trên node của bạn. Cơ sở dữ liệu của Belvador chỉ giữ cấu hình và trạng thái của từng monitor (khoẻ, hỏng, từ lúc nào) cùng các sự cố, để trang và cảnh báo vẫn chạy khi node mất kết nối. Trace ID là mã ngẫu nhiên; được lưu cùng lỗi và lượt chạy trên node, và không bao giờ có trong tin cảnh báo. Span và log OpenTelemetry bạn gửi cũng chỉ được lưu trên node của bạn và được lọc ở đó trước — xem [Quyền riêng tư OpenTelemetry](https://belvador.dev/vi/docs/otel/privacy).

## Những gì bộ lọc không đảm bảo

- **Tên lạ.** Bí mật nằm dưới tên như `x` hay `value` sẽ không được nhận ra.
- **Văn bản tự do.** Một câu như `` `Login failed for jane@example.com with Hunter2!` `` sẽ được che một phần email, nhưng mật khẩu chỉ là chữ bình thường. Tên người, số điện thoại, địa chỉ nhà trong câu không được nhận ra, IPv6 viết giữa câu cũng vậy.
- **Tên quá rộng.** Bộ lọc so theo kiểu "có chứa", nên cả những tên vô hại như `author`, `session_count` hay `token_count` cũng bị che. Đổi tên trường nếu bạn cần đọc giá trị.
- **Tệp đính kèm** được kiểm tra kích thước, không được lọc nội dung. Chỉ bật `attachments` cho những tệp bạn biết là an toàn.
- **Metadata của bạn.** `release`, `environment`, fingerprint và *tên* tag được gửi nguyên văn.
- **SDK khác.** Nếu bạn giữ một SDK không phải của Belvador, mặc định của SDK đó áp dụng cho tới khi bộ lọc của node chạy.

## Thói quen an toàn

- Không bao giờ đưa bí mật, token hoặc nguyên object người dùng vào ngữ cảnh của `captureException`, tag hay `setContext`.
- Dùng `setUser({ id })` với ID nội bộ, không dùng email.
- Thử bộ lọc bằng giá trị **giả**, ví dụ `password=not-a-real-secret` — không bao giờ dùng thông tin đăng nhập thật.
- Nếu một bí mật thật lỡ tới Belvador, hãy **đổi bí mật đó** trước tiên; xoá event không làm mất việc nó đã bị lộ.

## Pháp lý

Trang [Bảo mật](https://belvador.dev/vi/security) mô tả các biện pháp của Belvador và cách báo lỗ hổng. Trang [Xử lý dữ liệu](https://belvador.dev/vi/data-processing) giải thích vai trò theo luật bảo vệ dữ liệu.
