java代码审计--文件控制

基础知识

java文件操作相关类

1.JDK原始的java.io.FileInputStream类
2.JDK原始的java.io.RandomAccessFile类
3.Apache Commons IO提供的org.apache.commons.io.FileUtils类
4.JDK1.7新增的基于NIO非阻塞异步读取文件的java.nio.channels.AsynchronousFileChannel类。
5.JDK1.7新增的基于NIO读取文件的java.nio.file.Files类。
常用方法
如:
Files.readAllBytes、Files.readAllLines
FileInputStream
FileOutputStream
File
FileUtils
IOUtils
BufferedReader
ServletFileUpload
MultipartFile
CommonsMultipartFile
PrintWriter
ZipInputStream
ZipEntry.getSize

典型运算思路
直接上传思路 multipartFile.transferTo(file); 
DiskFileItemFactory
@MultipartConfig
MultipartFile
File
upload
InputStream
write

File操作相关函数
file.exists()判断存在
file.delete()删除文件
file.creatNewFile()
file.writer()
file.read()
file.readline()#读取行的思路
file.close()

一.文件上传

前言:现在这个漏洞的其实越来越少了,现在很多主流框架对上传的文件不会放在web可访问的页面下。

1.servlet框架文件上传方法
servlet3之前
使用commons-fileupload、commons-io这两个jar包来处理文件上传

servlet3之后
使用request.getParts()获取上传文件在这里插入图片描述
2.最简单的任意文件上传代码写法

private boolean uploadWithAnnotation(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    Part part = request.getPart("fileName");
    if(part == null) {
      return false;
    }
    String filename = UPLOAD_PATH + File.separator + part.getSubmittedFileName();
    part.write(filename);
    part.delete();
    return true;
}

3.修复方法

①判断context-type内容方法
https://mp.weixin.qq.com/s/LzVseWzLP3CjQsmYzvELZA
②文件上传改名
String fileExt = filename.substring(filename.lastIndexOf(".") + 1); //获取后缀名
String savename = UPLOAD_PATH + File.separator + DigestUtils.md5Hex(System.currentTimeMillis() + filename) + "." + fileExt;
③设置白名单后缀
if(!ESAPI.validator().isValidFileName("upload", filename, ALLOW_EXT, false)) {
    response.getWriter().println("后缀名不合法");
}
④文件大小限制
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
servletFileUpload.setSizeMax(1024 * 400);

二.文件操作(增删读下)

1.文件写入
思路:利用fileWriter类的思路去写

// 创建文件
file.createNewFile();
// creates a FileWriter Object
FileWriter fileWriter = new FileWriter(filename);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.write(fileContent);
bufferedWriter.close();

防御方法:
对写入的内容做一个过滤就好了

private boolean checkFilecontent(String fileContent) {
    List<String> blackContents = Arrays.asList("<%@page", "<%!");
    for(String blackContent : blackContents) {
      if(fileContent.contains(blackContent)) {
        return false;
      }
    }
    return true;
}

2.文件删除
利用file.delete函数判断就好了

String filename = request.getParameter("filename");
File file = new File(filename);
if(file != null && file.exists() && file.delete()) {
  	response.getWriter().println("delete success");
} else {
  	response.getWriter().println("delete failed");
}

3.文件读取
两种思路
1.基于InputStream+outputStream的思路
思路:即先把内容写入进去,然后在把内容用outputStream读取出来

//InputStream
File file = new File(filename);
InputStream inputStream = new FileInputStream(file);
while(-1 != (len = inputStream.read())) {
  	outputStream.write(len);
}

2.基于File.reader的思路

//FileReader
String fileContent = "";
FileReader fileReader = new FileReader(filename);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line = "";
while (null != (line = bufferedReader.readLine())) {
		fileContent += (line + "\n");
}

4.文件下载
原理:当浏览器不能识别的格式时即进行下载到本地端
实现方法:
1.)设置Content-Type段
确定客户端或者服务器以那种端进行获取
application/octet-stream:文件拓展名.*(二进制流)
application/x-msdownload:文件拓展名.dll(告诉浏览器这是一个要保存到本地的下载文件)

2.Content-header设置浏览器是否以附件形式获取数据
HttpServletResponse.setHeader方法设置Content-Disposition的值为"attachment:filename=文件名",浏览器通过附件的形式来获取到用户上传的文件

3.InputStream
将文件内容输入到java内存中
outputStream
利用输出流将java内存中的数据输出到文件中去

InputStream inputStream = new FileInputStream(file);
ServletOutputStream outputStream = response.getOutputStream();

类似于文件下载的思路
常用变量

fileName
filePath
getFile
getWriter
//stream
String filename = request.getParameter("filename");
File file = new File(filename);

response.reset();//把response的内容给重置为默认的
response.addHeader("Content-Disposition", "attachment;filename=" + new String(filename.getBytes("utf-8")));
response.addHeader("Content-Length", "" + file.length());
response.setContentType("application/octet-stream; charset=utf-8");

InputStream inputStream = new FileInputStream(file);
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
int len;
while(-1 != (len = inputStream.read())) {
  outputStream.write(len);
}

inputStream.close();
outputStream.close();

后面三个的防御思路相同
防御方法:对文件内容做一个检测

private boolean checkFilename(String filename) {
    filename = filename.replace("\\", "/"); //消除平台差异
  	//将文件操作限定在指定目录中
  	File file = new File(filename);
    if(file == null || !file.exists() || file.getCanonicalFile().getParent().equals(new File(DOWNLOAD_PATH).getCanonicalPath())) { //检测上级目录是否为指定目录
      return false;
    }
  	//检测文件名中是否有敏感字符
    List<String> blackList = Arrays.asList("./", "../");
    for(String badChar : blackList) {
      if(filename.contains(badChar)) {
        return false;
      }
    }
  	//对文件后缀名设置白名单
    List<String> whiteExt = Arrays.asList("png", "jpg", "gif", "jpeg", "doc");
    String fileExt = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
    if(!whiteExt.contains(fileExt)) {
      return false;
    }
    return true;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

goddemon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值