写web经常会遇到路径拼接
最简单的实现就是
root:="/home/xxx/files"
target:="/a/b"
full:=root+target
直接拼接两个字符串,简洁明了 但是并不意味着安全:
1.路径穿越
“/home/xxx/files”+"../a/b",这样就访问到别的目录了
解决方案:拼接后clean,变成"/home/xxx/a/b",再和"/home/xxx/files"做前缀判断
比较直觉的写法是
strings.HasPrefix(full, root)
但这也是不对的,可以通过拼凑前缀来越过:
root := "/data/disk"
target := "../disk2/secret"
full := root + target // /data/disk2/secret
HasPrefix(full, root) == true
所以还要做相对化
rel, err := filepath.Rel(root, full)
if err != nil || strings.HasPrefix(rel, "..") {
return "", fmt.Errorf("storage/local: location %s illegal %v", loc.Path, err)
}
2.不合法路径
当target为 “”/".“时,会发生什么?
target=”" full="/data/disk" rel="." pass=true target="." full="/data/disk" rel="." pass=true
要对target做提前判断
if loc.Path == "" || loc.Path == "." {
return "", fmt.Errorf("storage/local: empty location path")
}
null 字节(\x00):部分系统会截断文件名;
换行/控制字符:污染日志、出问题的 shell 拼接。
也是同理要进行拦截
if strings.ContainsAny(loc.Path, "\x00") {
return "", fmt.Errorf("storage/local: illegal characters in path")
}
symlink 攻击
- 符号链接:当前是纯字符串校验,要知道它的边界
Abs/Clean/Rel 全是字符串操作,不解析 symlink。威胁场景:
- root 自身是符号链接,或 root 路径中间某一跳是 symlink → 字符串算出来的"在 root 内"可能和真实落盘位置不一致;
- 被写目录里有攻击者预先放的 symlink,
Rename/CreateTemp跟着走(这是经典的"symlink 攻击")。
对"应用层抗 bit rot"的 NAS,一般两个做法:
- 够用就停:接受字符串级防护,在文档里写明"假定 root 及其祖先目录可信、非 symlink"。简单、无 TOCTOU。
- 彻底:构造期用
filepath.EvalSymlinks(root)解析到真实路径再存。注意它有 TOCTOU(校验和使用之间链接可能被换),且开销较高,只在威胁模型需要时上。
我建议先走第一种 + 文档声明,等你确认部署形态(双盘位 NAS,root 是固定挂载点)再决定要不要 EvalSymlinks