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. 核心流程
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. 修复建议(务实项)
- 鉴权:
create/fetch/total 至少接入入口层统一鉴权(聚合入口已有 JWT 能力,直接启用即可),不要留在匿名组。
- 修正 IP 过滤:明确"允许来源"与"禁止来源",按白名单/内网判定写正;
172./192. 需收敛为 172.16-31.、192.168.,并考虑网关代理下的 X-Forwarded-For。
fetch 修复:统一键名为 op_ip;level 用 mapstructure/json.Number 或结构体接收,避免 .(int) 断言;把 page/size 真正从请求读出并设上限。
total 修复:确认是要"按 level 分组返回全部"还是"单一统计值",按结果语义改用 Find/Scan,并回写 tx。
- 配置校验:
conf.NotNil 补上 Databases;确需可选时在初始化后显式判空并给出清晰错误。
- 清理:删除调试
Println 与该模块未使用的 Redis/Etcd 初始化(或按规划保留并注明)。
- 补测试:为
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/*.proto 与 pb/*.go,因此少数需要新增接口字段才能完整实现的项目(已在处理结果中标注)做了安全降级。