21. java安全编码指南之:文件IO操作
简介
对于文件的IO操作应该是我们经常会使用到的,因为文件的复杂性,我们在使用File操作的时候也有很多需要注意的地方,下面我一起来看看吧。
创建文件的时候指定合适的权限
不管是在windows还是linux,文件都有权限控制的概念,我们可以设置文件的owner,还有文件的permission,如果文件权限没有控制好的话,恶意用户就有可能对我们的文件进行恶意操作。
所以我们在文件创建的时候就需要考虑到权限的问题。
很遗憾的是,java并不是以文件操作见长的,所以在JDK1.6之前,java的IO操作是非常弱的,基本的文件操作类,比如FileOutputStream和FileWriter并没有权限的选项。
Writer out = new FileWriter("file");
那么怎么处理呢?
在JDK1.6之前,我们需要借助于一些本地方法来实现权限的修改功能。
在JDK1.6之后,java引入了NIO,可以通过NIO的一些特性来控制文件的权限功能。
我们看一下Files工具类的createFile方法:
public static Path createFile(Path path, FileAttribute<?>... attrs)
throws IOException
{
newByteChannel(path, DEFAULT_CREATE_OPTIONS, attrs).close();
return path;
}
其中FileAttribute就是文件的属性,我们看一下怎么指定文件的权限:
public void createFileWithPermission() throws IOException {
Set<PosixFilePermission> perms =
PosixFilePermissions.fromString("rw-------");
FileAttribute<Set<PosixFilePermission>> attr =
PosixFilePermissions.asFileAttribute(perms);
Path file = new File("/tmp/www.flydean.com").toPath();
Files.createFile(file,attr);
}
注意检查文件操作的返回值
java中很多文件操作是有返回值的,比如file.delete(),我们需要根据返回值来判断文件操作是否完成,所以不要忽略了返回值。
删除使用过后的临时文件
如果我们使用到不需要永久存储的文件时,就可以很方便的使用File的createTempFile来创建临时文件。临时文件的名字是随机生成的,我们希望在临时文件使用完毕之后将其删除。
怎么删除呢?File提供了一个deleteOnExit方法,这个方法会在JVM退出的时候将文件删除。
注意,这里的JVM一定要是正常退出的,如果是非正常退出,文件不会被删除。
我们看下面的例子:
public void wrongDelete() throws IOException {
File f = File.createTempFile("tmpfile",".tmp");
FileOutputStream fop = null;
try {
fop = new FileOutputStream(f);
String str = "Data";
fop.write(str.getBytes());
fop.flush();
} finally {
// 因为Stream没有被关闭,所以文件在windows平台上面不会被删除
f.deleteOnExit(); // 在JVM退出的时候删除临时文件
if (fop != null) {
try {
fop.close();
} catch (IOException x) {
// Handle error
}
}
}
}
上面的例子中,我们创建了一个临时文件,并且在finally中调用了deleteOnExit方法,但是因为在调用该方法的时候,Stream并没有关闭,所以在windows平台上会出现文件没有被删除的情况。
怎么解决呢?
NIO提供了一个DELETE_ON_CLOSE选项,可以保证文件在关闭之后就被删除:
public void correctDelete() throws IOException {
Path tempFile = null;
tempFile = Files.createTempFile("tmpfile", ".tmp");
try (BufferedWriter writer =
Files.newBufferedWriter(tempFile, Charset.forName("UTF8"),
StandardOpenOption.DELETE_ON_CLOSE)) {
// Write to file
}
}
上面的例子中,我们在writer的创建过程中加入了StandardOpenOption.DELETE_ON_CLOSE,那么文件将会在writer关闭之后被删除。
释放不再被使用的资源
如果资源不再被使用了,我们需要记得关闭他们,否则就会造成资源的泄露。
但是很多时候我们可能会忘记关闭,那么该怎么办呢?JDK7中引入了try-with-resources机制,只要把实现了Closeable接口的资源放在try语句中就会自动被关闭,很方便。
注意Buffer的安全性
NIO中提供了很多非常有用的Buffer类,比如IntBuffer, CharBuffer 和 ByteBuffer等,这些Buffer实际上是对底层的数组的封装,虽然创建了新的Buffer对象,但是这个Buffer是和底层的数组相关联的,所以不要轻易的将Buffer暴露出去,否则可能会修改底层的数组。
public CharBuffer getBuffer(){
char[] dataArray = new char[10];
return CharBuffer.wrap(dataArray);
}