服务器只检查 Content-Type,但 MIME 类型由客户端设置,可被伪造
if (!ALLOWED_MIMES.includes(req.body.mime)) {
return res.status(400).json({ error: '类型不允许' })
}
// 问题:MIME 可被客户端伪造!
// 读取文件头部字节
const magic = buffer.slice(0, 4).toString('hex')
const signatures = {
'ffd8ff': 'image/jpeg',
'89504e47': 'image/png'
}
// 根据实际内容判断类型
| 文件类型 | 魔数(十六进制) | ASCII |
|---|---|---|
| JPEG | FF D8 FF | ÿØÿ |
| PNG | 89 50 4E 47 | ‰PNG |
| GIF | 47 49 46 38 | GIF8 |
| 25 50 44 46 | ||
| ZIP | 50 4B 03 04 | PK.. |
| EXE | 4D 5A | MZ |
const fileType = require('file-type')
async function validateFile(buffer, claimedMime) {
// 1. 检测实际文件类型
const detected = await fileType.fromBuffer(buffer)
// 2. 对比声明的类型
if (!detected || detected.mime !== claimedMime) {
throw new Error('文件类型与声明不符')
}
// 3. 检查是否在白名单
if (!ALLOWED_MIMES.includes(detected.mime)) {
throw new Error('不允许的文件类型')
}
return true
}