安全实践:校验、SQL 注入与上传安全

Web 安全没有银弹,但大部分高危问题都集中在几个固定入口:输入没校验、SQL 拼接、文件上传不设防。GoFrame 提供统一的数据校验组件(gvalid),ORM 自带防注入的参数化执行,文件上传则需要开发者按官方 API 配合安全策略实现。

入参校验:用 v 标签声明规则

GoFrame 的校验组件与规范路由天然集成:请求结构体进入 handler 前自动校验。规则写在 v 标签里,常用规则包括 requiredlengthmin-lengthmax-lengthbetweenmin/maxinemaildate-formatregexsame 等,多规则用 | 连接:

type CreateUserReq struct {
    g.Meta  `path:"/user" method:"post" summary:"create user"`
    Name    string `v:"required|length:2,30"                 dc:"用户名"`
    Age     int    `v:"required|between:18,60"               dc:"年龄"`
    Email   string `v:"required|email"                       dc:"邮箱"`
    Role    string `v:"required|in:admin,user,guest"         dc:"角色"`
    Phone   string `v:"phone"                                dc:"手机号"`
    IdCard  string `v:"id-number"                            dc:"身份证号"`
}
  • 校验失败时规范路由自动返回错误,业务代码里几乎不用手写“if name == ''”这类防御。
  • 后端校验永远不能省:前端校验只是体验,后端 v 标签才是安全边界。
  • 同一套规则还会被自动转换成 OpenAPI 文档约束(见接口文档章节),一处声明多处受益。

SQL 注入防范

官方 ORM 文档明确说明:ORM 的所有输入参数在底层以预处理模式执行,防范常见 SQL 注入风险。我们只需要遵守两条铁律:

// 正确:条件参数化,值经预处理提交
list, err := dao.User.Ctx(ctx).
    Where("name = ? AND age > ?", name, age).
    All()

// 危险:把用户输入拼进 SQL 字符串
// sql := "SELECT * FROM user WHERE name='" + name + "'"
// list, err := g.DB().GetAll(ctx, sql)   // 禁止这样写
  • 使用链式 Where/WhereLike/WhereIn 等条件方法并传入参数值,不要拼接 SQL 文本。
  • 需要内嵌原生 SQL 片段(如 now()、字段自增)时用 gdb.Raw 类型显式声明,它不会被当作文本参数处理,语义可见、风险可控。
  • 排序字段、表名这类“无法参数化”的部分做白名单校验后再拼入。
  • 程序内一切 SQL 都走 ORM/DAO,不留 Raw 后门给日常业务。

文件上传安全

官方请求输入章节提供 GetUploadFile 等 API:上传文件先落内存对象,再显式保存。安全要点是“不信任文件名与内容”:

// 上传接口
func Upload(r *ghttp.Request) {
    f := r.GetUploadFile("file")
    if f == nil {
        r.Response.WriteStatus(400, "missing file")
        return
    }
    // 1. 校验大小与类型(示例:限制 2MB,白名单扩展名)
    const maxSize = 2 << 20
    if f.Size > maxSize {
        r.Response.WriteStatus(400, "file too large")
        return
    }
    ext := strings.ToLower(filepath.Ext(f.Name))
    if ext != ".jpg" && ext != ".png" && ext != ".pdf" {
        r.Response.WriteStatus(400, "unsupported file type")
        return
    }
    // 2. Save 时随机重命名,避免使用客户端文件名,防路径穿越/覆盖
    savedName, err := f.Save("./upload", true)
    if err != nil {
        g.Log().Error(r.Context(), err)
        r.Response.WriteStatus(500, "save failed")
        return
    }
    r.Response.WriteJson(g.Map{"url": "/static/" + savedName})
}
  • 扩展名校验只做第一层:更严格可校验文件头(magic bytes);图片可用库重编码后存储。
  • 上传目录禁止执行脚本:不要把上传目录设为可执行的静态服务根目录,或直接放对象存储。
  • 下载时用白名单响应头(Content-Disposition)并限制路径,防止穿越读取其他文件。

其他补充

  • 框架提供 CSRF 防御设置与自定义状态码处理等安全能力(官方文档有对应章节);敏感接口叠加鉴权中间件(见鉴权章节)与频率限制。
  • 定期关注官方安全公告与升级提醒(官方文档与发布说明为准)。

注意点

  • 校验规则与业务语义一起演进:新接口必须带 v 规则,评审时把“无校验接口”当作缺陷。
  • 报错信息别泄露内部细节(如 SQL、堆栈),统一走错误码体系。
  • 安全配置(密钥、数据库口令)走环境变量/配置中心,不入库、不进日志。

小结

安全实践的骨架:所有输入过 v 校验 → 所有 SQL 走 ORM 参数化 → 上传文件白名单 + 随机命名 + 隔离目录。这三道防线挡住的是最常见的高危漏洞,剩下的按业务形态参考官方文档 goframe.org 持续加固。

笔记加载中…