安全地创建相对java.io.File的方法

时间:2013-04-24 07:44:13

标签: java file security

如何以安全的方式创建相对于父文件夹的java.io.File实例,即防止恶意攻击者突破父文件夹。

示例:

  String path = request.getParameter( "path" );
  File file = new File( folder, path );

这是不安全的,因为攻击者可能会将../../../etc/passwd作为path发送给我。

我如何“清理”这样的路径?

4 个答案:

答案 0 :(得分:4)

在阅读其他答案后,我想出了这个解决方案:

public static boolean isParent( File parent, File file ) {

    File f;
    try {
        parent = parent.getCanonicalFile();

        f = file.getCanonicalFile();
    } catch( IOException e ) {
        return false;
    }

    while( f != null ) {
        // equals() only works for paths that are normalized, hence the need for
        // getCanonicalFile() above. "a" isn't equal to "./a", for example.
        if( parent.equals( f ) ) {
            return true;
        }

        f = f.getParentFile();
    }

    return false;
}

所以用法是:

File file = new File( folder, path );
if( ! isParent( folder, file ) ) {
    ... deny access ...
}

上面的代码可能不是很快,但所有其他解决方案都存在安全问题:

  • 我可以删除/../|^../|/..$,但这不适用于Windows。对于Windows,模式会变得更复杂,并且不要忘记Java接受/作为Windows上的文件分隔符(是的,C:/Windows/有效并且与C:\Windows\相同)

    此外,路径a/../b/c是有效的,因为它不会破坏边界,因此仅删除相对移动是不够的。

  • 我可以使用getCanonicalPath()创建两个字符串,并确保parent路径是file的前缀。但是,我必须确保父路径之后的字符是文件分隔符(请参阅上面为什么File.SEPARATOR不够)。

答案 1 :(得分:2)

一种解决方案是禁止以/..开头的任何内容。但我认为“安全/正确”的方式是在启用(JVM)安全管理器的情况下运行您的应用程序,并禁止在预定义配置之外访问文件。但启用安全管理器需要一些工作来确定运行巡视应用程序所需的所有权限。

显然,OS文件权限也很重要。

修改

根据要求添加了一个示例。示例来自Tomcat中部署的当前项目:

grant codeBase "file:${catalina.base}/webapps/mywebapp/-" {
    permission java.io.FilePermission "path/to/folder", "read"; // Anything else will be disallowed
    // Other required permissions
}

答案 2 :(得分:1)

    String path = "../../../etc/passwd";
    String pureFilename = (new File(path)).getName();
    File file = new File(folder, pureFilename);

答案 3 :(得分:-1)

boolean isChild(File parent, File child) throws IOException {
    String parentCanonicalPath = parent.getCanonicalPath();
    String childCanonicalPath = child.getCanonicalPath();
    return childCanonicalPath.startsWith(parentCanonicalPath);
}