检查用户请求的文件是否在基本目录下

时间:2018-07-13 14:21:23

标签: java

我在Web服务器上有一个充满文件的目录层次结构,我希望用户可以通过请求访问这些文件中的任何一个。 (比这复杂一点,这就是为什么我需要访问Java Web应用程序中的文件。)

实现此目的的自然方法是允许用户指定文件,例如正斜杠例如/my-web-endpoint?param=folder1/folder2/file.txt。然后,我会用new File(baseDirectory, param)之类的东西找到Java中的文件。到目前为止,一切正常。

但是,如果用户请求例如param=../something-private/file.txt还是什么?我该如何预防?

  1. 我可以禁止其中带有..的参数,但是这就足够了吗?我总是有点担心黑名单。如果我没有想到另一种语法怎么办? (例如,想到/开始一条路径。但是也许还有其他。)

  2. 我可以使用getCanonicalFile(),然后检查路径是否以基本目录开头,例如

    File baseDirectory = ....
    String param = ...
    File desiredFile = new File(baseDirectory, param);
    if ( ! desiredFile.getCanonincalPath().startsWith(
            baseDirectory.getCanonincalPath()+File.separator))
        throw ...
    

    够了吗?会出什么事吗?

正确的方法是什么?

2 个答案:

答案 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。