Java 带有尾部“..”的文件 canonicalPath 会导致不一致的行为
Java file canonicalPath with tailing '.. ' leads to inconsistent behavior
我目前正在调查一些与 pathTraversal 相关的安全机制,并遇到了 java.io.File.getCanonicalPath() 的奇怪行为。我认为 CanonicalPath 将始终代表抽象底层文件的真正唯一路径。但是,如果文件名由两个点组成,后跟 space,则 CanonicalPath 似乎不再代表正确的路径。
示例如下:
File root = new File("c:/git/");
String relative = ".. /.. \";
File concatFile = new File (root.getCanonicalPath(), relative);
System.out.println("ConcatFileAbsolute: '" + concatFile.getAbsolutePath() + "'");
System.out.println("ConcatFileCanonical: '" + concatFile.getCanonicalPath() + "'");
File canonFile = new File(concatFile.getCanonicalPath());
System.out.println("\ncanonFileCanonical: '" + canonFile.getCanonicalPath() + "'");
System.out.println("canonFileAbsolute: '" + canonFile.getAbsolutePath() + "'");
System.out.println("canonFileName: '" + canonFile.getName() + "'\n");
for (File file : canonFile.listFiles()) {
System.out.println("canon: '" + file.getCanonicalPath() + "' - absolute: '" + file.getAbsolutePath()+ "'");
}
控制台输出:
ConcatFileAbsolute: 'C:\git\.. \.. '
ConcatFileCanonical: 'C:\git\.. \'
canonFileCanonical: 'C:\git\'
canonFileAbsolute: 'C:\git\.. '
canonFileName: '.. '
canon: 'C:\git\.. $Recycle.Bin' - absolute: 'C:\git\.. $Recycle.Bin'
canon: 'C:\git\.. \.m2' - absolute: 'C:\git\.. \.m2'
canon: 'C:\git\.. \boot' - absolute: 'C:\git\.. \boot'
...other content of C:/
正如你所看到的,虽然canonFile的canonicalPath明确指出它在C:\git\中的位置,但是.listFiles列出了C:中的所有文件。
这只有在我使用新的 File(String, String) 构造函数时才会发生。如果我将第三行更改为 File concatFile = new File (root.getCanonicalPath() + relative);那么输出就是预期的:
ConcatFileAbsolute: 'C:\git.. \.. '
ConcatFileCanonical: 'C:\git\'
- The following output then lists files under C:\git\
我不知道这种行为在类 unix 系统上是否相似。
有人可以澄清这种行为吗?这是故意的吗?
在此先感谢!
恭喜!您发现了 WinAPI.
的可能错误(或功能?)
更具体地说,您的问题可以简化为一行代码:
为什么 new File("c:\temp\.. ").getCanonicalPath()
returns c:\temp\
而不是 c:\
?
简短回答:因为在 ..
之后拖尾 space(是的,您已经在上面提到过)。
长答案:如果我们查看底层 class 的 Java 实现,我们会发现 Windows 是 WinNTFileSystem
class,其中 native 方法 canonicalize0("c:\temp\.. ")
returns 这个损坏的值。为什么?
为了测试它,我编写了简单的 VBS 代码来测试 Win API:
Dim folderName
folderName = "c:\temp\.. "
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
Dim fullpath
fullpath = fso.GetAbsolutePathName(folderName)
WScript.Echo "fullpath: " & fullpath
它给出完全相同的结果:
fullpath: C:\temp
我目前正在调查一些与 pathTraversal 相关的安全机制,并遇到了 java.io.File.getCanonicalPath() 的奇怪行为。我认为 CanonicalPath 将始终代表抽象底层文件的真正唯一路径。但是,如果文件名由两个点组成,后跟 space,则 CanonicalPath 似乎不再代表正确的路径。
示例如下:
File root = new File("c:/git/");
String relative = ".. /.. \";
File concatFile = new File (root.getCanonicalPath(), relative);
System.out.println("ConcatFileAbsolute: '" + concatFile.getAbsolutePath() + "'");
System.out.println("ConcatFileCanonical: '" + concatFile.getCanonicalPath() + "'");
File canonFile = new File(concatFile.getCanonicalPath());
System.out.println("\ncanonFileCanonical: '" + canonFile.getCanonicalPath() + "'");
System.out.println("canonFileAbsolute: '" + canonFile.getAbsolutePath() + "'");
System.out.println("canonFileName: '" + canonFile.getName() + "'\n");
for (File file : canonFile.listFiles()) {
System.out.println("canon: '" + file.getCanonicalPath() + "' - absolute: '" + file.getAbsolutePath()+ "'");
}
控制台输出:
ConcatFileAbsolute: 'C:\git\.. \.. '
ConcatFileCanonical: 'C:\git\.. \'
canonFileCanonical: 'C:\git\'
canonFileAbsolute: 'C:\git\.. '
canonFileName: '.. '
canon: 'C:\git\.. $Recycle.Bin' - absolute: 'C:\git\.. $Recycle.Bin'
canon: 'C:\git\.. \.m2' - absolute: 'C:\git\.. \.m2'
canon: 'C:\git\.. \boot' - absolute: 'C:\git\.. \boot'
...other content of C:/
正如你所看到的,虽然canonFile的canonicalPath明确指出它在C:\git\中的位置,但是.listFiles列出了C:中的所有文件。
这只有在我使用新的 File(String, String) 构造函数时才会发生。如果我将第三行更改为 File concatFile = new File (root.getCanonicalPath() + relative);那么输出就是预期的:
ConcatFileAbsolute: 'C:\git.. \.. '
ConcatFileCanonical: 'C:\git\'
- The following output then lists files under C:\git\
我不知道这种行为在类 unix 系统上是否相似。
有人可以澄清这种行为吗?这是故意的吗?
在此先感谢!
恭喜!您发现了 WinAPI.
的可能错误(或功能?)更具体地说,您的问题可以简化为一行代码:
为什么 new File("c:\temp\.. ").getCanonicalPath()
returns c:\temp\
而不是 c:\
?
简短回答:因为在 ..
之后拖尾 space(是的,您已经在上面提到过)。
长答案:如果我们查看底层 class 的 Java 实现,我们会发现 Windows 是 WinNTFileSystem
class,其中 native 方法 canonicalize0("c:\temp\.. ")
returns 这个损坏的值。为什么?
为了测试它,我编写了简单的 VBS 代码来测试 Win API:
Dim folderName
folderName = "c:\temp\.. "
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
Dim fullpath
fullpath = fso.GetAbsolutePathName(folderName)
WScript.Echo "fullpath: " & fullpath
它给出完全相同的结果:
fullpath: C:\temp