23 个版本
0.7.12 | 2024 年 8 月 1 日 |
---|---|
0.7.11 | 2024 年 6 月 5 日 |
0.7.10 | 2024 年 4 月 30 日 |
0.7.8 | 2024 年 3 月 4 日 |
0.3.0 | 2022 年 6 月 24 日 |
#28 在 文件系统 中
5,777 每月下载量
用于 41 个 Crates(14 个直接使用)
160KB
2.5K SLoC
fs-mistrust
检查文件权限是否私有。
此库提供了一组功能来检查文件和目录的权限,以确保它们是有效私有的——也就是说,它们只能被可信[^1]用户读取或写入。
这种检查可以保护您的用户数据免受误配置的影响,例如,他们不小心使主目录对所有人可写,或者他们在一个由其他用户拥有的目录中使用了符号链接。
此库中的检查试图保证,一旦一个路径被证明是私有的,任何非可信用户的行为都不能使该路径变得私有。可信用户仍然可以在检查后更改路径。因此,如果您担心可信用户更改文件系统可能引起的时间检查/使用问题,您可能需要使用其他机制。
有关限制,请参阅下面的 限制 部分。
[^1]: 我们在这里使用“可信”这个词是指计算机安全意义上的:如果一个用户有机会破坏我们的安全保证,那么这个用户就是“可信”的。例如,在 Unix 环境中,“root”就是“可信”的,无论您是否真正信任他们。
检查权限有什么困难吗?
假设我们想知道一个给定的路径是否可以被不可信用户读取或修改。这比听起来要复杂
- 即使文件本身的权限是正确的,我们还需要检查包含该文件的目录的权限,因为它们可能允许不可信用户替换文件或更改其权限。
- 同样,我们需要检查该目录的父目录的权限,因为它们可能允许不可信用户替换目录或更改其权限。(以此类推!)
- 定义“可信用户”可能会很棘手。在Unix系统中,我们通常认为每个用户都信任自己,而root(UID 0)是可信的。但是很难说哪些组是可信的:即使一个给定的组今天只包含可信用户,也没有操作系统级别的保证,未来不会将不可信用户添加到该组中。
- 符号链接增加了另一层混淆。如果你正在检查的路径中存在任何符号链接,那么你需要检查包含符号链接的目录的权限,然后是目标路径的权限,以及其所有祖先的权限。
- 许多程序首先对正在检查的路径进行规范化,删除所有
..
和符号链接。这足以判断最终文件是否可以被不可信用户修改,但不足以判断路径是否可以被不可信用户修改。如果路径中存在可修改的符号链接,或者在路径解析的任何阶段,可以修改该符号链接的人可以更改该路径指向的文件。 - 即使你已经检查了一个目录只能由可信用户写入,这并不意味着该目录中的对象只能由可信用户写入。这些对象可能是指向文件系统上其他(可写入性更高)位置的符号链接;或者它们可能是通过存储在文件系统其他地方的硬链接可访问的。
不同的程序以不同的方式尝试解决这个问题,通常理由很少。这个crate试图为文件隐私检查和执行提供一个合理的实现,并在其源代码中提供清晰的解释,说明为什么它以这种方式运行。
我们实际上做了什么
为了确保文件解析过程中的每一步都被检查,我们在自己的系统中模拟这个过程。我们检查提供的路径中的每个组件,以查看它是否可以被不可信用户修改。如果我们遇到一个或多个符号链接,那么我们将解析由这些符号链接添加的路径的每个组件,直到我们最终到达目标。
实际上,我们正在模拟realpath
(或者如果你更喜欢,fs::canonicalize
),并检查我们在这样做过程中接触到的文件系统每个部分的权限,以查看谁有权更改我们的目标文件或导致我们到达该文件的过程。
对于组,我们使用以下启发式方法:如果存在与当前用户同名的组,并且当前用户属于该组,我们假设该组是可信的。否则,我们将所有组视为不可信。
示例
简单情况
确保目录只能由我们读取或写入(简单情况)
use fs_mistrust::Mistrust;
match Mistrust::new().check_directory("/home/itchy/.local/hat-swap") {
Ok(()) => println!("directory is good"),
Err(e) => println!("problem with our hat-swap directory: {}", e),
}
如上所述,但创建目录及其父目录(如果它们不存在的话)。
use fs_mistrust::Mistrust;
match Mistrust::new().make_directory("/home/itchy/.local/hat-swap") {
Ok(()) => println!("directory exists (or was created without trouble"),
Err(e) => println!("problem with our hat-swap directory: {}", e),
}
配置Mistrust
您可以调整Mistrust
对象来更改它允许的内容
# fn main() -> Result<(), fs_mistrust::Error> {
use fs_mistrust::Mistrust;
let my_mistrust = Mistrust::builder()
// Assume that our home directory and its parents are all well-configured.
.ignore_prefix("/home/doze/")
// Assume that a given group will only contain trusted users (this feature is only
// available on Unix-like platforms).
// .trust_group(413)
.build()?;
# Ok(())
# }
有关更多选项,请参阅Mistrust
使用Verifier
进行更细致的检查
对于对特定检查的更细致控制,您可以使用Verifier
API。与通常需要为多个请求配置的Mistrust
不同,Verifier
中的更改通常只适用于一次请求。
# fn main() -> Result<(), fs_mistrust::Error> {
use fs_mistrust::Mistrust;
let mistrust = Mistrust::new();
// Require that an object is a regular file; allow it to be world-
// readable.
mistrust
.verifier()
.permit_readable()
.require_file()
.check("/home/trace/.path_cfg")?;
// Make sure that a directory _and all of its contents_ are private.
// Create the directory if it does not exist.
// Return an error object containing _all_ of the problems discovered.
mistrust
.verifier()
.require_directory()
.check_content()
.all_errors()
.make_directory("/home/trace/private_keys/");
# Ok(())
# }
请参阅Verifier
获取更多选项。
使用CheckedDir
确保安全性。
您可以使用CheckedDir
API 确保不仅目录是私有的,而且对其内容的所有访问都继续验证和强制执行它们的权限。
# fn main() -> Result<(), fs_mistrust::Error> {
use fs_mistrust::{Mistrust, CheckedDir};
use std::fs::{File, OpenOptions};
let dir = Mistrust::new()
.verifier()
.secure_dir("/Users/clover/riddles")?;
// You can use the CheckedDir object to access files and directories.
// All of these must be relative paths within the path you used to
// build the CheckedDir.
dir.make_directory("timelines")?;
let file = dir.open("timelines/vault-destroyed.md",
OpenOptions::new().write(true).create(true))?;
// (... use file...)
# Ok(())
# }
限制
如上所述,这个crate只检查路径是否可以被非信任用户更改。路径检查后,信任用户仍然可以更改其权限。(例如,用户可以使他们的家目录对所有用户可写。)此crate不会尝试防御这种类型的检查时间/使用时间问题。
我们目前假设一个相当普通的Unix环境:我们会容忍其他系统,但实际上我们不会查看这些系统的任何细节
- Windows安全性(ACLs、安全描述符等)
- SELinux能力
- POSIX(和其他)ACLs。
当我们在检查目标目录内项目的权限时(使用Verifier::check_content
或CheckedDir
),我们会使用一个不太准确的启发式方法:我们继续禁止未受信任的可写目录和文件,但仍然允许可读的,即使我们坚持目标目录本身必须不可读。在可读对象具有硬链接的情况下,这过于宽容:如果文件在其他地方有硬链接,则未受信任的用户可以读取它。在没有硬链接的可写对象的情况下,这也过于严格:如果未受信任的用户没有访问这些对象的路径,他们实际上不能写入它们。
在Windows上,我们接受所有文件权限和所有者。
我们不检查挂载点和文件系统的设备的隐私。(例如,我们不区分本地管理员和远程文件系统的管理员。我们也不区分本地文件系统和不安全的网络文件系统。)
此代码尚未在setuid环境中经过审计,几乎肯定存在安全漏洞。
这是一款相当新的软件,尚未经过审计。
上述所有问题都被认为是“如果可行,则应该修复”。
致谢
这里执行的检查列表受到了OpenSSH的safe_path、GnuPG的check_permissions和Tor的check_private_dir列表的启发。所有错误都是我自己的。
许可:MIT OR Apache-2.0
依赖关系
~0.9–12MB
~82K SLoC