通过超链接下载文件时,如果浏览器可以识别该文件格式(txt、jpg等),浏览器就会直接打开。只有浏览器不能识别该文件格式的时候,才会实现下载。否则前端页面通过超链接方式发起文件下载请求,把要下载的文件名传递给后台,在对应的后台接口实现文件的下载操作(设置响应类型及响应头,输出流写入文件内容)。
以下是通过Servlet程序实现下载的过程:
1.通过File对象进行文件的封装,然后通过输入流InputStream将文件中的数据读取到java程序中;
2.通过setContentTpye方法设置Content-Type字段的值,设置为application/octet-stream或application/x-msdownload,决定客户端服务器以哪种方式来接受返回的信息;
application/octet-stream:文件拓展名.*(二进制流)
application/x-msdownload:文件拓展名.dll(告诉浏览器这是一个要保存到本地的下载文件)
3.通过HttpServletResponse.setHeader方法设置Content-Disposition的值为”attachment:filename=文件名”,浏览器通过附件的形式来获取到用户上传的文件;
4.读取下载文件,通过OutputStream方法使用输出流将刚刚封装在输入流的文件内容向客户端写入。
双层定位文件下载
常见业务关键字
-
download
-
fileName
-
filePath
-
write
-
getFile
-
getPath
-
getWriter
-
…
相关操作类
-
InputStream
-
File
-
OutputStreaam
-
BufferedInputStream
-
FileInputStream
除了上述方法以外,还可以直接定位源代码中的工具类,为了降低耦合,文件操作相关的代码都会通过封装成一个工具类,不通的需求会通过重载或者重写进行实现。
例如以下关键字:
-
FileUtil
-
IOUtil
审计要点
通过相关方法定位到关键字后,一般检查点如下:
-
路径相关参数是否用户可控
-
是否限定了可下载的文件目录范围
-
是否配置了全局过滤器或者额外进行了路径处理(例如过滤“…/”和“/”等)(如果使用了过滤器,需要检查数据获取方式、规则、顺序等)
相关案例
相关代码如下:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DownloadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//定义文件下载路径
String path =getServletContext().getRealPath("/")+"image/";
//获取要下载的文件名称
String filename = request.getParameter("filename");
File file = new File(path+filename);
if(file.exists()) {
//实现文件的下载
//设置响应类型application/octet-stream
response.setContentType("application/x-msdownload");
//设置头信息(以附件形式打开)
response.setHeader("Content-Disposition", "attachment;filename=""+filename+""");
//读取要下载的文件
InputStream inputStream = new FileInputStream(file);
ServletOutputStream outputStream = response.getOutputStream();
byte b[] =new byte[1024];
int n;
while((n = inputStream.read(b))!=-1) {
outputStream.write(b,0,n);
}
//关闭流,释放资源
outputStream.close();
inputStream.close();
}else {
request.setAttribute("errorResult", "文件不存在下载失败!");
//重定向
RequestDispatcher dispatcher = request.getRequestDispatcher("jsp/Filedownload.jsp");
dispatcher.forward(request, response);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
具体修复
-
对用户输入的数据进行过滤,过滤掉”./”、”…/”、”%”、”/”
-
对文件类型进行白名单控制,可以限定允许下载的文件后缀
-
文件路径保存至数据库,让用户提交文件名对应ID进行文件下载(注意SQL注入问题)
//获取要下载的文件名称
String filename =SqlQuery.PathURL(request.getParameter("id"));//通过id从数据库获取文件的值
-
公开文件可使用超链接方式放置在web应用程序下载目录中通过链接直接下载即可
-
使用file.getCanonicalPath()判断文件的真实路径,仅允许下载规定路径下的文件.对于getCanonicalPath()函数,“.”就表示当前的文件夹,而”…“则表示当前文件夹的上一级文件夹,函数处理返回的就是将符号完全解析的完整路径。
微信号 : yunzhongkexin
来云众,玩不同
长按识别二维码关注

原文始发于微信公众号(云众可信):原创干货 | Java代码审计之任意文件下载
本文转为转载文章,本文观点不代表威客安全立场。