可以使用 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 来生成大型数据集?

excel里可以使用sql语句吗

如果可以使用 synchronized(this),为啥还要使用 ReentrantLock?

YII框架中可以使用foreach遍历对象以及可以使用数组形式直接访问对象的原因

有啥方法可以使用在一个函数中定义的 const 变量可以被 C++ 中同一程序中的其他函数使用