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

Java 检查用户请求的文件是否位于基本目录下,java,Java,我在web服务器上有一个满是文件的目录层次结构,我希望用户能够通过请求访问这些文件中的任何一个。(这比这要复杂一些,这就是为什么我需要访问Java web应用程序中的文件。) 实现这一点的自然方式是允许用户指定文件,例如使用斜杠,例如/my web endpoint?param=folder1/folder2/file.txt。然后我会在Java中找到类似于newfile(baseDirectory,param)的文件。到目前为止,一切顺利 但是如果用户请求,例如,param=../someth

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

实现这一点的自然方式是允许用户指定文件,例如使用斜杠,例如
/my web endpoint?param=folder1/folder2/file.txt
。然后我会在Java中找到类似于
newfile(baseDirectory,param)
的文件。到目前为止,一切顺利

但是如果用户请求,例如,
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 ...
    
    这是否足够?会出什么问题吗

  • 正确的方法是什么?

    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
    

    您可以使用一种方法来检查候选路径是否以强制父路径开始。Java7NIO似乎对此有很好的支持

    以下是实现此想法的方法:

    /**
     * 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。

    您能有效地使用权限吗?我认为文件系统权限不够。它们将阻止对系统文件的访问,但仍有web服务器用户可以访问的文件,这些文件不应公开给web。