可以使用 filesystem::canonical 来防止传递给 fstream 的文件路径的文件路径注入吗
Posted
技术标签:
【中文标题】可以使用 filesystem::canonical 来防止传递给 fstream 的文件路径的文件路径注入吗【英文标题】:Can filesystem::canonical be used to prevent filepath injection for filepaths passed to fstream 【发布时间】:2019-04-10 10:40:12 【问题描述】:我有一个公用文件夹pub
,其中包含子文件夹和文件。用户现在给了我一个相对文件路径,我执行一些映射,然后我用fstream
读取文件并将其返回给用户。
现在的问题是,如果用户给我一条路径,例如../fileXY.txt
或其他一些考虑路径遍历或其他类型的文件路径注入的花哨的东西。 fstream
只是会接受它并读取我的公共 pub
文件夹之外的潜在文件,或者更糟糕的是给他们我系统上所有文件的列表等...。
在重新发明***之前,我在文件系统库中进行了搜索 我已经看到有这个std::filesystem::canonical 函数并且有很多关于正常形式的讨论。我这里有个一般性的问题,这个函数和变种std::filesystem::weakly_canonical可以用来防止这类漏洞吗?那么基本上就够了吗?
此外,我系统的文件系统库仍处于实验模式,并且缺少std::filesystem::weakly_canonical
。但我不能使用canonical
,因为文件必须存在于canonical
中。就我而言,我有某些映射,并且文件在这种意义上不存在。所以我需要模仿weakly_canonical
函数,但是怎么做呢?
我在realpath for nonexisting paths 上看到了一个相关的 *** 问题,建议他重复规范,只要路径存在,然后将不存在的部分添加到其中,但这又容易受到此类注入的攻击。那么我是否必须推出自己的 weakly_canonical
或者我可以通过组合一些 std::experimental::filesystem
函数以某种方式模仿它?
【问题讨论】:
即使用户给了你这样的路径,你应该没有问题,因为安全性应该是每个用户并由操作系统检查。如果用户故意为您提供这样的路径并且对该文件具有写入权限,那么他们无论如何都可以在没有您的应用的情况下执行此操作。 @MichaelChourdakis 我不确定我是否理解正确。澄清一下,用户给了我一个相对路径,这是我的系统(服务器应用程序)的相对路径,他不是在他的系统上而是在服务器系统上读取文件。并且不应该允许他读取公共文件夹之外的文件。 对于服务器应用程序,无论如何都不应允许用户引用本地文件。为什么要从用户那里得到这样的输入? 您应该使用数据库来实现它。该数据库将存储用户上传的主键 ID,以及用户指定的文件名,该文件名仅用于显示目的。当您将列表呈现给用户下载时,您将呈现他指定的文件名,但使用 ID 从数据库中读取数据,而不是使用文件名。 @MichaelChourdakis 但这实际上是这里的问题。所以如果std::filesystem::canonical
可以防止这些安全问题。
【参考方案1】:
简答否。
长答案这是仿照posix realpath
我了解混乱的根源。来自真实路径
realpath() 函数应从 file_name 指向的路径名派生一个绝对路径名,该路径名解析为相同的目录条目,其解析不涉及 '.'、'..
从cppref path你也可以看到双点被去掉了。但是路径仍然指向同一个文件。只是去掉了多余的元素。
如果您正在处理来自 db/webapp/whatever 的值,无论您的程序在哪里拥有与提供路径的用户不同的权限,您需要首先通过转义双点来清理文件名。点很好。
也许您可以使用正则表达式来转义带有反斜杠的双点,从而使它们无效。
#include <iostream>
#include <filesystem>
#include <string>
#include <regex>
int main()
std::string bad = "../bad/../other";
std::filesystem::path p(bad);
std::cout << std::filesystem::weakly_canonical(p) << std::endl;
std::regex r(R"(\.\.)");
p = std::regex_replace(bad, r, "\\.\\.");
std::cout << std::filesystem::weakly_canonical(p) << std::endl;
输出
“/tmp/其他”
"/tmp/1554895428.8689194/\.\./bad/\.\./other"
Run sample
【讨论】:
感谢您的回答。通过删除这些冗余元素。生成的路径是否保证它对这些攻击无害(不仅仅是点点攻击,我不是文件路径注入方面的专家,但我认为还有更多类似斜杠和其他的)。所以例如如果我有相对路径,应用真实路径或规范,生成的路径是否保证没有导向器遍历?保证文件位于结果路径中用斜杠分隔的所有子目录中也是如此。 如果您先应用规范,则双点将转换为目录名称,并将点一起删除。这取决于您正在执行的操作以及您允许的路径名,例如需要引号的空格。我很确定你可以找到一些与谷歌相关的东西 @czorp 查看execve 系列函数,它们不使用外壳,因此转义无关紧要,就像您直接调用 main 一样 谢谢,我去看看:D @czorp 很好,如果此条目回答了您的问题并且您对获得的信息感到满意,也许您可能想选择一个答案【参考方案2】:我可以看到您如何使用weakly_canonical()
来防止路径遍历 - 类似于here 的描述 - 通过检查结果是否以您的基本路径为前缀。例如
#include <iostream>
#include <filesystem>
#include <optional>
// Returns the canonical form of basepath/relpath if the canonical form
// is under basepath, otherwise returns std::nullopt.
// Note that one would probably require that basepath is sanitized,
// safe for use in this context and absolute.
// Thanks to https://portswigger.net/web-security/file-path-traversal
// for the basic idea.
std::optional<std::filesystem::path> abspath_no_traversal(
const std::filesystem::path & basepath,
const std::filesystem::path & relpath)
const auto abspath = std::filesystem::weakly_canonical(basepath / relpath);
// thanks to https://***.com/questions/1878001/how-do-i-check-if-a-c-stdstring-starts-with-a-certain-string-and-convert-a
const auto index = abspath.string().rfind(basepath.string(), 0);
if (index != 0)
return std::nullopt;
return abspath;
由于我不是安全专家,因此欢迎任何更正。
【讨论】:
以上是关于可以使用 filesystem::canonical 来防止传递给 fstream 的文件路径的文件路径注入吗的主要内容,如果未能解决你的问题,请参考以下文章
使用位置变量时是不是可以解决 SC2001(“看看是不是可以使用 $variable//search/replace”)?
是否可以使用 StreamingHttpResponse 生成 PDF,因为可以使用 CSV 来生成大型数据集?
如果可以使用 synchronized(this),为啥还要使用 ReentrantLock?