直接用 http.fileserver 会出安全问题,必须先用 filepath.clean 规范路径并校验是否在白名单根目录内;os.rename 跨设备失败需检测 sys.stat_t.dev 并手动 fallback 复制;上传大文件需合理设置 parsemultipartform。

直接用 http.FileServer 会出安全问题,别跳过路径校验
很多人一上来就写 http.Handle("/", http.FileServer(http.Dir("/data"))),以为能立刻跑起来——确实能列目录、点文件就能下载,但攻击者只要在 URL 里填 /../../etc/passwd 就可能读到系统文件。
根本原因:`http.FileServer` 不做路径合法性检查,它只管把请求路径拼到 `http.Dir` 后去 `os.Open`。必须前置拦截并规范化:
- 用
filepath.Clean(r.URL.Path)去掉所有..和冗余斜杠 - 再用
strings.HasPrefix(cleaned, allowedRoot)确保清理后路径仍落在白名单根目录内(比如/var/www/files) - 拒绝以
/开头但不匹配白名单的路径,也拒绝空路径或纯.、..
os.Rename 在跨设备时必然失败,得手动 fallback
Web 界面里点“重命名”或“移动”,后端调 os.Rename 很自然——但它底层调的是 Linux 的 rename(2) 系统调用,只支持同文件系统操作。一旦源路径和目标路径挂载点不同(比如 /tmp 和 /home),就会返回 invalid cross-device link 错误。
检测和处理方式要写死:
- 用
os.Stat分别获取源和目标路径的sys.Stat_t.Dev字段,相等才可直接os.Rename - 不等时,改用
os.Open+io.Copy+os.Remove组合;注意先写入临时文件,成功后再原子替换 - 别漏掉权限继承:复制后需调
os.Chmod(dst, srcInfo.Mode())和os.Chtimes(dst, srcInfo.ModTime(), srcInfo.ModTime())
上传大文件时 r.ParseMultipartForm(32 不是文件大小限制
这个参数叫 maxMemory,它只控制 multipart 表单字段(比如文本字段、小文件)在内存里缓存的上限。设成 32MB,不代表用户不能上传 500MB 文件——超出部分会自动落盘到临时目录,而 Go 不会自动清理 这些临时文件。
后果很直接:磁盘悄悄被占满,服务逐渐变慢甚至崩溃。
- 必须在 handler 结尾显式调用
r.MultipartForm.RemoveAll()或os.RemoveAll(form.TempDir) - 单文件大小限制得靠后续步骤:拿到
form.File后,用io.LimitReader(file.Open(), maxSize)包裹读取流 - 别信 “设了 maxMemory 就安全了” —— 它对恶意构造的超大文件毫无约束力
目录列表不能依赖 http.Dir 默认行为,HTML 要自己生成
http.FileServer(http.Dir(...)) 返回的目录页是 Go 内置的纯文本列表,没样式、没按钮、没法加删除/下载操作。真要做 Web 文件管理,必须手动读取并渲染 HTML。
推荐用 filepath.WalkDir(Go 1.16+)而非 os.ReadDir 递归:
-
filepath.WalkDir自动处理权限错误、符号链接循环、中断控制(返回非 nil error 可停止) - 遍历时用
d.IsDir()判断类型,用d.Info().ModTime()和d.Info().Size()提取元数据 - 生成 HTML 时,对每个文件路径用
filepath.Base(path)提取名称,避免暴露完整路径
真正麻烦的不是怎么列,而是怎么让每个条目带操作链接又不引入 XSS 或路径穿越——所有用户输入(包括文件名)必须 HTML 转义,所有 URL 参数必须经 filepath.Clean + 白名单校验再使用。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











