我的代码是否阻止了目录遍历或者它是否过大?

Does my code prevent directory traversal or is it overkill?

我想确保这足以防止目录遍历,如有任何建议或提示,我们将不胜感激。目录“/wwwroot/Posts/”是唯一允许的目录。

    [HttpGet("/[controller]/[action]/{name}")]
    public IActionResult Post(string name)
    {
        if(string.IsNullOrEmpty(name))
        {
            return View("Post", new BlogPostViewModel(true)); //error page
        }

        char[] InvalidFilenameChars = Path.GetInvalidFileNameChars();

        if (name.IndexOfAny(InvalidFilenameChars) >= 0)
        {
            return View("Post", new BlogPostViewModel(true));
        }

        DirectoryInfo dir = new DirectoryInfo(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/Posts"));

        var userpath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/Posts", name));

        if (Path.GetDirectoryName(userpath) != dir.FullName)
        {
            return View("Post", new BlogPostViewModel(true));
        }

        var temp = Path.Combine(dir.FullName, name + ".html");
        if (!System.IO.File.Exists(temp))
        {
            return View("Post", new BlogPostViewModel(true));
        }
        BlogPostViewModel model = new BlogPostViewModel(Directory.GetCurrentDirectory(), name);
        return View("Post", model);
    }

可能,但我不认为它是防弹的。让我们分解一下:

首先,您将已知的无效字符列入黑名单:

    char[] InvalidFilenameChars = Path.GetInvalidFileNameChars();

    if (name.IndexOfAny(InvalidFilenameChars) >= 0)
    {
        return View("Post", new BlogPostViewModel(true));
    }

这是良好的开端,但将输入列入黑名单还远远不够。它会阻止某些控制字符,但文档没有明确说明包含目录分隔符(例如 /\)。 documentation 状态:

The array returned from this method is not guaranteed to contain the complete set of characters that are invalid in file and directory names. The full set of invalid characters can vary by file system.

接下来,您尝试确保在 path.combine 之后您拥有文件的预期父文件夹:

    DirectoryInfo dir = new DirectoryInfo(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/Posts"));

    var userpath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/Posts", name));

    if (Path.GetDirectoryName(userpath) != dir.FullName)
    {
        return View("Post", new BlogPostViewModel(true));
    }

理论上,如果攻击者传入 ../foo(如果 / 不在无效字符列表中,则可能会通过上面的黑名单尝试),然后 Path.Combine 应该结合路径和 return /somerootpath/wwwroot/fooGetParentFolder 会 return /somerootpath/wwwroot 这将是不匹配的,它会被拒绝。但是,假设 Path.Combine 连接 return 和 /somerootpath/wwwroot/Posts/../foo。在这种情况下,GetParentFolder 将 return /somerootpath/wwwRoot/Posts 匹配并继续进行。似乎不太可能,但根据文档,可能会有控制字符超过 GetInvalidFileNameChars(),说明根据这些行将 Path.Combine 变成某些东西的技巧并不详尽。

您的方法可能会奏效。但是,如果可能的话,我强烈建议您将预期输入列入白名单,而不是尝试将所有可能的无效输入列入黑名单。例如,如果您可以确定所有有效的文件名都将由字母、数字和下划线组成,请构建一个正则表达式来断言并在继续之前进行检查。对 ^[A-Za-z0-0_]+$ 的测试将断言并且是 100% 防弹的。