Web安全漏洞排查清单:从SQL注入到越权修复实战
做了几年安全测试和漏洞修复我最大的感受是大多数安全问题翻来覆去就那么几类原理不复杂但总有人前赴后继地踩坑。很多公司花钱买了扫描器却连最基本的注入和越权都没堵住原因不是工具不好用而是团队对漏洞的认知停留在“听说过”层面一说修复就只知道加WAF完全不理解漏洞为什么会产生。这篇文章我想把这几年和漏洞打交道的过程做个沉淀把最常见的漏洞类型、攻击链路、修复思路整理成一份能直接用的清单。不管是正在补课的新人还是要给团队做安全培训的负责人希望你看完能对“漏洞到底是怎么回事”有一个整体的判断力——知道什么场景容易出什么问题出了问题该怎么排优先级而不是盲目跟风买设备、上产品。1. 注入类漏洞拼接字符串是最危险的习惯注入类的核心痛点就一句话该当数据处理的东西被当成代码执行了。这类漏洞在各类榜单上常年霸榜不是因为它高级而是因为开发习惯太普遍——只要有人喜欢拼字符串就有人能利用这条缝隙。1.1 SQL注入一条引号引发的数据泄露SQL注入是最经典、也是最好理解的一种。当后端代码把用户输入直接拼进SQL语句时输入里的引号和语句片段就被数据库当成指令执行了。最常见的问题写法长这样$sql SELECT * FROM users WHERE id . $_GET[id]; $result mysqli_query($conn, $sql);攻击者在浏览器里输入1 OR 11这条SQL就变成SELECT * FROM users WHERE id 1 OR 11直接把整张表查出来。更狠一点配合UNION SELECT把密码字段带出来或者用INTO OUTFILE写一句话木马。我见过不止一次这样的案例某后台管理系统的导出功能日期参数是拼进去的攻击者通过时间盲注把管理员的密码哈希一点点拖了出来然后离线跑字典破解最后登录后台删数据。整个过程没触发任何报警因为日志里看起来就是正常的几次导出请求。修复这事儿其实不难查询一律参数化让数据库把输入永远当值处理而不是当代码拼进执行计划。以PHP为例$stmt $conn-prepare(SELECT * FROM users WHERE id ?); $stmt-bind_param(i, $_GET[id]); $stmt-execute();Java、Python、Go都有对应的PreparedStatement、参数绑定机制思路完全相同。参数化之后用户再输入什么花活都只是数据SQL结构纹丝不动。提示不要以为加了ORM框架就万事大吉。ORM的链式查询API也有不少场景存在字符串拼接比如动态排序字段、复杂原生查询语义一旦有字段名由前端传入理论上是存在同类注入路径的只不过利用难度高一些。1.2 命令注入与模板注入从参数到RCE的距离比起SQL注入命令注入和模板注入的危害更直接——前者可能导致服务器被人接管后者往往直接通向远程代码执行。命令注入常见于系统管理类功能某个接口要ping一个IP、要解析一个域名、要执行一个外部程序。开发图省事直接把参数拼进系统命令ip request.args.get(ip) result subprocess.run(ping ip, shellTrue)此时攻击者传入127.0.0.1; cat /etc/passwd分号后面的内容就会被shell当成新命令执行。修复方式也很明确避免shellTrue用参数列表方式传参并且对输入做严格白名单校验——判断是否为合法的IP格式不是就直接拒绝。模板注入则更隐蔽。一些内容管理系统允许用户自定义邮件模板、报表模板如果模板引擎直接渲染了用户可控的字符串攻击者通过构造特定语法就能调用底层函数执行代码。以Jinja2为例精心构造的payload可以读取环境变量、执行系统命令。这类漏洞的修复核心是不要让普通用户直接操作模板源文件模板渲染走固定页面数据内容走变量注入并严格隔离模板解析环境。1.3 注入漏洞的统一修法永远把数据与代码分开注入漏洞虽然形态各异但修复思路高度统一输入校验白名单化、参数绑定或参数化调用、最小权限运行。第一层是校验凡是进入业务逻辑的参数必须先验证类型、格式、长度、取值范围。手机号必须有手机号的正则格式IP必须是合法的IPv4/IPv6格式ID必须是整数。校验通过才能往下走。第二层是绑定数据库查询用参数化系统命令调用用参数数组方式模板渲染和代码生成绝不直接拼接用户输入。第三层是最小权限数据库账号别给DROP、FILE权限别用root跑Web服务容器里运行命令的账户不给sudo权限。三层都做了即便某条链路出现绕过损失也能控制住。这三层做法不是安全团队逼出来的教条而是每一条都有对应着真实攻击路径。我有次排查一个老系统发现管理员账号的权限是DBA级别注入点虽然被修了但如果哪次的修复漏了一处攻击者就能直接遍历所有数据库。把权限收窄后同样的漏洞就算再出现利用成本也翻了十倍不止。2. 前端防线的三连坑XSS、CSRF与点击劫持如果说注入类漏洞是后端代码的习惯问题那前端的坑就更偏“默认信任”了。很多开发者觉得输出到页面的内容没啥风险结果XSS、CSRF这些漏洞往往就在这种放松中爆发。2.1 XSS三种形态与存储型危害XSS跨站脚本攻击的本质是攻击者提供的恶意脚本被目标网站当成了可执行内容输出。按照触发位置分成三种反射型恶意脚本藏在URL参数里每次都要欺骗用户点击构造好的链接才能触发。存储型恶意脚本保存在服务器上比如评论区、个人简介、商品标题所有访问这个页面的用户都会被打中危害最大。DOM型不经过后端纯粹在前端JavaScript里通过DOM操作把恶意代码插入页面执行。存储型XSS我印象最深。某内容社区允许用户提交昵称昵称在后端没有做过滤前端渲染时直接插进了HTML。攻击者把昵称设置成一段JavaScript指向一个钓鱼页面管理员在后台一打开用户列表浏览器就带着管理员的登录态请求了那个钓鱼页面结果会话凭证被转走。管理员账号说丢就丢前后不到十分钟。修复XSS的核心是输出编码什么时候都不把用户输入当成HTML去渲染。具体来说在HTML标签内输出转义、、、、在JavaScript字符串中输出转义引号和反斜杠在URL属性中输出做URL编码富文本场景用白名单标签过滤而不是黑名单禁用。同时配合响应头Content-Security-Policy限制脚本来源即使某处漏了浏览器也会拦下外联的恶意脚本。这是一层非常重要的兜底很多团队完全忽略了它。2.2 CSRF与点击劫持的利用差异CSRF跨站请求伪造和XSS容易混淆实际上完全不同。XSS是利用合法用户在页面里执行脚本CSRF是让合法用户在不知情的情况下发起攻击者指定的请求。举个例子某后台有修改绑定手机的接口请求方式是GET参数是newPhone和token。攻击者在论坛上放一张图片图片地址就是后台接口的完整URL。管理员只要在登录态下点开这个帖子浏览器就会自动请求那个接口把绑定手机改成攻击者的号码。整个过程管理员毫无感知攻击者拿到了关键的账号恢复入口。CSRF的修复三板斧同步Token校验每个请求带上随机的CSRF Token服务端校验存在且未过期同源检查校验请求的Origin和Referer是否来自本站敏感操作二次验证修改手机、绑定邮箱、转账这些操作要求输入原密码或短信验证码。点击劫持则更加“视觉化”。攻击者构造一个透明的页面叠放在正常按钮上方用户看到一个诱导按钮实际点击的是被隐藏的目标按钮。因为整个过程用户都是主动操作的浏览器安全机制很难防御。后端加一个X-Frame-Options: DENY响应头或者用CSP的frame-ancestors指令禁止页面被嵌入第三方iframe就能基本杜绝。2.3 前端防御的正确姿势前端漏洞修复不能只看“输出编码”这一点。我建议团队做一次整体梳理第一全局搜索DOM操作位置重点关注把innerHTML、outerHTML、document.write直接赋值的场景统一改成textContent或安全的DOM构建API。第二梳理所有敏感接口逐个检查请求方法是否合理、是否带了CSRF防护、是否有额外的身份校验字段。特别留意使用GET方法完成状态修改的接口。第三设置安全响应头。除了X-Frame-Options和CSP还有X-Content-Type-Options: nosniff防止浏览器把上传文件或其他类型资源当脚本解析。这三个响应头配起来几分钟能挡掉一大片基于浏览器默认行为的攻击。前端安全很容易被当成“小问题”实际上一旦出了存储型XSS整个系统的会话安全都形同虚设。把它排在和SQL注入同等的优先级一点不过分。3. 服务端信任边界的失守文件上传、SSRF与XXE这一类的共同点是服务端对外部数据太过信任在输入进入系统内部逻辑之前没有做必要的隔离和约束。尤其是文件上传和SSRF是攻破内网的两条高频路径。3.1 文件上传后缀白名单不是终点文件上传功能几乎每个业务系统都有头像、附件、导入Excel、发布图片。漏洞也几乎都出在这些地方因为开发往往只做了后缀校验甚至只是前端校验。攻击者的思路很简单先上传一个.jpg文件但Content-Type设置为application/x-php配合Apache的解析特性某些老配置下能被当成PHP执行或者上传.php.jpg、.php%00.jpg这类文件看哪个环境配置存在解析歧义更直接的是上传一个合法的WebShell然后通过目录遍历找到路径直接访问执行。我的建议是文件上传的修复至少做四层白名单后缀不是允许哪些而是只允许业务必要的扩展名其他一律拒绝内容校验图片用getimagesize或服务端图形库重新解码编码不保留原始字节流文档类校验文件头的魔数存储隔离上传文件保存到独立存储服务或独立目录执行权限一律去掉Web服务器不能把该目录下的文件当脚本执行随机命名文件名改成随机字符串后缀固定为业务白名单内的扩展名剥离原始文件名。曾经有一次渗透测试我在某系统上传了一个图片马因为对方只做了后缀检测没有做内容检测图片的EXIF信息里塞的PHP代码被解析执行了。这类问题在加固后基本可以杜绝但前提是白名单覆盖的是完整链路而不是只堵了一个入口。3.2 SSRF从外网打进内网的后门SSRF服务端请求伪造是很多内网攻击的起点。它发生在服务端接收了一个URL参数然后由服务器去请求这个URL。典型的场景有图片代理、Webhook配置、远程文件导入、URL预览。攻击者把参数改成内网地址比如http://192.168.1.10:80/或http://127.0.0.1:3306/服务器就会去访问内网服务。通过返回的内容或响应时间攻击者能探测内网端口、读取内网服务信息再配合其他漏洞进入内网横向移动。修复SSRF的思路相对固定协议限制只允许HTTP/HTTPS禁止file://、gopher://、dict://等协议地址限制解析目标域名的IP过滤内网IP段、回环地址、链路本地地址禁止直接访问端口限制业务需要只开放特定端口其余一律拒绝请求抓包核对有的攻击会用DNS重绑定绕过IP校验这时候最好的兜底是实际访问后再核对一次Socket连接的IP确保连接对端就是白名单范围内的目标。有一次排查某个在线预览服务攻击者通过构造特殊URL读取到了本地的配置文件。原因就是代码直接用了Ruby的open()方法方法支持file://协议。把协议白名单设置好之后这个问题就没再出现过。3.3 XXE被忽略的XML解析器XXEXML外部实体注入曾经是Java和PHP老系统中的常客现在依然偶尔出现尤其是那些处理SOAP协议、XML配置导入、Office文档解析的服务。攻击原理很简单XML规范允许定义外部实体如果解析器开启了外部实体解析而且没有禁止外部实体加载攻击者就可以在XML里写!DOCTYPE foo [!ENTITY xxe SYSTEM file:///etc/passwd] rootxxe;/root解析结果会把外部文件内容带入XML响应里攻击者因此读取到服务器上的敏感文件。更高级的利用还能发起内网端口探测、读取源代码。修复方式非常直接如果业务不用DTD直接禁用DTD和外部实体。Java解析器设置setFeaturePHP设置libxml_disable_entity_loader(true)Python的lxml关闭resolve_entities。如果确实要用XML而且必须支持外部实体那就要把白名单范围做得很窄并且对外部实体内容做严格过滤。现在很多系统已经改用JSON传输数据XML类接口大幅减少但遗留系统和第三方对接过程中依然存在渗透测试时值得重点盯一下。4. 权限与认证的薄弱环节越权、暴力破解与反序列化注入、XSS这些是“代码逻辑”层面的漏洞权限与认证这一章则是“业务逻辑”层面的漏洞。这类漏洞没有统一的payload可查扫描器很难发现往往需要人工测试所以也最考验开发者的安全意识。4.1 IDOR越权可遍历ID带来的越权越权漏洞特别是IDOR不安全的直接对象引用是说攻击者通过修改URL或参数中的ID访问到了不属于自己的资源。最典型的例子用户下单后查看订单详情接口是/order/detail?id10001后端查询时只用了这个ID没有验证当前登录用户是否是这笔订单的归属者。攻击者把ID从10001改成10002就能看到别人的收货地址、手机号、支付记录。这种漏洞的可怕在于开发完全没写错代码——查询、返回、展示每一步都是正常的唯一的疏漏是少了一次权限校验。修复方案是在服务端对每个资源访问都做归属权校验查数据库的时候条件里加上WHERE order_id ? AND user_id ?后面的user_id从当前会话取绝不从前端JSON或URL参数里取。还要额外注意有些团队用Nginx直接按id匹配静态文件路径比如/files/{id}/xxx.pdf这类场景也要做同样的鉴权。我做代码审计时会给团队立一个规矩涉及用户数据的接口随手写一条断言校验数据归属和角色权限没有校验不许上生产。4.2 弱口令与无效限速暴力破解为何屡试不爽很多系统像堡垒一样防住了所有漏洞点最后却在登录页被弱口令攻陷。原因不外乎两个一是用户密码太简单二是登录接口没有有效的防暴力破解机制。暴力破解的手段不外乎使用默认密码表admin/admin123、test/test逐个尝试批量撞库拿已经泄露的帐号密码组合来撞未改密用户对某个特定用户发起高频字典攻击只要能连续尝试大部分弱密码都撑不过几轮。修复要从两端同时入手。入口端登录接口必须部署防暴力破解IP维度限流、账号维度锁定、验证码图形验证或滑块验证均可在连续失败多次后弹出来。配置端强制初始密码修改、弱密码复杂度校验、密码定期轮换。更关键的是公司内部系统启用双因素认证即使某位用户的密码真的泄露攻击者也进不来。我不太赞成用单纯的次数锁定来防破解——因为攻击者可以用分布式IP池绕开IP维度限制。更合理的做法是连续5次失败就要求图形验证码连续10次失败锁定15分钟锁定时间随次数递增并允许用户通过绑定的手机号自助解锁。这样既防止滥用又不影响正常用户体验。4.3 反序列化一个对象还原引发的代码执行反序列化漏洞属于原理较深、利用门槛较高但一旦成功危害极大的类型。它发生在把二进制格式或字符串格式的序列化数据还原成对象的过程中如果还原逻辑里调用了某些“魔法方法”攻击者就能控制参数触发危险操作。PHP中经典的利用点是一些魔法方法像__wakeup()、__destruct()在对象还原时自动执行Java中利用点是ObjectInputStream.readObject()配合存在危险方法调用链的Gadget类。攻击者会构造一个恶意的序列化字符串传给任何接收反序列化数据的入口Cookie、Session存储、缓存数据、消息队列消息从而在目标机器上执行任意代码。修复优先选替换方案能不用原生序列化就不用换成JSON等纯数据格式。如果一定要用则采用白名单机制只允许反序列化指定类的对象并对反序列化数据签名校验杜绝恶意流进入解析流程。另外升级依赖库版本也能堵住很大一部分已知Gadget链。很多反序列化漏洞的根因不是业务代码本身而是低版本第三方库。这类漏洞不是每个项目都遇得到但只要遇到基本都是严重以上等级。建议团队排查所有入口是否使用了原生反序列化函数有的话单独列一个专项治理计划。5. 信息泄露与配置缺陷最容易被低估的隐患上面聊的都是主动攻击这一章说的信息泄露与配置缺陷更像是“被动送给攻击者的礼物”。很多团队对外部漏洞严防死守却忘了自己不小心在响应头、错误页、备份文件里泄露了大量信息。5.1 错误页与调试信息泄露生产环境开着debug模式可以说是最令人无语的安全漏洞之一。页面报错时直接把堆栈打印出来攻击者看到代码路径、数据库版本、中间件信息、甚至配置文件片段后续的定向攻击就变得非常容易。还有一类是备份文件和源码泄露.git目录可以直接访问、备份压缩包放在Web目录下、开发环境测试脚本部署到了生产。攻击者把这些文件下载下来代码审计就像看自家项目一样轻松。修复方式很简单但需要刻意检查生产环境关闭debug、trace、verbose日志统一错误页面只展示友好提示不暴露任何异常详情详细错误走服务端日志检查Web根目录下是否有.git、backup.zip、.sql、test.php这些文件发现立即移走用目录枚举工具或者手动检查排除规则把不应该被公众访问的路径在Nginx/Apache配置里显式拒绝。5.2 传输与存储中的明文问题明文问题分两类传输过程明文化和存储过程明文化。传输过程很多系统内部接口还在走HTTP而非HTTPS。攻击者在同一Wi-Fi或者在运营商链路中做中间人抓包账号密码、Token、业务数据直接裸奔。修复就是全局启用HTTPS并在响应头加Strict-Transport-SecuritySTS强制浏览器在一段时间内只走HTTPS连接防止降级攻击。存储过程的明文问题常见于密码库用了明文或只用MD5摘要存储没有加盐撞库时能反向匹配用户手机号、身份证号这类敏感字段在数据库中直接明文保存一旦脱库就是批量泄露。存储层的修复思路是用户密码用bcrypt、argon2这类自适应慢哈希并加随机盐敏感数据字段按合规要求做加密存储端到端的加密传输尤其需要注意日志系统别把账号密码打到日志文件里。曾有一起事件攻击者没有黑进业务系统而是直接下载了未授权访问的日志文件从中提取到了上万条明文密码。5.3 几个必查的默认配置陷阱目录列表Apache/Nginx的autoindex开启后访问没有index文件的目录就能列出所有文件名源码、备份、内部资料一览无余。关闭方法autoindex off或者把Nginx的index配置清除掉。默认账号很多中间件和数据库安装时会生成默认账号如某数据库的admin/admin上线前必须改掉。开发环境、测试环境和生产环境的密码要完全区分内部测试库的弱密码也是泄露源头。CORS配置过宽Access-Control-Allow-Origin: *允许任意跨域读取数据配合CSRF或XSS用户数据可能被第三方站点拉走。正确配置应该是一个精确的允许列表并且非公开数据接口不设置通配符。配置类问题往往不被当成“漏洞”但它确实是行业漏洞库里的常客。强烈建议上线流程里加一个配置检查脚本把上述几项做成自动化校验每次发版自动跑一遍发现违规直接阻断发布。由此能看到很多安全隐患都在配置检查中消解掉大半。6. 按优先级排险从自动化扫描到人工复核各项漏洞类型都梳理完了解决完“这是什么”之后真正关键的是怎么把它们变成一套可持续的行动方案。最后这章想分享的是我平时在项目里实践出来的漏洞排查顺序和工具使用方式。6.1 常用工具链与使用场景安全测试工具很多但每一类工具适合的场景不一样选对工具能省大量时间工具/类型主要用途适用阶段抓包代理工具分析与修改HTTP请求测试越权、参数篡改、CSRF人工测试注入检测工具自动化检测SQL注入、XSS、SSRF等Web漏洞上线前扫描端口/服务扫描工具发现对外开放的端口与服务识别默认配置风险资产梳理依赖安全检查工具检测第三方库已知漏洞如某些版本的反序列化问题持续集成源码静态分析工具从代码层面发现不安全函数、硬编码密钥、注入风险开发阶段以经验而言老老实实按顺序做往往效率最高先用端口/服务扫描工具摸清资产边界再做Web应用漏洞扫描最后抓包工具逐接口人工复核业务逻辑漏洞。很多团队买了扫描器发现报告生成得很快结果全是中低危的噪音。原因就是扫描器只能发现“规则已知”的问题像越权、逻辑漏洞这类没有固定模式的缺陷必须人工测试。所以我的排险流程一定包括“自动化工具人工复核”两步缺一不可。6.2 修复优先级怎么排漏洞修复不能“扫出来就全量修”一定要排优先级否则会被低危问题淹死真正的关键风险反而无人处理。我的排序逻辑是看三件事可被直接利用的程度无需登录或只需低权限就能利用的优先级最高。比如未授权访问、存储型XSS、RCE类漏洞。影响范围能影响所有用户还是只影响个别角色管理员权限被拿下基本就是最高优先级。被攻击的难度需要社工配合、需要特定浏览器版本、需要内网环境这些会降低优先考虑价值。参考这个思路我会把漏洞分成四个梯队P0远程代码执行、未授权访问、SQL注入、存储型XSS——立即修4小时内响应最好当天上线修复补丁。P1越权、SSRF、批量账号信息泄露、弱口令——2-3天内修完并同步排查日志确认没有被利用痕迹。P2信息泄露、配置缺陷、缺少安全响应头——下一个版本周期修复即可但要进入待办清单。P3COS配置过宽、日志记录不完整——持续改善不用单独排期。这里需要强调的是P2和P3类问题不能永远不修。因为攻击者经常组合利用多个低危问题打出一条高危链路比如“日志泄露弱口令CORS配置过宽”组合起来往往就能完成一次完整的横向攻击。6.3 建立自己的漏洞清单与复测机制最后建议每个团队维护一份自己的“漏洞历史清单”把这段时间踩过的坑记录在案。这份清单不需要多复杂包含漏洞类型、出现位置、数据库表或API接口、修复方案、发现人、复测状态。这么做的好处是同样的错误我们这个行业每天都在不同团队重复发生而有了清单新成员接手时可以先对照历史清单自查一遍把常见的“历史遗留”问题先排查掉再开始新的功能开发。复测机制也很有必要。每次修复后都要跑一遍“回归测试”——用和发现漏洞时相同的payload、相同的路径去验证漏洞是否真的关闭了。有些修复只是加了个前端校验绕过前端直接构造HTTP请求还是能打穿。只有重新用攻击者视角去测试才能确认修复到位。安全修复没有“一劳永逸”这回事代码在变、依赖在变、业务也在变过三个月再扫描大概率又有新的问题。把漏洞治理变成周期性的习惯比憋大招搞一次“安全月”有用得多。我自己在带项目时曾试过把代码审计、外部渗透测试、配置基准检查三个环节穿插进每一次迭代中虽然每次投入的精力不多但持续半年后线上系统的重点漏洞基本清零。治安全漏洞靠的从来不是哪一次大扫除而是那套反复迭代的流程能不能跑起来。希望这份清单能给你一点启发从你自己的第一份漏洞记录开始。