在Java(或Scala)中过滤向上路径遍历[已关闭]

2022-09-03 00:18:14

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

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

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

限制性太强的方法(即可能导致漏报)可能适合我的特定用例,如果这简化了事情,而且,我目前的需求是文件系统路径而不是Web路径(尽管,理论上,等效的Web模块可能在这里工作)。


答案 1

您可以使用 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;
}

答案 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");
}

当包含对父目录的引用或为绝对路径时,可以引用 中未包含的文件。在这种情况下,您可以在允许对文件进行任何修改之前引发异常。pathAsStringrelativeroot


推荐