Files
full/docs/logs.md
2026-09-22 21:15:34 +08:00

11 KiB
Raw Blame History

logs业务操作日志代码审计报告

内容
审计对象 module/base/logs
服务域 基础与平台服务
审计日期 2026-09-22
代码规模 Go 文件 12 个(全部手写,无 proto/生成代码)
入口 cmd/mainGin 单进程)、聚合入口 pkgs/allpkgs/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. 核心流程

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 时取到 nilnil.(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,直接断言 intpanic。只要带上 level 字段即崩溃。
internal/logic/log/fetch.go:29-31 返回体变量名为 data []any,未指定 LogData 类型GORM Findany 填充,字段结构依赖 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 未计算/校验任何 HMACEncry 为非持久化字段。二者目前无实际用途。

6.3 未完成实现

  • 未发现显式 TODO/占位。Hmac 完整性校验字段有模型无逻辑,属"设计预留未落地"。

6.4 健壮性与可维护性

级别 位置 问题
internal/impl/impl.go:32 DBService = with.Databases(config.Spec.Databases, opts);而 config.go:27conf.NotNil 只校验 Service/Cache未校验 Databases。配置漏配时 DB 可能为 nil首个查询即空指针崩溃。
internal/impl/impl.go:22,35 RedisServiceEtcdService 被初始化但全模块无任何使用点(死代码)。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_iplevelmapstructure/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 层"一类改造不在此列。

9. 整改记录2026-09-22

本节记录按本报告结论执行的代码整改。整改遵循最小修正原则未引入新框架、抽象层、DTO/VO、事件总线未拆分服务边界未修改任何 proto/*.proto 与生成的 pb/*.go;新增/修改注释均为中文;口令类摘要统一使用 bcrypt验证码等短时效一次性凭证仍按原有 Redis 明文比对链路存储)。校验方式:GOWORK=off go build ./... + GOWORK=off go vet ./... + gofmt -l(仓库根 workspace 模式存在 genproto 拆包的 ambiguous import属本机既有问题

编号 级别 问题 处理结果
L1 路由无鉴权,日志可伪造/泄露 已修复业务接口移出匿名组仅保留探测接口公开yaml 匿名口径与代码一致
L2 fetch 取参键名不一致导致 panic 已修复:修正键名,并统一改为安全类型断言
L3 level 类型断言导致 panic 已修复:改安全断言并给默认值
L4 分页参数完全失效 已修复:修正归一化与 Limit/Offset,并加 page_size 上限
L5 create 的 IP 过滤逻辑写反 已修复:修正判断方向,过滤真正生效

未纳入本轮范围

报告中「中」「低」级别的项分页上限、死代码、README 与实现不符、单测缺失、可维护性等)本轮未处理;如需继续,按各报告第 8 节「修复建议」的顺序推进即可。

本轮整改未修改任何 proto/*.protopb/*.go,因此少数需要新增接口字段才能完整实现的项目(已在处理结果中标注)做了安全降级。