首頁 > 軟體

SpringBoot後端上傳檔案型別檢測方式

2022-03-24 13:01:45

檔案上傳大部分通過web前端判斷後尾名或者service後端判斷後尾名,這種操作具有一定的風險,比如:我可以將一個jsp頁面,修改後尾名改成jpg檔案進行上傳,由於圖片預覽功能,這個檔案會被執行,這時就可以傳送使用者資料到指定的服務下,竊取使用者資訊。

本文通過檔案流頭部判斷檔案型別

不同的檔案具有不同的頭部,比如:

不同的檔案具有不同的頭部資訊,以SpringBoot為例,通過攔截器攔截檔案流進行判斷:

1、新增組態檔checkFileHeader.properties

在src/main/resources中增加組態檔checkFileHeader.properties,檔案內容:

JPEG=FFD8FF
PNG=89504E47
GIF=47494638
TXT=75736167
PDF=255044462D312E
DOC=D0CF11E0
XML=3C3F786D6C
DOCX=504B0304
APK=504B030414000808
IPA=504B03040A000000

2、編寫讀取properties檔案類

讀取checkFileHeader.properties檔案內容,用於攔截器判斷

/**
 * 讀取檔案流頭資訊
 * @author hanjie
 *
 */
public class FileHeaderHelper {    
    private static FileHeaderHelper me ;
    private static List<String> headerList ; 
    private FileHeaderHelper(){}    
    public static FileHeaderHelper getInstance(){
        if(me == null){
            me = new FileHeaderHelper() ;
        }
        return me ;
    }
    
    public List<String> getHeaderList(){
        if(headerList == null){
            headerList = new ArrayList<String>() ;
            
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            String classpathResource = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "/fileheader.properties";
            Properties p = new Properties();
            try {
                Resource[] res = resolver.getResources(classpathResource) ;
                for (Resource re : res) {
                    p.load(re.getInputStream());
                    break ;
                }
                
            } catch (IOException e) {
                e.printStackTrace();
            }
            for (Map.Entry<Object, Object> item : p.entrySet()) {
                headerList.add(item.getValue().toString()) ;
            }
        }        
        return headerList ;
    }
}

3、編寫攔截器

攔截去中,獲取檔案流,讀取檔案流前8個位元組,根據需要可以讀取更多位元組判讀,8個位元組轉成16進位製為16個字串,我這裡最長的APK/IPA檔案也就16個位元組,所以讀取8個位元組,讀取位元組後判斷是否checkFileHeader.properties檔案中字串

/**
 * 檔案上傳攔截器
 * @author hanjie
 *
 */
public class FileHeaderCheckInterceptor implements HandlerInterceptor {
 
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        // 判斷是否為檔案上傳請求
        if (request != null && request instanceof MultipartHttpServletRequest) {
            MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
            Map<String, MultipartFile> files = multipartRequest.getFileMap();
            Iterator<String> iterator = files.keySet().iterator();
            while (iterator.hasNext()) {
                String formKey = (String) iterator.next();
                MultipartFile multipartFile = multipartRequest.getFile(formKey);
                //String filename = multipartFile.getOriginalFilename();
                byte[] file = multipartFile.getBytes() ;
                
                獲取位元組流前8位元組,差不多夠了,不行再加
                int HEADER_LENGTH = 8 ;
                
                if(file.length>HEADER_LENGTH){
                    //轉成16進位制
                    StringBuilder sb = new StringBuilder();
                    for(int i=0;i<HEADER_LENGTH;i++){
                        int v = file[i] & 0xFF;     
                        String hv = Integer.toHexString(v);     
                        if (hv.length() < 2) {     
                            sb.append(0);     
                        }     
                        sb.append(hv);
                    }                    
                    
                    boolean isFound = false ;
                    String fileHead = sb.toString().toUpperCase() ;
                    List<String> headerList = FileHeaderHelper.getInstance().getHeaderList() ;
                    for(String header : headerList){
                        if(fileHead.startsWith(header)){
                            isFound = true ;
                            break ;
                        }
                    }
                    if(!isFound){
//                        throw new BaseRunException("上傳檔案有異常,已被系統禁止!") ;
                        System.out.println("----------上傳檔案有異常,已被系統禁止!頭部資訊:"+fileHead);
                        response.setCharacterEncoding("UTF-8");
                        response.setContentType("application/json;charset=utf-8"); 
                        PrintWriter printWriter = response.getWriter();    
                        printWriter.write("上傳檔案有異常,已被系統禁止!");    
                        return false; 
                    }
                }
            }
        }
        return true;
    }
 
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub 
    }
 
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // TODO Auto-generated method stub 
    }    
}

4、設定攔截檔案

攔截器寫完了,設定下讓它生效,在Configuration中設定攔截器,攔截檔案流進行判斷

@Configuration
public class MyfWebAppConfiguration extends WebMvcConfigurerAdapter {
    
    //攔截器,攔截檔案流
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new FileHeaderCheckInterceptor()) 
                .addPathPatterns("/**"); 
    }
    
//    //註冊過濾
//    @Bean
//    public FilterRegistrationBean myFilterRegistration() {
//     
//       FilterRegistrationBean registration = new FilterRegistrationBean();
//       registration.setFilter(new LoginFilter());
//       registration.addUrlPatterns("/serviceInvoke");
//       //registration.addInitParameter("paramName", "paramValue");
//       registration.setName("loginFilter");
//       registration.setOrder(1);
//       return registration;
//     }
//    
//    
//    //註冊servlet
//    @Bean  
//    public ServletRegistrationBean myServletRegistration() {  
//        ServletRegistrationBean registration = new ServletRegistrationBean(new DownloadServlet());  
//        registration.addUrlMappings("/download");  
//        return registration;  
//    } 
}

頁面訊息提醒已經在printWriter中輸出了,根據自己的頁面編寫顯示吧。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com