接入层上传问题
zKing 2018-11-26 Web 安全
# 上传问题
- 上传文件
- 再次访问上传的文件
- 上传的文件被当成程序解析
# 防御
# 限制上传后缀
但由于后台语言不同,可能会有漏洞去伪造后缀名来欺骗程序
# 文件类型检查
可以根据浏览器传过来的文件类型进行检查,但是入侵者可能用其他方式来访问页面进行伪造,也不太可靠
# 文件内容检查
根据文件内容的前几个字节进行检查,但是也能够被欺骗
# 程序输出
直接将文件读到浏览器,不给文件任何执行的机会,会牺牲性能。但是比较保险。
# 权限控制-可写可执行互斥
不给上传的文件有执行的权限。这跟代码本身没关系,而是与服务器有关。这是应该做的,是最低安全保障。
# 总结
- 代码方面 --- 要对文件后缀或者文件类型等进行检查,在读取文件的时候直接将文件内容输出到浏览器而不是执行文件
- 服务器方面 --- 设置好文件读写权限
# 简单示例
app.js
const koaBody = require("koa-body");
app.use(koaBody({
multipart: true,
formidable: {
maxFileSize: 1 * 1024 * 1024 // 设置上传文件大小最大限制,默认2M
}
}));
index.js
/*
'POST /upload': upload,
'GET /showUp/*': showUp,
*/
var upload = async (ctx) => {
// 获取上传文件
let file = ctx.request.files.file;
let ext = path.extname(file.name);
console.log(ext);
if (file.size > 1024 * 1024) {
return ctx.body = "文件大小不能超过 1 m";
}
if (ext != '.png' && ext != '.jpg') { // 后缀名限制,或者也可以使用文件类型检测和文件内容检测,不过比较麻烦
return ctx.body = "请上传后缀名伪 .jpg 或 .png的文件"
} else {
try {
const readerStream = fs.createReadStream(file.path); // 创建可读流
let filePath = path.join(__dirname, '../../upload/') + Date.now() + ext;
const writeStream = fs.createWriteStream(filePath); // 创建可写流
readerStream.pipe(writeStream); // 可读流通过管道写入可写流
return ctx.body = '上传成功!';
} catch (e) {
return ctx.body = '上传失败!请稍后再试';
}
}
}
var showUp = async (ctx) => {
let resPath = ctx.request.path.replace(/^\/showUp\//, '');
let filePath = path.join(__dirname, '../../upload/') + resPath;
if(!fs.existsSync(filePath)){
ctx.status=404;
return ctx.body = "不存在该文件";
}
ctx.body = fs.readFileSync(filePath);
}
text.html
<form action="/upload" method="post" autocomplete="off" enctype="multipart/form-data">
<input type="file" name="file" id="file" value="" multiple="multiple" />
<p><input type="submit" value="Submit"></p>
</form>