go1.24: 新的标准库 os.Root

go1.24: 新的标准库 os.Root. Backlink | |Photo by Kaleidico on Unsplash Golang 1.24 已经进入冻结期了,目前很多特性我们在 Go 1.24 Release Notes 能够看到,接下来一段时间我会学习Golang1.24将会添加的新功能以及。

分享

man drawing on dry-erase board

Backlink | |Photo by Kaleidico on Unsplash

Golang 1.24 已经进入冻结期了,目前很多特性我们在Go 1.24 Release Notes 能够看到,接下来一段时间我会学习Golang1.24将会添加的新功能以及修改点。如果您也想学习Go最新的进展,请关注我,
这篇文章,我们将学习一下os.Root这个新增的Standard library。

Proposal

目录遍历漏洞是一类常见的漏洞,攻击者会诱骗程序打开其不希望的文件。这些攻击通常采用提供相对路径名的形式,例如"../../../etc/passwd" ,这会导致在预期位置之外进行访问。 CVE-2024-3400是最近的一个真实的目录遍历示例,导致远程代码执行漏洞被主动利用。

在其他语言以及操作系统中,已经都有类似的实现, 比如:

  • Python 的 chroot:通过 os.chroot() 将根目录限制为特定目录。
  • Linux 的文件系统命名空间:通过 mount 和 chroot 限制进程的视图。
    我们可以写一个demo 测试一下。
    首先构造一个 “机密”文件
echo 'password123' >> /tmp/password

然后写一个Golang函数, 原义是打开当前目录的文件

func main() {  
    fileName := os.Args[1]  
    //readFile  
    localFilePath := "."  
    filePath := fmt.Sprintf("%s/%s", localFilePath, fileName)  
    content, err := os.ReadFile(filePath)  
    if err != nil {  
       fmt.Printf("Error reading file %s: %s\n", fileName, err)  
       return  
    }  
    fmt.Printf("File %s opened successfully. file content %s\n", fileName, content)  
}

但是因为传入了不可靠的参数,代码能够访问到 权限范围之外的地方。


➜  os_root git:(main) ✗ pwd
/Users/hxzhouh/workspace/github/me/blog-example/go/go1.24/os_root
➜  os_root git:(main) ✗ ./main ../../../../../../../../../tmp/password
./../../../../../../../../../tmp/password
File ../../../../../../../../../tmp/password opened successfully. file content password123

在 Go1.24 中,新增了 os.Root 的类型,其提供了在特定目录中执行文件系统操作的能力。整个体系是围绕着这个新类型进行的。 对应的核心函数是 os.OpenRoot,函数打开一个目录并返回一个 os.Root。
os.Root 上的方法仅允许在目录内操作,不允许指向目录外部位置的路径,包括遵循目录外符号链接的路径。(防御住了前面提案背景提到的攻击范围)
我们用Go1.24 的方式修改代码

func main() {  
    fileName := os.Args[1]  
    root, err := os.OpenRoot(".")  
    if err != nil {  
       panic(err)  
    }  
    file, err := root.Open(fileName)  
    if err != nil {  
       fmt.Println(fmt.Sprintf("Error opening file %s: %s\n", fileName, err.Error()))  
       return  
    }  
    content := make([]byte, 1024)  
    c, err := file.Read(content)  
    if err != nil {  
       panic(err)  
    }  
    content = content[:c]  
    fmt.Printf("File %s opened successfully. file content %s\n", fileName, content)  
}

再次运行

➜  os_root git:(main) ✗ gotip version 
go version devel go1.24-fafd447 Wed Dec 11 15:57:34 2024 -0800 darwin/arm64
➜  os_root git:(main) ✗ gotip build -o go1.24  main.go                      
➜  os_root git:(main) ✗ ./go1.24 ../../../../../../../../../tmp/password 
Error opening file ../../../../../../../../../tmp/password: openat ../../../../../../../../../tmp/password: path escapes from parent

如果文件夹脱离了parent 层级,就会报错。
更多的APi接口请参考官方文档 。1.24正式发布后,估计很多第三方库都会第一时间适配把,毕竟其他语言基本上都有这种功能了。

阅读更多

那些意外变成特性的 Bug:当代码漏洞改写计算史

那些意外变成特性的 Bug:当代码漏洞改写计算史

那些意外变成特性的 Bug:当代码漏洞改写计算史. 比起草台班子将错就错的神话,面对异常数据时的观测定力才是真的 社交网络上常年流传着一类爽文:某家公司的程序员把代码写崩了,结果不仅没被开除,这个 Bug 反而成了拳头产品,妥妥的爽文主角剧本。 最常被搬出来的有三个:Audio Hijack 写错试用期拯救公。

By huizhou92