亲宝软件园·资讯

展开

SpringBoot上传文件类型检测

牟云飞 人气:2

文件上传大部分通过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中输出了,根据自己的页面编写显示吧。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

加载全部内容

相关教程
猜你喜欢
用户评论