我想确保这足以防止目录遍历,任何建议或提示将不胜感激。目录"/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));
}
这是一个很好的第一步,但将输入列入黑名单是不够的。 它将阻止某些控制字符,但文档没有明确说明目录分隔符(例如 包括/
和(。文档指出:
从此方法返回的数组不保证包含 文件和目录中无效的完整字符集 名字。完整的无效字符集可能因文件系统而异。
接下来,您尝试确保在 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
应该组合路径并返回/somerootpath/wwwroot/foo
。GetParentFolder
会返回/somerootpath/wwwroot
这将是不匹配的,它会被拒绝。 但是,假设Path.Combine
连接并返回/somerootpath/wwwroot/Posts/../foo
。 在这种情况下GetParentFolder
将返回/somerootpath/wwwRoot/Posts
这是匹配项,然后继续进行。 似乎不太可能,但可能会有一些控制字符根据文档通过GetInvalidFileNameChars()
,说明哪些技巧Path.Combine
这些内容并不详尽。
你的方法可能会奏效。 但是,如果可能的话,我强烈建议您将预期的输入列入白名单,而不是尝试将所有可能的无效输入列入黑名单。 例如,如果您确定所有有效的文件名都将由字母、数字和下划线组成,请构建一个断言该正则表达式,并在继续之前进行检查。 测试^[A-Za-z0-0_]+$
会断言这一点,并且是 100% 防弹的。