從Java的角度修復文件下載漏洞


 

 

  從Java的角度談下文件下載漏洞的產生,然后到他的修復方案。這里我的修復方案是白名單,而沒有采用黑名單的方式。

  首先先看一段存在文件下載漏洞的代碼code:

    HTML視圖頁面  download.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h1>使用a標簽直接指向服務器上的資源</h1>
    <a href="/WEB14/download/a.flv">a.flv</a><br>
    <a href="/WEB14/download/a.jpg">a.jpg</a><br>
    <a href="/WEB14/download/a.mp3">a.mp3</a><br>
    <a href="/WEB14/download/a.mp4">a.mp4</a><br>
    <a href="/WEB14/download/a.txt">a.txt</a><br>
    <a href="/WEB14/download/a.zip">a.zip</a><br>
    <h1>使用服務器端編碼的方式實現文件下載</h1>
    <a href="/WEB14/downloadServlet2?filename=a.flv">a.flv</a><br>
    <a href="/WEB14/downloadServlet2?filename=a.jpg">a.jpg</a><br>
    <a href="/WEB14/downloadServlet2?filename=a.mp3">a.mp3</a><br>
    <a href="/WEB14/downloadServlet2?filename=a.mp4">a.mp4</a><br>
    <a href="/WEB14/downloadServlet2?filename=a.txt">a.txt</a><br>
    <a href="/WEB14/downloadServlet2?filename=a.zip">a.zip</a><br>
    <a href="/WEB14/downloadServlet2?filename=美女.jpg">美女.jpg</a><br>
</body>
</html>

 

  服務器端驗證文件下載代碼: 

    downloadServlet2.java代碼如下:

        

package cn.downloadServlet;

import java.io.*;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Base64;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.ws.RespectBinding;

import com.sun.org.apache.xerces.internal.util.SynchronizedSymbolTable;
import com.sun.xml.internal.bind.v2.runtime.output.StAXExStreamWriterOutput;

import sun.misc.BASE64Encoder;

public class DownloadServlet2 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 請求filename參數
        String filename = request.getParameter("filename");
        // System.out.println(filename);
        // 解決獲得中文參數的亂碼
        filename = new String(filename.getBytes("ISO8859-1"), "UTF-8");
        // 獲得請求頭中的User-Agent
        String agent = request.getHeader("User-Agent");
        // 根據不同瀏覽器進行不同的編碼
        String filenameEncoder = "";
        if (agent.contains("MSIE")) {
            // IE瀏覽器
            filenameEncoder = URLEncoder.encode(filename, "utf-8");
            filenameEncoder = filenameEncoder.replace("+", " ");
        } else if (agent.contains("Firefox")) {
            // 火狐瀏覽器
            BASE64Encoder base64Encoder = new BASE64Encoder();
            filenameEncoder = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
        } else {
            // 其它瀏覽器
            filenameEncoder = URLEncoder.encode(filename, "utf-8");
        }
        String requestURI = request.getRequestURI();
        System.out.println(requestURI);
        // 自動判斷文件的數據類型
        response.setContentType(this.getServletContext().getMimeType(filename));
        // 告訴客戶端該文件不是直接解析 而是以附件形式打開(下載)
        response.setHeader("Content-Disposition", "attachment;filename=" + filenameEncoder);
        //獲取請求的文件路徑名稱
        String realPath = this.getServletContext().getRealPath("/download/" + filename);
            ServletOutputStream outputStream = response.getOutputStream();
            FileInputStream fileInputStream = new FileInputStream(realPath);
            int length = 0;
            byte[] buff = new byte[1024];
            while ((length = fileInputStream.read(buff)) != -1) {
                outputStream.write(buff, 0, length);
            }
        } 

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

  這段代碼是是存在漏洞的,這里獲取filename參數的時候未進行驗證,導致可以任意文件下載。

    正常的圖片顯示:

    

 

進行繞過下載顯示文件內容:

    

未深入利用,這里的修復方案有兩種1.黑名單驗證修復 2.白名單驗證修復

    這里我選擇2的修復方案。

    談修復方案之前先講解下何為黑名單,何為黑名單!

      黑名單就是決定阻止的內容,比如我想要過濾一些特殊的字符,如果我的列表中存在這些特殊字符就攔截他們,這種屬於黑名單

      白名單就是決定放行的內容,比如我允許你輸入a-z,如果用戶輸入的內容范圍不在a-z以內,我就認為你的行為是非法的,這種屬於白名單。

          各自的優缺點:白名單比黑名單更安全,白名單不利於用戶體驗,為了用戶體驗大多數企業還是選擇黑名單,黑名單容易被繞過!

          這里的修復思路:允許用戶下載的文件在我的某個列表中,如果用戶下載的內容不在我的文件列表中,那么我認為你的行為是非法的!

          具體修復代碼如下:

             首先如果需要下載的文件不存在那么將會302跳轉到ErrorInfo.html頁面,代碼如下:

             

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    未找到文件!請重試...
</body>
</html>

 

      java部分文件下載實現代碼:

         

package cn.downloadServlet;

import java.io.*;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Base64;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.ws.RespectBinding;

import com.sun.org.apache.xerces.internal.util.SynchronizedSymbolTable;
import com.sun.xml.internal.bind.v2.runtime.output.StAXExStreamWriterOutput;

import sun.misc.BASE64Encoder;

public class DownloadServlet2 extends HttpServlet {
    static String[] arr =null;
    //獲取文件夾下所有文件名稱
    public static void getFileName() {
        String path = "D:\\JavaWeb\\.metadata\\.plugins\\org.eclipse.wst.server.core\\tmp2\\wtpwebapps\\WEB14\\download";
        File f = new File(path);
        ArrayList<String> arrayList=new ArrayList<>();
        if (!f.exists()) {
            System.out.println(path + " not exists");
            return;
        }
        File fa[] = f.listFiles();
        for (int i = 0; i < fa.length; i++) {
            File fs = fa[i];
            if (fs.isDirectory()) {
                arrayList.add(fs.getName());
            } else {
                arrayList.add(fs.getName());
            }
        }
        arr= new String[arrayList.size()];
        for (int i = 0; i <arr.length; i++) {
            arr[i]=arrayList.get(i);
        }
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws 

ServletException, IOException {
        // 請求filename參數
        String filename = request.getParameter("filename");
        // System.out.println(filename);
        // 解決獲得中文參數的亂碼
        filename = new String(filename.getBytes("ISO8859-1"), "UTF-8");
        // 獲得請求頭中的User-Agent
        String agent = request.getHeader("User-Agent");
        // 根據不同瀏覽器進行不同的編碼
        String filenameEncoder = "";
        if (agent.contains("MSIE")) {
            // IE瀏覽器
            filenameEncoder = URLEncoder.encode(filename, "utf-8");
            filenameEncoder = filenameEncoder.replace("+", " ");
        } else if (agent.contains("Firefox")) {
            // 火狐瀏覽器
            BASE64Encoder base64Encoder = new BASE64Encoder();
            filenameEncoder = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes

("utf-8")) + "?=";
        } else {
            // 其它瀏覽器
            filenameEncoder = URLEncoder.encode(filename, "utf-8");
        }
        getFileName();
        String requestURI = request.getRequestURI();
        System.out.println(requestURI);
        // 自動判斷文件的數據類型
        response.setContentType(this.getServletContext().getMimeType(filename));
        // 告訴客戶端該文件不是直接解析 而是以附件形式打開(下載)
        response.setHeader("Content-Disposition", "attachment;filename=" + 

filenameEncoder);
        //獲取請求的文件路徑名稱
        String realPath = this.getServletContext().getRealPath("/download/" + filename);
        boolean key = false;
        //函數調用
        getFileName();
        String arrs=null;
        for (int i = 0; i < arr.length; i++) {
            arrs=arr[i];
            //文件判斷
            if (arrs.equals(filename)) {
                key = true;
            } 
        }
        

        if (key == true) {
            ServletOutputStream outputStream = response.getOutputStream();
            FileInputStream fileInputStream = new FileInputStream(realPath);
            int length = 0;
            byte[] buff = new byte[1024];
            while ((length = fileInputStream.read(buff)) != -1) {
                outputStream.write(buff, 0, length);
            }
        } else {
            response.sendRedirect("/WEB14/ErrorInfo.html");
        }
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws 

ServletException, IOException {
        doGet(request, response);
    }
}

    我們再次實驗下是否存在文件下載漏洞了。

      正常的圖片下載:

    

 

嘗試讀取其他文件內容:

    

 

說明我們利用這種方法是可以有效防止文件下載漏洞的!

    不忘初心,方得始終。

 

 

 

 

      

 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM