17

是否有任何标准库方法可以过滤掉包含特殊遍历序列的路径,例如../向上目录遍历的所有其他复杂形式,以保护文件路径 API 输入不向上遍历给定的“根”路径?

我有一个包含根文件夹值成员的类,以及一个接受递归删除路径的成员函数。我的目标是使这个 API 安全,过滤掉提供给它的任何输入路径——这将转换为根文件夹值以上的路径。目的是该类将被广泛用于删除根路径下的文件,但它永远不会触及根路径之上的任何内容。

这类似于更广泛的路径遍历攻击

过于严格的方法(即可能导致误报)可能适用于我的特定用例,如果这可以简化事情,而且我当前的需求是文件系统路径而不是 Web 路径(尽管,等效的 Web 模块清酒理论上可能在这里工作)。

4

2 回答 2

30

您可以使用Path.normalize()从路径中去除“..”元素(及其前面的元素)——例如,它将“a/b/../c”变成“a/c”。请注意,它不会在路径的开头删除“..” ,因为它也没有前面的目录组件可以删除。因此,如果您要预先设置另一条路径,请先执行此操作,然后对结果进行规范化。

您还可以使用Path.startsWith(Path)来检查一个路径是否是另一个路径的后代。并且Path.isAbsolute()毫不奇怪地告诉您路径是绝对的还是相对的。

以下是我如何处理进入 API 的不受信任的路径:

/**
 * Resolves an untrusted user-specified path against the API's base directory.
 * Paths that try to escape the base directory are rejected.
 *
 * @param baseDirPath  the absolute path of the base directory that all
                     user-specified paths should be within
 * @param userPath  the untrusted path provided by the API user, expected to be
                  relative to {@code baseDirPath}
 */
public Path resolvePath(final Path baseDirPath, final Path userPath) {
  if (!baseDirPath.isAbsolute()) {
    throw new IllegalArgumentException("Base path must be absolute");
  }

  if (userPath.isAbsolute()) {
    throw new IllegalArgumentException("User path must be relative");
  }

  // Join the two paths together, then normalize so that any ".." elements
  // in the userPath can remove parts of baseDirPath.
  // (e.g. "/foo/bar/baz" + "../attack" -> "/foo/bar/attack")
  final Path resolvedPath = baseDirPath.resolve(userPath).normalize();

  // Make sure the resulting path is still within the required directory.
  // (In the example above, "/foo/bar/attack" is not.)
  if (!resolvedPath.startsWith(baseDirPath)) {
    throw new IllegalArgumentException("User path escapes the base path");
  }

  return resolvedPath;
}
于 2015-10-12T15:05:45.083 回答
2

您不需要使用第三方库来执行此操作。Java 提供的文件 API 使您能够验证文件是否是另一个文件的后代。

Path.resolve(String)将解析父目录引用、绝对路径和相对路径。如果将绝对路径作为参数传递给 resolve 方法,则它返回绝对路径。它不保证返回的值是调用该方法的路径的后代。

您可以使用Path.startsWith(Path)方法检查一个路径是否是另一个路径的后代。

Path root = java.nio.file.Files.createTempDirectory(null);
Path relative = root.resolve(pathAsString).normalize();
if (!relative.startsWith(root)) {
    throw new IllegalArgumentException("Path contains invalid characters");
}

pathAsString包含对父目录的引用或者是绝对路径时,relative可以引用不包含在root. 在这种情况下,您可以在允许对文件进行任何修改之前抛出异常。

于 2015-10-12T14:49:20.767 回答