Files
full/docs/logs.md
2026-09-22 18:53:53 +08:00

136 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# logs业务操作日志代码审计报告
| 项 | 内容 |
| --- | --- |
| 审计对象 | `module/base/logs` |
| 服务域 | 基础与平台服务 |
| 审计日期 | 2026-09-22 |
| 代码规模 | Go 文件 12 个(全部手写,无 `proto`/生成代码) |
| 入口 | `cmd/main`Gin 单进程)、聚合入口 `pkgs/all``pkgs/ecmall` |
| 对外协议 | 仅原生 REST前缀 `/rest/logs` |
| 结论摘要 | 结构简单、职责清晰;但核心接口存在**过滤逻辑写反**、**取参键名不一致**、**分页参数未生效**、**类型断言可 panic**,且全部路由挂匿名组、无鉴权。 |
## 1. 服务定位与职责
集中保存与查询业务操作记录供后台追踪操作行为和排查问题。数据模型包含服务名、操作人、IP、数据类型、级别、内容等字段。它是业务日志的**存储与查询**服务,不负责进程日志采集、指标监控或链路追踪。
## 2. 代码结构与入口
| 路径 | 职责 |
| --- | --- |
| `cmd/main/main.go` | 独立进程入口:读配置 → 初始化 impl → 建 Gin → 注册路由 → 监听 `Spec.Port` |
| `cmd/cli/main.go` | 命令行辅助入口 |
| `internal/config/config.go` | 配置结构Base、Databases、Rpc、APM、Etcd与校验 |
| `internal/impl/impl.go` | 初始化 Redis、DB、Etcd 实例 |
| `internal/routers/register.go` | REST 路由注册(全部挂在匿名组) |
| `internal/logic/hello/ping.go` | 健康探测 |
| `internal/logic/log/{create,fetch,total}.go` | 创建、分页查询、按级别统计 |
| `internal/models/log_data.go` | `LogData` 模型 + 自动迁移注册 |
| `service/{expose,dependencies}.go` | 聚合宿主注入接口 |
依赖注入:`service.Dependencies` 支持外部传入 Redis/Etcd/DB/Cache非 nil 时覆盖 `internal/impl` 对象,供 `pkgs/all` 复用共享连接。
## 3. 接口清单
| 方法 | 路径 | 功能 | 鉴权 | 实现位置 |
| --- | --- | --- | --- | --- |
| GET | `/rest/logs/ping` | 健康探测,返回 `{"message":"Pong"}` | 无 | `logic/hello/ping.go` |
| POST | `/rest/logs/create` | 批量写入日志记录 | 无 | `logic/log/create.go` |
| POST | `/rest/logs/fetch` | 按条件分页查询日志 | 无 | `logic/log/fetch.go` |
| POST | `/rest/logs/total` | 按 level 分组统计数量 | 无 | `logic/log/total.go` |
| HEAD | `/` | 健康检查(独立入口注册) | 无 | `cmd/main/main.go` |
> 该模块没有 `proto` 定义,接口契约以逻辑层实际读取的 JSON 字段为准。
## 4. 数据模型
### 表 `log_data`(经 GORM 自动迁移创建)
| 字段 | 类型 | 键/约束 | 说明 |
| --- | --- | --- | --- |
| `id` | uint | PK | 自增主键 |
| `created_at` | TIMESTAMP | - | 创建时间 |
| `service` | varchar(255) | 默认 `def` | 服务名称 |
| `op_id` | uint | - | 操作人 ID |
| `op_name` | varchar(255) | - | 操作人姓名 |
| `ip` | varchar(255) | 默认 `0.0.0.0` | 操作 IP |
| `data_type` | varchar(255) | - | 数据类型 |
| `level` | uint | 默认 1 | 日志级别1 信息 / 2 警告 / 3 错误) |
| `content` | text | - | 日志内容 |
| `hmac` | varchar | - | 预留校验字段,写入逻辑未计算 |
| `encry` | - | `gorm:"-"` | 非持久化字段 |
## 5. 核心流程
```mermaid
flowchart TD
A["POST /rest/logs/create"] --> B["ClientIP 过滤"]
B --> C["BindJSON 到 []LogData"]
C --> D["DBService.Create 批量写入"]
E["POST /rest/logs/fetch"] --> F["BindJSON 到 map"]
F --> G["按 op_name/service/ip/level 过滤"]
G --> H["Count + Limit/Offset 分页"]
```
## 6. 审计发现
### 6.1 安全
| 级别 | 位置 | 问题 |
| --- | --- | --- |
| **高** | `internal/logic/log/create.go:21` | **IP 过滤逻辑与注释相反**。注释为"禁止公网网段提交",但代码在 IP 属于 `127.0.0.1`/`localhost`/`10.`/`172.`/`192.` **内网段时返回错误**,等于只拦内网、放行公网,与设计意图完全颠倒。 |
| **高** | `internal/routers/register.go:21-27` | 全部业务路由注册在**匿名组**`JwtAuth` 中间件被注释掉。`create`/`fetch`/`total` 可被任意匿名调用,日志可被伪造写入、全量读取。 |
| 中 | `internal/logic/log/fetch.go:36,39,42` | 查询关键词未做长度/字符限制即拼接进 `LIKE`;虽使用参数占位符未直接注入 SQL`%kw%` 全表扫描与超长入参缺少约束。 |
| 低 | `internal/logic/log/create.go:22,30,35,41` | 用标准库 `log.Printf` 打印请求 IP、解析错误与 DB 错误,可能将内部信息写入进程日志。 |
### 6.2 正确性与逻辑缺陷
| 级别 | 位置 | 问题 |
| --- | --- | --- |
| **高** | `internal/logic/log/fetch.go:42` | **取参键名不一致**:判断用 `request["op_ip"]`,取值用 `request["ip"].(string)`。实际传入 `op_ip` 时取到 `nil``nil.(string)` 触发 **panic**,服务 500。该过滤条件永远生效不了或直接崩溃。 |
| **高** | `internal/logic/log/fetch.go:33-53` | **分页参数未生效**`size`/`page` 声明后从未从 `request` 赋值,判断 `page<=0`/`size<=0` 恒成立,永远固定第 1 页、每页 50 条。调用方传 `page`/`size` 无效。 |
| **高** | `internal/logic/log/fetch.go:45` | `request["level"].(int)`JSON 数字反序列化为 `float64`,直接断言 `int`**panic**。只要带上 `level` 字段即崩溃。 |
| 中 | `internal/logic/log/fetch.go:29-31` | 返回体变量名为 `data []any`,未指定 `LogData` 类型GORM `Find``any` 填充,字段结构依赖 GORM 猜测,易与表结构脱节。 |
| 中 | `internal/logic/log/total.go:24` | `tx.Where(...)` 未回写 `tx`,在 GORM v2 下当前能生效但依赖内部行为,一旦链式调用顺序变化即静默丢失条件;同类写法在 `fetch.go` 同样存在。 |
| 中 | `internal/logic/log/total.go:28` | `Select("level","count(level)").Group("level").First(&result)``First` 会附加 `ORDER BY id LIMIT 1`,与 `GROUP BY` 组合语义不明确,多分组时只返回一条。 |
| 低 | `internal/models/log_data.go:19-20` | `Hmac` 字段存在于表中,但 `create.go` 未计算/校验任何 HMAC`Encry` 为非持久化字段。二者目前无实际用途。 |
### 6.3 未完成实现
- 未发现显式 TODO/占位。`Hmac` 完整性校验字段有模型无逻辑,属"设计预留未落地"。
### 6.4 健壮性与可维护性
| 级别 | 位置 | 问题 |
| --- | --- | --- |
| 中 | `internal/impl/impl.go:32` | `DBService = with.Databases(config.Spec.Databases, opts)`;而 `config.go:27``conf.NotNil` 只校验 `Service`/`Cache`**未校验 `Databases`**。配置漏配时 DB 可能为 nil首个查询即空指针崩溃。 |
| 低 | `internal/impl/impl.go:22,35` | `RedisService``EtcdService` 被初始化但全模块无任何使用点(死代码)。`Dependencies.Cache` 也被声明却未在 `applyDependencies` 中应用。 |
| 低 | `internal/routers/register.go:20` | 保留调试用 `fmt.Println(v1_key)`。 |
| 低 | 全模块 | **无任何单元测试**`*_test.go` 缺失)。`fetch.go` 的两处 panic 若有测试即可暴露。 |
| 低 | `internal/logic/log/fetch.go:19` | 请求体用 `map[string]any` 承载,缺失字段/类型错误只能运行时发现,无误用反馈。 |
## 7. 风险汇总
| 编号 | 级别 | 问题 | 影响面 |
| --- | --- | --- | --- |
| L1 | 高 | 路由无鉴权,日志可伪造/泄露 | 数据可信性、信息安全 |
| L2 | 高 | `fetch` 取参键名不一致 → panic | 接口可用性 |
| L3 | 高 | `level` 类型断言 → panic | 接口可用性 |
| L4 | 高 | 分页参数完全失效 | 功能不可用 + 全表返回风险 |
| L5 | 高 | `create` IP 过滤逻辑写反 | 防护失效 |
| L6 | 中 | `Databases` 未做非空校验 | 启动后运行期崩溃 |
| L7 | 中 | GORM 链式未回写、`First`+`Group` 语义问题 | 查询结果不确定 |
| L8 | 低 | 死代码、调试输出、无测试 | 可维护性 |
## 8. 修复建议(务实项)
1. **鉴权**`create`/`fetch`/`total` 至少接入入口层统一鉴权(聚合入口已有 JWT 能力,直接启用即可),不要留在匿名组。
2. **修正 IP 过滤**:明确"允许来源"与"禁止来源",按白名单/内网判定写正;`172.`/`192.` 需收敛为 `172.16-31.``192.168.`,并考虑网关代理下的 `X-Forwarded-For`
3. **`fetch` 修复**:统一键名为 `op_ip``level``mapstructure`/`json.Number` 或结构体接收,避免 `.(int)` 断言;把 `page`/`size` 真正从请求读出并设上限。
4. **`total` 修复**:确认是要"按 level 分组返回全部"还是"单一统计值",按结果语义改用 `Find`/`Scan`,并回写 `tx`
5. **配置校验**`conf.NotNil` 补上 `Databases`;确需可选时在初始化后显式判空并给出清晰错误。
6. **清理**:删除调试 `Println` 与该模块未使用的 Redis/Etcd 初始化(或按规划保留并注明)。
7. **补测试**:为 `create`/`fetch`/`total` 各写一条正例 + 一条边界用例,覆盖本次发现的三处 panic。
> 本报告只列出与现有实现直接相关的修复项,不引入新的分层或抽象封装。日志审计中"统一请求校验框架""引入 DTO 层"一类改造不在此列。