增强 jQuery 文件上传:在扩展名更改后进行可靠的 MIME 类型校验

增强 jQuery 文件上传:在扩展名更改后进行可靠的 MIME 类型校验

本教程详细介绍了如何在 jQuery File Upload 插件中实现可靠的文件 MIME 类型校验。针对用户可能通过修改文件扩展名来绕过传统校验的问题,我们采用读取文件头(Magic Number)的方式进行深度验证。通过将校验逻辑集成到插件的 add 回调函数中,确保在文件上传前进行严格的类型检查,从而有效防止非法文件上传,提升应用安全性。

文件上传中的 MIME 类型校验挑战

在web应用中,文件上传功能常伴随着对文件类型的严格限制,以确保系统安全和数据完整性。然而,传统的客户端文件类型校验方法,如依赖 file.type 属性或检查文件扩展名,存在明显的局限性。用户可以通过简单地修改文件扩展名(例如,将一个可执行文件重命名为 .jpg),轻松绕过这些校验,从而上传恶意文件。

最初的尝试可能包括在文件输入框的 change 事件中独立执行文件头校验,并期望它能与 jQuery File Upload 插件协同工作。然而,这种分离的逻辑常常导致问题:on(‘change’) 事件可能无法正确触发或与 fileupload 插件的内部机制冲突,导致校验结果不一致,甚至在显示“文件类型不支持”后仍允许文件上传,这表明校验逻辑与上传流程未能有效同步,且校验状态可能没有被正确清除或重置。

基于文件头(Magic Number)的 MIME 类型校验原理

为了克服传统校验的不足,我们可以采用更底层的“文件头”校验方法。文件头,也称为“魔术数字”(Magic Number),是文件起始位置的一串特定字节序列,用于标识文件的真实类型。例如:

  • PNG 图片文件通常以 89 50 4E 47 (89504e47) 开头。
  • GIF 图片文件通常以 47 49 46 38 (47494638) 开头。
  • JPEG 图片文件通常以 FF D8 FF E0 (ffd8ffe0)、FF D8 FF E1 (ffd8ffe1) 或 FF D8 FF E2 (ffd8ffe2) 开头。
  • PDF 文档通常以 25 50 44 46 (25504446) 开头。

通过读取文件的前几个字节并将其与已知的文件头进行比对,我们可以准确地判断文件的真实类型,即使其扩展名已被篡改。

集成文件头校验至 jQuery File Upload 插件

将文件头校验逻辑直接集成到 jQuery File Upload 插件的 add 回调函数中,是确保校验在上传前执行且与插件流程紧密结合的最佳实践。

HTML 结构

首先,确保你的HTML结构包含一个文件输入框和用于显示上传状态的容器,这些都应被 fileupload 插件识别:

增强 jQuery 文件上传:在扩展名更改后进行可靠的 MIME 类型校验

图改改

在线修改图片文字

增强 jQuery 文件上传:在扩展名更改后进行可靠的 MIME 类型校验455

查看详情 增强 jQuery 文件上传:在扩展名更改后进行可靠的 MIME 类型校验

<div id="myfile_mydrive" class="fileupload">    <div class="fileinput-button btn btn-success btn-sm">       <i class="fa fa-paperclip"></i>       <span>浏览文件</span>       <input type="file" id="myfiles" name="myfiles">    </div>    <table role="presentation" class="table table-striped">      <tbody class="files"></tbody>    </table>  </div>

这里,#myfile_mydrive 是 fileupload 插件的容器,zuojiankuohaophpcninput type=”file” id=”myfiles” name=”myfiles”> 是实际的文件选择控件。

JavaScript 校验逻辑

核心校验逻辑应放置在 fileupload 插件的 add 回调函数中。add 函数在文件被添加到上传队列时立即触发,但在实际上传请求发送之前。

$(function () {   $('#myfile_mydrive').fileupload({     // add 回调函数在文件被添加到队列时触发,是执行校验的理想位置     add: function(e, data) {       var file = data.files[0]; // 获取当前文件       var fileReader = new FileReader();        fileReader.onload = function(e_reader) {         // 读取文件的前4个字节作为文件头         var arr = (new Uint8Array(e_reader.target.result)).subarray(0, 4);         var header = "";         for (var i = 0; i < arr.length; i++) {           header += arr[i].toString(16).padStart(2, '0'); // 转换为十六进制字符串,确保两位         }          // 定义允许的文件类型及其对应的Magic Number         var allowedHeaders = [           '89504e47', // PNG           '47494638', // GIF           'ffd8ffe0', // JPEG (JFIF)           'ffd8ffe1', // JPEG (Exif)           'ffd8ffe2', // JPEG (Canon)           '25504446'  // PDF         ];          // 检查文件头是否在允许列表中         if (allowedHeaders.includes(header.toLowerCase())) {           // 文件类型匹配,允许上传           data.submit(); // 提交文件进行上传         } else {           // 文件类型不匹配,阻止上传并提示用户           alert("文件类型不匹配,请上传图片(PNG/GIF/JPG)或PDF文件。");           // 可以选择清除文件输入框或进行其他错误处理           // 例如:$('#myfiles').val('');         }       };        // 以 ArrayBuffer 格式读取文件内容,只读取前4个字节       fileReader.readAsArrayBuffer(file.slice(0, 4));     },     downloadTemplateId: 'template-download-gallery',     uploadTemplateId: 'template-upload-gallery',     paramName: 'files[]',     url: 'mydrive-upload.php',     dataType: 'json',     autoUpload: false, // 禁用自动上传,以便在校验后手动触发 data.submit()     maxNumberOfFiles: 10,     // acceptFileTypes 正则表达式作为初步的客户端过滤,但文件头校验更可靠     acceptFileTypes: /(.|/)(pdf|gif|jpe?g|png)$/i,   }); });

代码详解:

  1. add: function(e, data): 这是 blueimp jQuery File Upload 插件的关键回调函数。当用户选择文件时,data 对象会包含待上传的文件信息。
  2. var file = data.files[0];: 获取用户选择的第一个文件对象。
  3. var fileReader = new FileReader();: 创建 FileReader 实例,用于异步读取文件内容。
  4. fileReader.onload = function(e_reader) { … }: 当文件读取完成时触发。
  5. var arr = (new Uint8Array(e_reader.target.result)).subarray(0, 4);:
    • e_reader.target.result 包含读取到的文件内容(ArrayBuffer)。
    • new Uint8Array(…) 将 ArrayBuffer 转换为 Uint8Array,这是一个8位无符号整数数组,每个元素代表一个字节。
    • .subarray(0, 4) 提取数组的前4个字节,即文件头。
  6. header += arr[i].toString(16).padStart(2, ‘0’);: 遍历文件头字节数组,将每个字节转换为两位十六进制字符串,并拼接起来形成完整的 Magic Number 字符串。padStart(2, ‘0’) 确保单个数字(如 9)也被格式化为 09。
  7. allowedHeaders.includes(header.toLowerCase()): 将提取到的文件头与预定义的允许 Magic Number 列表进行比对。toLowerCase() 用于确保大小写不敏感的比较。
  8. data.submit();: 如果文件头匹配,则调用 data.submit() 手动触发文件的上传。
  9. alert(“文件类型不匹配…”);: 如果文件头不匹配,则阻止上传并向用户显示警告。
  10. fileReader.readAsArrayBuffer(file.slice(0, 4));: 关键优化。我们只读取文件的前4个字节,而不是整个文件。这大大提高了校验效率,尤其对于大文件。

核心改进与优势

  • 集成到 add 函数: 将校验逻辑置于 add 回调中,确保了文件在被添加到上传队列后、实际上传请求发送前,得到及时且严格的校验。这解决了之前 on(‘change’) 事件与 fileupload 插件之间可能存在的同步问题。
  • 基于文件头校验: 通过读取文件的 Magic Number,我们能够准确判断文件的真实类型,有效防止用户通过修改文件扩展名来绕过校验。这比依赖 file.type 或文件扩展名更具鲁棒性和安全性。
  • 精确控制上传流程: 将 autoUpload 设置为 false,并根据校验结果手动调用 data.submit(),使得开发者对上传流程拥有更精细的控制。

注意事项与最佳实践

  1. 服务器端校验不可或缺: 客户端校验主要用于提升用户体验和初步过滤,但绝不能替代服务器端的严格校验。任何恶意用户都可以绕过客户端脚本,直接向服务器发送请求。因此,服务器端必须对上传的文件进行再次的文件头校验、大小限制、病毒扫描等安全检查。
  2. 扩展支持的文件类型: 如果需要支持更多文件类型,可以查阅常见文件格式的 Magic Number 列表,并将其添加到 allowedHeaders 数组中。
  3. 错误提示优化: 生产环境中,应提供更友好和清晰的错误提示,例如在页面上显示错误消息而不是简单的 alert。
  4. 性能考量: fileReader.readAsArrayBuffer(file.slice(0, 4)) 的使用是高效的,因为它只读取文件的一小部分。即使对于非常大的文件,校验速度也几乎不受影响。
  5. 用户体验: 考虑在校验过程中显示加载指示器,并在校验失败时提供明确的下一步操作建议。

通过上述方法,您可以显著增强 jQuery File Upload 功能的安全性,确保只有符合预设类型的文件才能被成功上传。

以上就是增强 jQuery 文件上传:在扩展名更改后进行可靠的 MIME 类型校验的详细内容,更多请关注php javascript java jquery html js json 正则表达式 字节 回调函数 pdf JavaScript jquery html 回调函数 字符串 var number function 对象 事件 异步 alert input PDF 文档

大家都在看:

php javascript java jquery html js json 正则表达式 字节 回调函数 pdf JavaScript jquery html 回调函数 字符串 var number function 对象 事件 异步 alert input PDF 文档

事件
上一篇
下一篇