山东大学软件学院项目实训-创新实训-SDUMeeting(七)
web安全(二)
山大会议的客户端加入了对用户自定义头像的支持,需要用户自行上传头像图片,这个过程存在文件注入漏洞,这篇文章记录解决文件注入漏洞的过程。
package com.meeting.file.util;
import com.meeting.common.exception.BaseException;
import com.meeting.common.exception.FileFormatException;
import com.meeting.common.util.UUIDUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
@Component
public class PictureUtil {
@Value("${file.path}")
public String path;
@Value("${picture.reg}")
private String reg;
/**
* 文件后缀名,.jpg
*/
@Autowired
private UUIDUtil uuidUtil;
/**
* 文件地址 /file/pic/{图片类型}/{uid}.jpg
* 处理图片
* @param type 类型
* @param file 图片
* @param fileType 图片格式
*/
public void handlePicture(String type, MultipartFile file, long uid, String fileType)
throws IOException{
String originalName = file.getOriginalFilename();
String fileType_in = file.getContentType();
1.文件名检查
要求文件名不能为空,文件头格式为"image/jpeg",文件后缀为"jpeg"和"png",限制了上传的文件只能为图片格式
if (originalName == null) {
throw new BaseException("文件名不能为空");
}
if (!("image/jpeg").equals(fileType_in)
&& !("image/png").equals(fileType_in)) {
throw new FileFormatException("不支持的文件格式,文件头表示的格式不支持");
}
if (!"jpeg".equals(fileType) && !"png".equals(fileType)) {
throw new FileFormatException("不支持的文件格式,fileType参数");
}
if (!originalName.matches(reg)) {
throw new FileFormatException("不支持的文件格式,img参数");
}
2.文件重命名
将文件名重命名为用户的uid
防范00截断
00截断原理,为什么能做到00截断? 答:0x00是字符串的结束标识符,攻击者通过手动添加字符串标识符的方式,将00后面内容截断。 但是00后面的东西又能帮助我们绕过文件检测。
// 新的文件名
String fileName = "" + uid + "." + fileType;
// 文件目的地址
File dest = new File(path + '/' + type + '/' + fileName);
if(!dest.getParentFile().exists()){
dest.getParentFile().mkdir();
}
file.transferTo(dest);
}
public byte[] openPicture(String filename, String type)
throws FileNotFoundException {
File source = new File(path + '/' + type + '/' + filename);
if (source.exists() && source.isFile()) {
InputStream inputStream = null;
ByteArrayOutputStream outputStream = null;
try {
inputStream = new FileInputStream(source);
outputStream = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int read = -1;
while ((read = inputStream.read(buff)) != -1) {
outputStream.write(buff, 0, read);
}
if (outputStream.size() == 0) {
throw new FileNotFoundException("空文件");
}
return outputStream.toByteArray();
} catch (IOException exception) {
return null;
} finally {
handleClose(inputStream, outputStream);
}
} else {
throw new FileNotFoundException("文件不存在");
}
}
private void handleClose(InputStream inputStream, OutputStream outputStream) {
try {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
} catch (IOException exception) {
exception.printStackTrace();
}
}
}
3.限制上传的文件大小,限制文件最大为5Mb
package com.meeting.file;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.util.unit.DataSize;
import javax.servlet.MultipartConfigElement;
@SpringBootApplication
public class FileApplication {
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
// 单个文件最大
factory.setMaxFileSize(DataSize.parse("5MB"));
// 文件上传总大小
factory.setMaxRequestSize(DataSize.parse("5MB"));
return factory.createMultipartConfig();
}
public static void main(String[] args) {
SpringApplication.run(FileApplication.class, args);
}
}