Froggy-the-creator/IDS_From_Scratch
GitHub: Froggy-the-creator/IDS_From_Scratch
一个从零构建的 Node.js 数据库活动监控命令行工具,提供加密存储、RBAC、防篡改审计日志和 Grafana/Loki 可观测性管道,旨在帮助开发者理解底层安全组件的运作原理。
Stars: 0 | Forks: 0
# 数据库活动监控器(从零构建)
一个命令行工具,用于监视小型加密数据存储上发生的操作——谁登录了、谁登录失败了、谁读取了什么、谁更改了用户——并将这些操作逐一记录到防篡改的审计追踪中。事件会以两种方式同时写入:作为权威记录写入 SQLite,以及输出到 JSON 日志流中,该日志流会被发送到 Grafana/Loki 用于仪表盘展示和警报提醒。
我没有拼凑现成的库,而是自己构建了这些组件——包括加密、审计日志、访问控制和数据库设置。这样做的目的是为了真正理解这些东西是如何运作的,而不是为了发布一个绝对安全的工具。
## 关于名称的说明
这个项目最初是作为一个 "IDS" 孵化的——这也是现在的仓库名称和 `ids` 命令的由来。随着开发的深入,我发现它实际上是一个**数据库活动监控器**,而不是入侵检测系统:它监视针对数据存储的活动并标记出有趣的部分,而不是检查流量或匹配攻击特征。我保留了这个最初的名字,因为我觉得它对这个项目的故事发展很重要。
## 功能介绍
- **基于角色的访问控制** — 包含三个角色(`anonymous`、`user`、`root`),每条命令在执行敏感操作前都会检查调用者的角色。
- **身份验证** — 密码基于每个用户的 salt 使用 SHA-256 进行哈希处理;数据库中只存储 salt 和哈希值,绝不存储明文密码。
- **加密** — 我自己编写的一种替换密码,每条记录都会使用新的随机 salt,确保相同的明文每次加密出的结果都不同。
- **防篡改日志** — 每个事件都会生成一个 SHA-256 指纹,因此记录一旦生成就无法被悄悄篡改,否则指纹就会匹配不上。
- **遵循归属规则的解密** — 普通用户只能解密自己的数据;root 可以解密任何人的数据,并且这种特权访问会以更高的严重级别被记录下来。
- **双写审计追踪** — 每个操作都会同时写入 SQLite *和* JSON 日志流中(具体原因见下文)。
## 架构设计
一个有趣的设计决策是,事件会被刻意存储在两个地方,因为这两种存储解决的是不同的问题。
```
┌─────────────► SQLite (events table)
│ authoritative, structured, queryable by SQL
action ──► logEvent ┤
│
└─────────────► JSON log file ──► Alloy ──► Loki ──► Grafana
append-only stream, for time-series + alerting
```
**SQLite** 是权威记录。它是结构化和关系型的。我可以将事件与用户关联,提取特定时间范围内的所有数据,查找单条记录并检查其指纹。这就是 `db`、`enq` 和 `encDb` 命令所读取的数据源。
**JSON 日志文件** 并不是直接给我看的——它的存在是为了让 [Grafana Alloy](https://grafana.com/docs/alloy/) 能够追踪(tail)它,并将每一行发送到 Loki。然后,Grafana 会查询 Loki 来构建仪表盘(例如,随着时间推移的登录失败次数、每个用户的事件数、解密他人数据的行为),并最终用于警报。Loki 只能接收日志*行*流——它无法直接读取 SQLite 文件——因此 JSON 流成为了连接整个可观测性侧的接口。
所以,同一个事件发挥着两个作用:SQLite 回答了 *“到底发生了什么,并且记录是否完好无损”*,而 Loki/Grafana 则回答了 *“这种事随着时间的发展趋势如何,以及有没有什么突发情况”*。每个事件还带有一个严重性级别(`info` / `warn` / `critical`,对于任何我尚未分类的类型,默认值为 `unknown`),以便仪表盘可以根据事件的重要程度进行过滤。这里的严重性仅仅是每种事件类型的一个基础权重——至于判断某事是否 *真正* 可疑(例如一分钟内登录失败 50 次还是只失败 1 次),则交给 Grafana 来判断,因为在那里可以清晰地看到这些模式,而不是将其固化在单条日志行中。
## 前置条件
- Node.js 22 LTS(推荐)
- npm
- Linux 用户可能需要用于原生依赖项的构建工具:
```
sudo apt update
sudo apt install build-essential
```
## 运行方式
你需要安装 Node.js。然后执行:
```
npm install
```
## 安装 CLI
要让 `ids` 命令全局可用:
```
npm link
```
如果你不想全局安装该命令,可以直接运行它:
```
node bin/ids.js
```
该工具需要一个加密密钥。复制示例环境变量文件并设置你自己的配置:
```
cp .env.example .env
```
然后打开 `.env` 并将 `DB_ENCRYPTION_KEY` 设置为一个长、随机且不含空格的字符串。生成它的一个好方法是:
```
openssl rand -hex 32
```
该密钥会在每次运行时被读取,因此只需设置一次。**请妥善保管,切勿遗失**。在只有一个主密钥且没有密钥恢复机制的情况下,如果密钥发生更改或遗失,在其加密下的任何内容都将永久无法读取。`.env` 文件已被 gitignore 忽略;切勿将其提交到代码仓库。
事件会以 JSON 格式写入到 `DAM_LOG_FILE`(默认为 `/var/log/dam/activity.log`),Alloy 会追踪该文件并将其发送到 Loki。请创建此目录,确保运行 `ids` 的用户拥有写权限——并且运行 Alloy 的用户拥有读权限,否则事件将无法送达 Loki:
```
sudo mkdir -p /var/log/dam && sudo chown $USER /var/log/dam
```
如果你将 `DAM_LOG_FILE` 指向了其他位置,请更新 Alloy 配置中的 `__path__` 以保持匹配。搭建 Loki、Alloy 和 Grafana 本身不在本文讨论范围内——请查阅它们各自的文档;本工具假定你已经拥有了一个运行中的技术栈。
基本用法:
```
ids status # see who you are
ids login # log in
ids encrypt # store an encrypted value
ids decrypt # decrypt one (your own, or any if you're root)
ids db # view the event log (root)
ids --help # commands available to your role
```
## 已知局限性
我宁愿坦诚面对这些不足,也不愿假装它们不存在:
- **加密算法是自制的。** 这是一个学习实践项目,而不是一个安全的加密算法。“不要自己搞加密”是行业铁律,而我之所以自己动手,是因为这个项目是在我真正了解到这一点之前开始的。我保留它是因为我在类似恩尼格玛密码机类型的加密上投入了大量精力。如果是正式版本,会使用经过严格审查的加密库中的认证加密算法。
- **所有数据共用一个密钥。** “你只能解密自己的数据”这一规则是在应用层强制执行的,而不是在加密机制层。每条记录都使用相同的 `DB_ENCRYPTION_KEY`,因此任何拥有该密钥和数据库的人都可以在工具外部进行解密。真正的用户隔离需要每个用户拥有独立的密钥。
- **解密时缺乏完整性校验。** 密钥错误会导致输出乱码而不是报错,因为该加密算法没有身份验证标签。存储在每个事件上的明文指纹可以用来检测这种情况,这是一个计划中的改进项。
- **指纹未进行链式连接。** 每个事件的指纹都是独立的,因此它只能证明单条记录未被篡改,却无法证明整个*历史记录*未被篡改。将每个指纹与上一个进行哈希链式连接,就能使任何篡改日志的行为破坏整个后续链条。
- **匿名操作者没有来源身份。** 已注销用户的登录失败会被记录为 `anonymous`,且没有 IP 或会话信息,因为这是一个本地 CLI。要将事件与来源绑定,需要引入该工具目前尚不具备的概念。
## 未来规划
- 对事件指纹进行哈希链式连接,以实现跨整个日志的防篡改能力,而不仅限于单条记录。
- 将 Grafana 端扩展为带有 SIEM 风格的功能:为暴力破解尝试、权限变更以及非工作时间的数据访问设置警报规则。
- 根据存储的指纹验证解密结果,确保密钥错误时能够发出明显的警报。
- 通过一个小型密钥环进行密钥轮换(每条记录存储一个密钥 *id*),确保旧数据在密钥更改后依然可读。
标签:Grafana, Homebrew安装, MITM代理, SQLite, Streamlit, 审计日志, 数据加密, 数据库监控, 自定义脚本, 访问控制