ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ASP上传组件实战:部署、排查与安全加固

ASP上传组件实战:部署、排查与安全加固 简介这是一份面向ASP开发者的文件上传组件源码包适用于在线投稿、图片分享、文件共享等需要处理用户上传文件的Web应用场景可帮助开发者快速实现多文件上传、类型校验、大小限制、进度反馈与安全检测等核心功能。压缩包共21个文件大小260KB主要包含5个asp示例与调用页面、3个txt说明与配置文档、3个htm帮助页面、1个mdb数据库及dll组件文件另有js、css、gif、bat等辅助资源目录结构清晰便于直接引入项目。已有111人浏览学习适合有一定ASP基础、希望减少重复开发成本的初级及中级Web开发者参考使用。解压后可结合示例代码快速理解组件调用方式跟随说明文档完成上传表单搭建、服务器端文件保存与合法性检查并参考错误处理机制构建健壮的上传模块。1. 拿到一份“ASP源码—风声ASP上传组件 v2.0.zip”先别急着解压丢进网站我第一次接到这类需求是帮人把一个跑在 IIS 6 上的老站点迁到新服务器。对方扔过来一个 zip名字就叫“ASP源码—风声ASP上传组件 v2.0.zip”里面全是 .asp 和 .inc 文件没有安装程序也没有 DLL。这套东西本质上是把浏览器上传文件的 multipart/form-data 解析逻辑写成了 VBScript 类v2.0 相比 v1.x 一般会多一些可配置属性比如扩展名白名单、文件大小上限、自动改名但部署方式没有本质变化。它要解决的是 ASP 没有原生上传对象的痛点Request.Form 只能拿到表单字段拿不到文件流。适合还要维护老 ASP 系统、或者被要求“把这套老站点跑起来”的读者。下面按我拿到这种 zip 后的完整处理路径展开先讲原理再落地部署调参数最后做安全验证。2. 为什么一个上传都要用组件源码从 Request.BinaryRead 到 multipart 拆包2.1 ASP 原生拿不到文件问题在协议层浏览器上传文件时表单的 enctype 是 multipart/form-data请求体会按 boundary 分隔成多个块每个文件块有自己的头部信息和二进制内容。ASP 的 Request.Form 和 Request.QueryString 只解析 application/x-www-form-urlencoded 格式遇到 multipart 请求时会忽略文件部分普通文本框的值能读到文件流直接丢失。这是协议层面决定的不是 IIS 配置能绕开的。所以早期 ASP 开发者唯一的办法是用 Request.BinaryRead 把原始请求体完整读出来再自己按 boundary 切块。一个最小示意如下% Dim binBody, contentType, boundary binBody Request.BinaryRead(Request.TotalBytes) 从 Content-Type 请求头里取 boundary例如 ---------------------------7d0d contentType Request.ServerVariables(HTTP_CONTENT_TYPE) boundary -- Mid(contentType, InStr(contentType, boundary) 9) 下面需要按 boundary 切成数组再解析每一块里的 Content-Disposition 从中找到 filename... 和 name...抽出文件二进制区。 注意块之间的分隔符是 CRLF而二进制内容里也可能出现 CRLF 所以不能只靠 InStr 找换行得逐字节处理。 %这段代码只是拆包前两步的示意真正写完要处理边界判断、空行位置、文件名编码、二进制区提取代码量会在两百行以上。Request.BinaryRead 的参数是字节数如果超过 AspMaxRequestEntityAllowed 的限制会在读取阶段直接报 ASP 0104 错误这一点后文还会专门讲。大多数人选择上传组件就是因为这些边界情况自己处理起来太容易出错。2.2 三种实现形态为什么源码包是相对省心的形态ASP 上传功能历史上主要有三种实现方式无组件手工拆包、编译 DLL 组件、ASP 源码封装类。三者的差异决定了它们各自的使用场景。实现形态部署成本典型故障适用环境无组件手工拆包零部署直接放 ASP 文件二进制解析出错、文件损坏、中文名乱码虚拟主机、一次性上传需求编译 DLL 组件需要 regsvr32 注册虚拟主机无法操作组件未注册、64 位系统不兼容、被安全软件拦截独立服务器可控制注册表ASP 源码封装类拷贝文件和 include 即可类名/方法名需按源码实际定义调整老系统迁移、需要长期维护“风声ASP上传组件 v2.0.zip”这类包大多数属于第三种把 multipart 解析、文件保存、大小判断、扩展名过滤都封装到一个 VBScript 类文件里页面顶部用!--#include fileinc/UploadCls.asp--引入即可。与 DLL 相比源码包不需要在服务器上执行 regsvr32也不存在 32 位和 64 位注册表重定向的问题与纯手工无组件代码相比它处理了二进制边界、空块、多文件等常见坑。缺点是所有逻辑都是脚本解释执行上传大文件时 CPU 占用偏高这一点要做好心理准备。2.3 先确认包里的文件结构再动代码解压之后先别急着把文件复制到网站根目录。常见打包结构里会有三个部分保存类定义的 .inc 或 .asp 文件、演示上传页和处理页、说明文档。我一般先用 PowerShell 解压并列出文件确认类名和方法名再开始改Expand-Archive -Path ASP源码—风声ASP上传组件 v2.0.zip -DestinationPath C:\inetpub\wwwroot\fsupload -Force Get-ChildItem -Path C:\inetpub\wwwroot\fsupload -Recurse | Select-Object FullName, LengthExpand-Archive 要求 PowerShell 5.0 以上Windows Server 2012 R2 以上的系统基本都满足。如果解压过程中提示文件损坏先看看是不是 zip 带有密码或者使用了古老的压缩算法找原负责人要密码比自行处理更稳妥。列出文件后重点找包含Class关键字的文件里面会有类似Public Function SaveFile、Public Property Let AllowExt这样的定义这些才是后续调用时的真实接口。3. 在 IIS 上把最小上传跑通include 类、写上传页、处理页3.1 准备目录和权限上传功能落地第一步不是写代码而是把保存文件的目录建好并给 IIS 匿名用户写入权限。以 Windows Server 2012 以后的系统为例IIS 应用池默认使用 ApplicationPoolIdentity对应到文件系统里是 “IIS AppPool\应用池名称” 这个账户。右击上传目录在安全页签里添加该账户并授予修改权限。如果站点用的是 IIS 6 兼容模式或经典管线则可能是 IUSR 或 IUSR_机器名不确定时先看应用程序池的高级设置里“标识”是哪一种。目录建在哪也有讲究。常见做法是放在站点根目录下的 uploads 文件夹比如C:\inetpub\wwwroot\fsupload\uploads后面调用组件时传相对路径。不要直接传到站点根目录更不要传到数据库备份目录这种敏感位置否则后面做权限隔离会非常麻烦。3.2 前端上传页的最小代码一个能触发文件上传的表单核心点是 form 的 enctype 必须是 multipart/form-datamethod 必须是 post。下面是最小示例form actiondoUpload.asp methodpost enctypemultipart/form-data input typetext nameremark value测试备注 / input typefile namefile1 / input typesubmit value开始上传 / /form如果漏掉 enctype浏览器会按默认的 x-www-form-urlencoded 发送服务器端收到的只是文件名这个字符串文件内容不会进入请求体。这里的namefile1要与后面处理页里组件的文件字段名保持一致多个文件时有的 v2.0 包支持namefile1的数组形式有的支持多个不同 name具体以你 zip 内 demo 页的写法为准。3.3 处理页调用组件的标准写法处理页的核心逻辑只有四步引入类文件、创建上传对象、设置参数、执行保存。下面是兼容大多数源码包的写法!--#include fileinc/UploadCls.asp -- % 关闭错误回显避免半截 HTML 影响判断 On Error Resume Next 类名按 zip 内 inc 文件实际定义调整常见有 Upload、ClsUpload、FsUpload Set up New Upload 保存目录指向站点物理路径下的 uploads 文件夹 up.SaveTo /uploads 扩展名白名单只允许这些类型提交 up.AllowExt jpg,gif,png,zip,rar,doc,pdf 最大文件大小单位是字节这里约等于 10MB up.MaxSize 10 * 1024 * 1024 自动重命名建议设为 True可以绕开中文名和重名 up.AutoRename True If up.SaveFile() Then Response.Write OK| up.FilePath Else Response.Write ERR| up.ErrorMsg End If %这段代码里最需要改的是类名Upload。不同版本组件定义的类名差异很大如果实际类名不是 Upload调用时会报“缺少对象”的错误。SaveFile 是执行保存动作的常见方法名有的包叫 SaveAs 或 Process确认方法名后把这里同步替换。SaveTo 属性是保存路径组件一般会在当前站点物理路径下拼接这个相对路径不要用带盘符的绝对路径迁移服务器时会失效。AllowExt 用逗号分隔且不带点号是用来过滤文件名后缀的不是过滤 Content-Type。设置完成后调用 SaveFile返回 True 时 FilePath 里是保存后的相对路径可以直接拼到 img 标签或写入数据库。3.4 把 demo 页面改成自己的业务逻辑v2.0 包附带的 demo 通常只是打印上传结果实际项目里要对接数据库。常见做法是把 FilePath 和表单里的 remark 字段一起写入记录表 假设数据库连接已存在于 conn 对象中 Dim sql sql INSERT INTO t_upload(file_name, file_path, remark, create_time) VALUES ( _ up.FileName , _ up.FilePath , _ Replace(Request.Form(remark), , ) , _ Now() ) conn.Execute sql注意 Request.Form 里的字符串拼 SQL 之前必须做单引号转义否则 remark 里出现单引号会直接把语句打断形成注入点。这是老 ASP 项目里最常见的问题v2.0 组件只负责文件上传不会帮你处理 SQL 注入。FilePath 字段建议存相对路径配合站点域名拼完整 URL这样换域名时不用改数据库。4. 上传大小、执行超时与中文文件名v2.0 最常见的三个坑4.1 ASP 0104 与 maxAllowedContentLength上传大小被两层限制卡住上传文件明明不大页面却报Request object error ASP 0104: 80004005这是判断上传类问题时第一个要排查的现象。原因是 IIS 对请求实体大小有两层限制ASP 脚本层的 AspMaxRequestEntityAllowed以及 IIS 请求过滤层的 maxAllowedContentLength。IIS 6 默认只有前者默认值 204800 字节也就是 200KB传个稍大的图片就报错。用 IIS 6 的经典命令调整大小的方式如下cscript.exe %SystemDrive%\inetpub\AdminScripts\adsutil.vbs SET w3svc/1/root/AspMaxRequestEntityAllowed 10485760这条命令把默认站点根路径的上传上限改成 10MB。注意 w3svc/1 里的 1 是站点序号多站点时要对应到具体站点。修改完成后执行 iisreset 重启 IIS 让配置生效。IIS 7.5 之后这个值改由 web.config 里的配置节控制configuration system.webServer security requestFiltering requestLimits maxAllowedContentLength10485760 / /requestFiltering /security asp limits maxRequestEntityAllowed10485760 / /asp /system.webServer /configurationmaxAllowedContentLength 默认是 30000000 字节约 28.6MB所以小文件一般不会触发这层但如果你把组件里的 MaxSize 调大比如允许 100MB这两处配置必须同步调大否则组件还没开始读流就被 IIS 拦截了。我一般会先在 web.config 里把两层都写出来避免不同服务器默认行为不一致。4.2 脚本超时与慢速上传的处理文件超过 20MB 或者用户网络很慢时经常出现上传到一半页面直接变空白日志里没有错误码问题多半出在 ASP 脚本超时。ASP 脚本默认执行时间是 90 秒上传走的是脚本解释执行整个文件流要在这 90 秒内读完并写入磁盘否则 ASP 引擎会强制中断。处理页顶部加一行可以临时延长单个页面的超时时间Server.ScriptTimeout 300单位是秒300 秒等于 5 分钟。如果上传需求经常达到 50MB 以上我建议把超时设到 600同时换用分片上传方案而不是只依赖组件。另外保存文件时优先使用组件内部基于 ADODB.Stream 的写入方式避免用传统 FSO 的 TextStream 写二进制文件TextStream 面向文本流遇到二进制内容会丢字节。4.3 中文文件名乱码与自动改名策略中文文件名上传后在服务器上变成乱码是这类组件的典型问题。原因有两个层面页面或组件源码的编码不是 UTF-8以及 multipart 块头里的 filename 解析时按单字节处理。最省事的办法不是去修组件的解码逻辑而是直接启用自动改名让服务器用生成的规则文件名保存。 在组件参数里开启自动重命名 up.AutoRename True 也可以自己生成规则文件名然后赋值给组件 Randomize Dim newName newName Year(Now()) Right(0 Month(Now()), 2) Right(0 Day(Now()), 2) _ Int(Rnd() * 999999) . up.FileExt这段代码里日期部分生成 20260611 这样的前缀Rnd 生成随机数拼在后面FileExt 是组件解析出来的原文件扩展名。这样既保留了扩展名用于判断文件类型又完全绕开了中文解码问题。如果业务上必须保留原始文件名再去改组件的字符集解析但 IME 这行的收益很低。4.4 上传失败的现象与排查对照现象常见原因处理方向报 ASP 0104: 80004005请求实体大小超限调整 AspMaxRequestEntityAllowed页面 404Form 提交后无响应enctype 写错或 action 路径不对检查 form enctype 和相对路径保存成功但文件 0 字节组件用了文本方式写文件确认组件内部用的是 ADODB.Stream纯英文文件名正常中文乱码页面编码与解析编码不一致开启 AutoRename 或统一 CodePage视频文件一直上传失败脚本超时或单请求大小限制加 Server.ScriptTimeout 和请求限制排查顺序一般是从外到内先看 IIS 日志有没有 404、413、500404 是路径问题413 是请求体太大被 IIS 拦截500 再看组件内部哪一行报错。组件类文件里的错误处理往往只是On Error Resume Next如果它自己吞掉了异常就把处理页的On Error Resume Next注释掉让错误真实抛出能看到具体行号。5. 上传目录安全加固与用一次真实的 multipart 请求做验证5.1 让上传目录变成“只存不执行”的目录文件能传上来只是开始真正麻烦的是有人传了一个 .asp 文件并在浏览器里直接访问它。只要上传目录允许脚本执行这一步等于把服务器大门敞开。IIS 7 之后可以在 web.config 里针对上传目录单独收紧configuration location pathuploads system.webServer handlers accessPolicyRead / staticContent mimeMap fileExtension.jpg mimeTypeimage/jpeg / /staticContent /system.webServer /location /configurationhandlers 的 accessPolicy 设为 Read表示该目录只提供静态文件读取不执行 ASP、ASPX 等动态脚本。这样即使有人绕过扩展名白名单传上去一个 xx.asp访问时也只会被当作静态内容或直接拒绝不会执行。IIS 6 环境下对应操作是在目录属性的“执行权限”里选择“无”效果相同。5.2 在组件源码里补三道防线扩展名白名单是最浅的一层容易被大小写、双扩展名绕过。我在现有组件上会再加两道检查文件内容头校验和路径穿越过滤。读取文件前几个字节判断真实类型是有效手段Function IsImageFile(bytArr) Dim b0, b1, b2, b3 b0 AscB(MidB(bytArr, 1, 1)) b1 AscB(MidB(bytArr, 2, 1)) b2 AscB(MidB(bytArr, 3, 1)) b3 AscB(MidB(bytArr, 4, 1)) If b0 HFF And b1 HD8 And b2 HFF Then IsImageFile True JPEG ElseIf b0 H89 And b1 H50 And b2 H4E And b3 H47 Then IsImageFile True PNG ElseIf b0 H47 And b1 H49 And b2 H46 Then IsImageFile True GIF Else IsImageFile False End If End Function调用时机在 SaveFile 内部真正写盘之前先检查文件头再决定是否保存。对应地路径穿越过滤需要检查原始文件名里是否包含..或\有就直接拒绝避免通过构造文件名把文件写到站点目录之外If InStr(LCase(up.FileName), ..) 0 Or InStr(up.FileName, \) 0 Then up.ErrorMsg Illegal filename End If5.3 用 curl 模拟一次完整的 multipart 上传验证页面调试完用浏览器点几次表单只能覆盖正常路径。我习惯用 curl 构造一个真实的 multipart 请求验证上传接口对不同大小、不同扩展名文件的响应方便脚本化回归curl -i -F remarktest -F file1D:\tmp\test.jpg http://127.0.0.1/fsupload/doUpload.asp-F 参数会自动生成 multipart/form-data 格式并把 test.jpg 的内容放进 file1 字段。返回里包含OK|/uploads/20260611_123456.jpg说明上传成功返回 ERR 则对照 ErrorMsg 判断是哪层拦截。再构造一次超大文件或者把 test.jpg 改名成 test.asp 上传确认 Response 里返回的是 ERR 而不是成 OK。全部通过后去 uploads 目录手工检查文件字节数和文件头是否与源文件一致再确认数据库里的 filepath 字段能拼出可访问的 URL这个 zip 才算真正接手完成。本文还有配套的精品资源点击获取
返回列表