必须在解析前硬性设限:调用 r.parsemultipartform(32

限制文件大小和类型,避免内存爆满或恶意上传
Go 默认读取 multipart/form-data 时会把整个文件加载进内存(通过 request.ParseMultipartForm),大文件或恶意构造的超大表单直接导致 OOM。必须在解析前硬性设限:
- 调用
r.ParseMultipartForm(32 (如 32MB)——这个参数是内存缓冲上限,不是文件大小上限,仅控制 form 字段值存内存的部分 - 真正限制上传文件大小,得靠
r.MultipartReader()+ 手动流式读取,并累计字节数,超限时立即关闭连接、返回413 Payload Too Large - 文件类型不能只信
Header.Get("Content-Type"),它可被客户端随意伪造;必须读取文件前几个字节(magic bytes)校验,比如用github.com/h2non/filetype库 - 禁止接受
application/x-executable、text/html、application/javascript等高危 MIME 类型,即使扩展名合法
生成安全文件名,防止路径遍历和覆盖攻击
用户传来的 filename 字段可能含 ../、空字节、控制字符或重复后缀,直接拼接路径等于开放任意写权限。
- 彻底丢弃原始
filename,用服务端生成唯一 ID:uuid.New().String()或time.Now().UnixNano()+ 随机盐 - 若需保留原始扩展名,仅从文件头推断(非
filepath.Ext()),且白名单过滤:map[string]bool{".jpg": true, ".png": true} - 绝对不用
filepath.Join(uploadDir, userFilename);应先filepath.Clean()再检查是否仍在目标目录内:strings.HasPrefix(absPath, absUploadDir) - Linux 下注意
filename含\x00会导致os.OpenFile截断,引发逻辑绕过
保存到隔离目录,禁用执行与 Web 访问权限
上传目录若在 Web 根目录下,又没关掉脚本解析(如 Nginx 的 fastcgi_split_path_info 配置错误),.php/.py 文件可能被直接执行。
- 上传目录必须脱离 Web server 的 document root,例如放在
/var/uploads/而非/var/www/static/uploads/ - Linux 下设置目录权限为
0750,属主为运行 Go 进程的用户,Web server 用户(如 www-data)无读写权 - 若需 Web 访问,走 Go 的
http.ServeFile或反向代理(Nginx 静态转发),并显式禁止执行:location ~ \.(php|py|sh)$ { return 403; } - Windows 下注意
CON、AUX等设备名不能作文件名,需提前过滤
校验文件完整性,防范内容篡改和二次渲染漏洞
图片等文件可能嵌入恶意 payload,某些浏览器/编辑器会二次解析(如 GIF 中藏 PHP 代码,被 ImageMagick 渲染执行)。基础防护要覆盖:
- 对图片做「重编码」:用
golang.org/x/image/...解码再编码一次,剥离注释、EXIF、隐藏帧 - PDF/Office 文档建议用外部工具(如
pdfinfo、libreoffice --headless)转成纯文本或图片再保存 - 计算并存储 SHA-256 哈希值,后续下载前比对,防止存储层被篡改
- 不要用
os.Chmod给上传文件加可执行位——绝大多数场景完全不需要
实际部署时,最易忽略的是 magic bytes 校验与目录权限的组合失效:比如校验了 PNG 头,但攻击者传一个截断的 PNG(后面拼了 PHP 代码),Web server 又恰好配置了 try_files $uri $uri/ /index.php?$args,就可能触发解析。这类链式漏洞得通盘看 infra,不能只盯 Go 代码。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











