9

我有一个公用文件夹pub,其中包含子文件夹和文件。用户现在给了我一个相对文件路径,我执行一些映射,然后我读取文件fstream并将其返回给用户。

现在的问题是,如果用户给我一个路径,例如../fileXY.txt,或者考虑到路径遍历或其他类型的文件路径注入的其他一些花哨的东西。fstream只是会接受它并读取我的公共pub文件夹之外的潜在文件,或者更糟糕的是给他们我系统上所有文件的列表等......

在重新发明轮子之前,我搜索了文件系统库,我看到有这个std::filesystem::canonical函数,并且有很多关于正常形式的讨论。我在这里有一个普遍的问题,这个函数和变体std::filesystem::weakly_canonical可以用来防止这种类型的漏洞吗?所以基本上够了吗?

此外,我系统的文件系统库仍处于实验模式,并且std::filesystem::weakly_canonical丢失了。但我不能使用,canonical因为文件必须存在于canonical. 就我而言,我有某些映射,并且文件在这种意义上不存在。所以我需要模仿这个weakly_canonical功能,但是怎么做呢?

我在realpath 上看到了有关不存在路径的相关 stackoverflow 问题,建议他重复规范,只要路径存在,然后将不存在的部分添加到其中,但这又容易受到此类注入的攻击。那么我必须自己动手weakly_canonical还是可以通过组合一些功能以某种方式模仿它std::experimental::filesystem

4

2 回答 2

1

我可以通过检查结果是否以您的基本路径为前缀来了解如何使用weakly_canonical()来防止路径遍历 - 类似于此处描述的内容。例如

#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://stackoverflow.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;
}

由于我不是安全专家,我欢迎任何更正。

于 2020-09-11T00:11:47.827 回答
1

简短的回答没有。

长答案这是以posix realpath为模型的

我理解混乱的根源。从真实路径

realpath() 函数应从 file_name 指向的路径名派生一个绝对路径名,该路径名解析为相同的目录条目,其解析不涉及'.'、'..

cppref 路径中,您还可以看到双点已被删除。但是路径仍然指向同一个文件。只是删除了多余的元素。

如果您正在处理来自 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”

运行示例

于 2019-04-10T11:25:33.367 回答