我在Web服务器上有一个充满文件的目录层次结构,我希望用户可以通过请求访问这些文件中的任何一个。 (比这复杂一点,这就是为什么我需要访问Java Web应用程序中的文件。)
实现此目的的自然方法是允许用户指定文件,例如正斜杠例如/my-web-endpoint?param=folder1/folder2/file.txt
。然后,我会用new File(baseDirectory, param)
之类的东西找到Java中的文件。到目前为止,一切正常。
但是,如果用户请求例如param=../something-private/file.txt
还是什么?我该如何预防?
我可以禁止其中带有..
的参数,但是这就足够了吗?我总是有点担心黑名单。如果我没有想到另一种语法怎么办? (例如,想到/
开始一条路径。但是也许还有其他。)
我可以使用getCanonicalFile()
,然后检查路径是否以基本目录开头,例如
File baseDirectory = ....
String param = ...
File desiredFile = new File(baseDirectory, param);
if ( ! desiredFile.getCanonincalPath().startsWith(
baseDirectory.getCanonincalPath()+File.separator))
throw ...
够了吗?会出什么事吗?
正确的方法是什么?
答案 0 :(得分:2)
startsWith(baseDirectory.getCanonincalPath()+"/")
应该没问题。
最好使用新的文件和路径类。
Path path = file.toPath();
path = path.toRealPath();
// Absolute, ".." removed, sym links resolved as no param NOFOLLOW_LINKS.
path = path.normalize(); // Removes ".."
path.startsWith(baseDir); // Base path
答案 1 :(得分:1)
您可以使用一种检查候选路径是否以必填父路径开头的方法。看来Java 7 nio对此提供了很好的支持。
以下是实现此想法的方法:
/**
* Check if a candidate path starts with a mandatory parent path
* @param mandatoryParentPath Mandatory parent path
* @param candidate Candidate path which should be under the mandatory parent path
* @return {@code true} if a candidate path starts with a mandatory parent path, else {@code false}
*/
/**
* Check if a candidate path starts with a mandatory parent path
* @param mandatoryParentPath Mandatory parent path
* @param candidate Candidate path which should be under the mandatory parent path
* @return {@code true} if a candidate path starts with a mandatory parent path, else {@code false}
*/
public static boolean absoluteStartsWith(Path mandatoryParentPath, Path candidate) throws IOException {
Objects.requireNonNull(mandatoryParentPath, "Mandatory parent path should not be null");
Objects.requireNonNull(candidate, "Candidate path should not be null");
Path path = candidate.toRealPath();
System.out.println(" " + candidate); // debugging code
return path.startsWith(mandatoryParentPath.toAbsolutePath());
}
这是验证上述方法的简单测试:
public static void main (String[] args) throws java.lang.Exception
{
Path root = Paths.get("d:/work");
System.out.println(absoluteStartsWith(root, root.resolve("/tmp")));
System.out.println(absoluteStartsWith(root, root.resolve("..")));
System.out.println(absoluteStartsWith(root, root.resolve("ems")));
}
这会在我的机器上打印出来:
d:\tmp
false
d:\work\..
false
d:\work\ems
true
注意:如果候选路径之一不存在,则上述absoluteStartsWith
方法将引发IOException。