Web安全这块儿,入行门槛看着不高,但水是真的深。你可能听说过“三百亿条数据泄露”“某大厂被脱库”这类新闻,往深了挖,背后基本都是Web层面的漏洞被利用。无论是刚转行做开发、想往安全方向走的运维,还是已经有几年经验但没系统梳理过漏洞原理的朋友,这篇内容都能给你搭一个比较完整的框架——从攻击面怎么梳理、漏洞为什么会存在,到具体怎么修、怎么防,一整套思路捋下来,比零散刷几十篇漏洞报告要管用得多。
1. 全景视角:Web安全到底在防什么
1.1 攻击者的目标与我们手里的牌
先说一个常见的误区:很多人以为Web安全就是在防“黑客”攻击服务器。其实攻击者的目标因动机而异,可能是窃取数据、勒索赎金、植入后门,也可能是纯粹破坏。但不管动机是什么,攻击者真正盯上的,永远是三个东西:数据、业务逻辑、人。
数据最容易理解——用户手机号、身份证、银行卡、订单记录,这些都是黑产市场的硬通货。业务逻辑则更隐蔽——比如某电商的优惠券兑换接口,攻击者通过反复重放请求就能薅走大把券;再比如某转账系统,攻击者修改请求参数把转账金额改成负数,反而能让账户余额增加。人这个环节就更不用说了,钓鱼邮件、社工手段,往往比技术漏洞更致命。
我们手上的牌其实也不少:Web应用防火墙、代码层面的输入过滤与输出转义、安全配置加固、密钥管理、纵深防御体系……但很多团队的问题不是没工具,而是不知道威胁面在哪里。防御者最怕的不是攻击者多强,而是不知道自己哪里可能挨打。所以我后面的内容,重点会放在“攻击面梳理”和“漏洞根因”这两块,把牌打好,比单纯堆设备重要得多。
1.2 从一条HTTP请求看攻击面
一条普通的HTTP请求长什么样?GET /api/user/info?id=123,就这么一条看似简单的东西,可能涉及攻击点已经超过十个。
第一层是传输通道本身——如果你的应用走的是HTTP明文,数据就相当于在大街上裸奔。中间人攻击、Cookie截获、DNS劫持,全都可能在传输层发生。第二层是请求本身——URL路径、查询参数、请求头、Cookie、Body体,每一个字段都可能是注入点。第三层则是服务端需要处理的各类组件:Web服务器、应用框架、数据库、缓存、文件系统,每个环节都有自己的漏洞形态。
这里我常举一个例子:很多开发者对参数校验的理解就是“前端做个必填项判断”,这完全不够。前端校验只是用户体验的一部分,攻击者用Burp Suite或直接拼一个curl命令就能绕过一切前端限制。真正的安全校验必须放在服务端,否则你的后端代码就像一个嘴上说锁门但实际敞着的大门。
1.3 为什么安全总是“事后补课”
说个实话,国内大部分中小团队的Web安全建设,都处于“出了事再补”的阶段。项目工期紧,排期满,安全评审被压缩到上线前的一个晚上,走个过场就算完了。我自己也经历过类似的事情:某次我们做了一个对外提供数据查询的接口,上线前做安全自查,结果发现在接口参数拼接SQL时没有任何过滤,直接就被注入了整个用户表。好在只是内测阶段被发现,代价相对可控。
这类问题背后核心原因只有一个:安全成本其实主要不是在“修复”,而是在“返工”。同样一个SQL注入漏洞,如果在代码评审阶段发现,改一行代码就能解决;如果在上线后发现,可能要把线上数据迁移、通知用户、修复漏洞、评估影响全部走一遍,成本至少是前者的几十倍。
Web安全想做好,核心思路不是“加几台防火墙”,而是把安全嵌入到软件生命周期的每个环节里。需求阶段梳理资产和信任边界,设计阶段做威胁建模,开发阶段做编码规范和代码评审,测试阶段做安全测试,上线阶段做基线检查和持续监控——每走一步都顺手把安全做了,才不会在最后关头“救火”。
2. 底层原理:漏洞为什么永远堵不完
2.1 罪魁祸首:输入与输出的信任链断裂
所有Web漏洞,本质上其实只有一个根因:代码在某个环节,错误地信任了本不该信任的输入。
我们平时写代码,脑子里默认的情况是“用户会按我设计的方式来操作”。但现实是,攻击者完全控制请求的每一个字节——URL能改、Header能加、Cookie能伪造、Body能塞各种奇奇怪怪的payload。当代码把这些输入直接拼进SQL语句、带进HTML输出、传给操作系统命令或文件路径时,问题就出现了。
举个例子,一段最简单的登录代码:
$sql = "SELECT * FROM users WHERE username = '" . $_POST['username'] . "' AND password = '" . md5($_POST['password']) . "'";在开发者看来,用户输入一个用户名和密码,查询一下数据库,匹配了就放行,逻辑没毛病。但攻击者可以提交admin' --作为用户名,拼出来就变成:
SELECT * FROM users WHERE username = 'admin' -- ' AND password = '...'--在SQL里是注释符,后面的密码校验直接被注释掉,攻击者甚至不需要知道密码就能以admin身份登录。这就是SQL注入的经典形态——代码把用户的输入当成了SQL代码的一部分,信任链在那一瞬间断裂了。
一个更普遍的规律是:凡是“拼接”的地方,都是风险点。SQL拼接、HTML拼接、命令行拼接、XML拼接、URL拼接……只要你在拼接时没有做好边界处理,就一定存在被注入的潜力。
2.2 输出侧漏洞:XSS才是最常见的老大哥
如果说SQL注入是“输入侧”问题,那XSS(跨站脚本攻击)就是典型的“输出侧”问题。服务端把用户提交的内容原封不动地输出到页面上,攻击者提交的内容中包含<script>标签,浏览器就把这段脚本当成页面的一部分执行了。
XSS为什么可怕?因为它能把攻击面从“服务端”扩展到“所有用户浏览器”。举个例子,一个社区论坛允许用户发帖子,如果信息没有做输出转义,攻击者发布一篇包含恶意脚本的帖子,所有看到这篇帖子的用户浏览器都会执行攻击者预设的脚本——窃取Cookie、模拟用户操作、劫持会话、记录键盘输入,攻击者几乎为所欲为。
XSS按触发类型分三种:反射型、存储型、DOM型。反射型是一次性的,payload藏在URL里,需要诱导用户点击恶意链接;存储型是永久的,恶意脚本被保存到服务端,所有看页面的人都中招;DOM型更隐蔽,完全在前端JavaScript里通过DOM操作触发的,甚至很多WAF都拦不住。
修复思路其实也简单——输出转义。在把变量输出到HTML时,把<、>、"、'、&这些特殊字符转义成HTML实体就好了。问题是很多开发框架默认的输出并没有做HTML上下文识别,需要开发者主动调用转义方法。所以我的建议是:别依赖开发者的自觉,直接在模板层做默认转义,或者用Content Security Policy兜底。
2.3 业务的七寸:越权与逻辑漏洞
前面聊的SQL注入、XSS,都是漏洞界的“明星”,但实际上真正大规模发生的,其实是越权漏洞。用大白话说,越权就是“用户A访问了用户B的数据”。
水平越权很常见——某系统里每个用户有自己的订单列表,订单详情URL长这样:/api/order/detail?id=10001。服务端如果只根据id参数查询订单,而没有校验“这个订单是否属于当前登录用户”,那我随便把id改成别人的订单号,就能看到别人家订单的全部信息。垂直越权则更严重——普通用户直接把URL改成管理端地址,如果服务端没做权限校验,就直接获得了管理员权限。
这类漏洞代码层面修复很简单,加一步鉴权校验就行。但为什么漏网率那么高?因为越权漏洞没法靠扫描器发现,只能靠人工测试和上下文理解。这也是我在安全测试里,反复强调“业务分析师+安全测试合并检查”的原因。
还有一个容易被忽视的业务漏洞是逻辑缺陷——比如验证码不需要刷新、订单金额在前端计算、支付回调不验签、优惠券可批量领取。这些漏洞没有复杂的技术含量,靠的是对业务规则不厌其烦地推敲。排查思路就一条:把业务的核心流程画出来,在每个状态流转的边界点问自己——如果我把这一步重复N次、跳过某一步、或者改变参数值,会发生什么?
3. 典型攻击链演示:从信息收集到拿到权限
3.1 信息收集阶段:眼光比工具更重要
一次标准的Web攻击,往往不是从“发payload”开始的,而是从信息收集开始的。攻击者首先会对目标进行被动信息收集——搜索引擎(site语法、Google Hacking)、证书透明度日志(看子域名)、DNS记录、GitHub代码泄露、网盘文件等。这些都是不接触目标系统本身就能拿到的信息。
然后是主动探测,这一步通常会用到几个基础工具:nmap做端口扫描与服务识别,whatweb或wappalyzer做指纹识别(看用的什么框架、什么中间件),dirsearch做敏感目录爆破(探测后台、备份文件、配置文件)。把目标资产摸清楚之后,攻击者会画出一张资产地图,标记出哪些系统是老旧的,哪些是边缘系统防护较弱的,哪个WAF可以绕过,然后集中火力打最弱的一个点。
说句实话,信息收集的深度,决定了后续攻击的成功率。攻击者能通过子域名找到一个没接入统一认证体系的旧系统,再通过旧系统的漏洞拿下内网权限,最后横向移动到核心业务系统——这条攻击链,每一步的起点都是信息收集时的细致程度。防御端反过来也一样:自测时,你要先把自己当成攻击者,从信息收集开始走一遍,才知道哪些边缘资产是没人盯防的。
3.2 利用阶段:从注入到接管的过程记录
为了让你对攻击的推进有直观感受,我模拟一个最典型的场景(已脱敏处理,系统代号“模拟项目X”)。
某系统的后台登录接口存在SQL注入。攻击者通过参数注入拿到管理员账号的密码哈希,之后在本地用字典或彩虹表破解哈希得到明文密码。登录后台后发现存在一个文件上传功能,上传点做了类型校验,但没限制文件内容——攻击者将PHP一句话木马伪装成图片上传,再利用文件包含漏洞(或解析漏洞)执行了木马。拿到WebShell后,攻击者通过UDF提权等方式获取了服务器操作系统的权限。
整个过程拆解下来,漏洞的根本原因是三处:一是SQL拼接,二是文件上传校验不严,三是服务器未做Web目录的执行权限限制。这三个漏洞单看都不算稀罕,但串在一起就形成了一条完整的攻破路径。这也是为什么安全测试要做纵深检查——单点防御再强,也怕多点串联。
3.3 防御视角:如果是我来防守这个系统
同样的系统,换一个懂得安全的负责人来部署,防御策略会完全不一样。
入口层,Web应用防火墙开启SQL注入和XSS的规则集,在未修复漏洞前先把攻击挡在外面;应用层,将SQL语句全面参数化,禁止任何字符串拼接;文件上传功能改成白名单校验(不仅看扩展名,还要检查文件头、MIME类型和内容),同时上传目录禁止执行脚本;服务器层面,Web服务以低权限用户运行,上传目录设置s权限(特殊权限位,禁止脚本执行);监控层面,数据库和Web日志接入集中告警系统,连接数异常、报错日志突增、异常文件上传行为都能实时触发告警,L3级联动的安全运营(SOC)值班人员会在10分钟内响应。
这套方案的核心思路叫纵深防御——不指望某一层完全挡住攻击,而是让攻击者即使突破了一层,也无法继续深入,从而有充足时间触发告警、被人发现、做出响应。说句不好听的,大多数被攻破的案例,不是没设备,而是没形成多层联动。
4. HTTPS与加密:传输层的地基
4.1 不加密的流量有多透明
很多开发者对HTTPS的认知停留在“证书过期了要续”,但在Web安全里,HTTPS已经是底线而非加分项。
用Wireshark抓包演示一下,当用户通过HTTP访问某论坛时,登录提交的username和password字段在包里就是明文。如果用户所在网络环境里有个恶意Wi-Fi热点(比如咖啡馆的免费Wi-Fi),攻击者打开Wireshark设置过滤条件,几秒钟就能把同一网段内所有HTTP请求里的Cookie、密码、Token全部捞出来。这还不算有意的中间人代理——DNS劫持、ARP欺骗都能把用户的流量导向攻击者控制的服务器,明文信息完全等于被直接看光。
所以部署HTTPS不是“保护密码”,而是保护一切经过网络的敏感内容。图片、页面内容、API请求体,只要是HTTP明文传输的,都等于公开给网络链路中每一个能拦截流量的角色。这个观念一定要转变过来:不是“我们系统里没什么核心机密”,而是“链路中任何一个节点都可能成为敌人”。
4.2 全站HTTPS部署的几个关键坑
从HTTP迁移到HTTPS,看起来是“换个端口、装上证书”这么简单,实际操作中让人踩坑的点其实挺多。
第一个坑是混合内容。页面本身用了HTTPS加载,但页面里的图片、脚本、样式资源仍然以HTTP链接加载,浏览器会拦截这类资源,导致页面样式错乱、功能异常。排查方式是打开浏览器开发者工具,在Console和Network里筛选所有http://的资源,一次性替换。
第二个坑是证书链不完整。有些运维只部署了域名证书,忘了把中间证书一起配置,部分客户端会报证书错误甚至拒绝连接。用openssl s_client -connect 域名:443能查出证书链是否完整。
第三个坑是重定向造成信息泄露。有些网站在HTTP上直接返回业务内容后再跳转到HTTPS,用户输入的内容已经在明文阶段传出去了。正确做法是HTTP层面直接301重定向到HTTPS,并用HSTS(HTTP严格传输安全)强制浏览器记住“这个域名只走HTTPS”。
第四个坑更隐蔽——回源链路。你在CDN和用户之间做了HTTPS加密,但CDN回源到源站时如果还是HTTP,链路后半段依然裸奔。正确做法是CDN回源也启用HTTPS,或者通过内网专线传输,这一步很容易被忽略。
4.3 密码与敏感数据的存储
学习Web安全绕不开一个场景:数据库泄露后,用户的密码怎么办?如果开发者直接用MD5存密码,泄露时效果约等于明文。因为MD5是快速哈希,每秒能算几十亿次,字典攻击和彩虹表攻击都能在极短时间内破解大量密码。正确姿势是用加盐的慢哈希算法,比如bcrypt、scrypt、Argon2,这类算法设计上就是“计算尽量慢”,攻击者破解单个密码的成本被天文数字式拉高。以bcrypt为例,成本因子设置为10到12,单次哈希计算耗时大约几十毫秒——对正常登录只是一瞬间的延迟,却让攻击者每秒能尝试的次数降了几个数量级。
顺带说一句,密码重置逻辑里的Token、API调用的密钥、数据库的密码、云厂商的AccessKey,这些都应该用独立的凭据管理方案(如Vault或密钥管理系统)来统一保管,禁止硬编码在代码里或写在配置文件后提交到Git仓库。这一点上我亲眼见过不少团队吃过亏:某公司程序员把数据库连接配置连同云数据库的公网IP一起push到了开源仓库,没过多久整库数据被拖走。凭据一旦进入代码仓库,就等于直接公开了,这一条堪称灾难级事故的起手式。
5. 实战修复:五类高频漏洞的标准化处置方案
5.1 SQL注入的标准化修复流程
我之前接手过一个遗留系统,整个管理后台的列表查询都用的字符串拼接SQL,总数超过两百处。面对这类系统,我的修复策略是分三步走。
第一步,全局搜索拼接模式。用正则扫代码里所有出现在SQL拼接里的变量,梳理出一份清单,把接口及其数据库调用一一对应起来。第二步,逐接口改造为参数化查询。以Java为例,把Statement改成PreparedStatement,SQL里的变量全部换成?占位符,用setString/setLong等方法绑定参数。第三步,二次验证。用SQLMap加上手工测试,对每个修复过的接口跑一遍注入检测,确认原始payload返回的结果不再包含数据库报错特征。
参数化查询的原理很简单:SQL语句的结构在数据库编译阶段就已确定,用户输入只当作数据值处理,永远不可能变成SQL代码的一部分。这相当于把SQL语句和变量之间的“信任边界”通过协议固定了下来,从根上杜绝了注入的可能性。
如果遇到的是MyBatis这类框架,语法类似:<select>标签里用#{param}代替${param},就能避免字符串拼接。唯一需要小心的是ORDER BY、表名、列名这种无法参数化的地方,这类位置通常需要严格白名单校验,不允许用户直接传值。
5.2 XSS的完整加固操作
XSS修复不只是“转义一下就完事”,而是一套分层的处理逻辑。
应用层,对所有动态输出的内容做上下文感知的编码处理。输出到HTML标签内,转义<、>、"、'、&;输出到HTML属性中,用HTML属性编码;输出到JavaScript代码中,用JavaScript转义;输出到URL参数中,做URL编码。用Vue、React这类框架开发时,默认模板语法已经做了自动转义,需要小心的反而是v-html、dangerouslySetInnerHTML这类“绕过转义”的接口。
框架层以外,加强Content Security Policy也是关键防线。CSP头能限制浏览器只执行来自可信源的脚本:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:;设置这条策略后,即使攻击者往页面里塞了恶意脚本,浏览器也会因为来源不在白名单里而拒绝执行。这是XSS防御的“第二道保险”。
还有一个容易被忽略的入口:富文本编辑器。编辑器允许用户贴入自定义HTML,最容易被塞入<img onerror>、<iframe>这类危险标签。处理方式是引入HTML白名单过滤库(如jsoup的Cleaner或DOMPurify),只保留安全的标签和属性,其它全部剥离。
5.3 文件上传漏洞的不留死角策略
文件上传功能的攻防堪称Web安全里的经典博弈。攻击者的绕过思路大致有几类:扩展名绕过(上传shell.php.jpg)、Content-Type绕过(修改请求头)、文件头伪造(在PHP代码前加上GIF89a伪装成图片)、双扩展名和大小写绕过、空字节截断(老版本PHP的%00截断)、以及配合解析漏洞(IIS6.0的asp;.jpg解析)。
防御策略必须在多个层面同时上锁。
第一道锁:白名单扩展名校验。上传时只允许明确需要的扩展名,比如图片就只允许jpg、png、gif、webp。
第二道锁:内容真实性校验。用PHP的getimagesize或Java的ImageIO,检查文件内容是否能被真实解析为图片。能通过这个校验的,意味着文件头、填充字节、结构都是真实的图片,单纯在文件里拼接代码很难同时保持合法图片结构和脚本可执行性。
第三道锁:存储与执行隔离。上传文件不放在Web可访问目录下,或者放在专门的Upload目录并禁止脚本执行。Nginx示例配置:
location ~* ^/uploads/.*\.(php|php5|jsp|jspx|asp|aspx)$ { deny all; }第四道锁:随机文件名。上传后重命名为随机字符串(如UUID),丢弃原始文件名。这不仅防止文件名被用来构造路径穿越(../../shell.php),也增加了攻击者对已上传文件的可预测性。
这四道锁同时开启后,哪怕某一层被绕过,后面还有其它层兜底。千万别只做其中一两个就算完事——我在实际测试里见过太多“只做了扩展名校验”的系统,被改两个请求头就突破的例子。
5.4 越权漏洞的系统化体检方法
越权漏洞的排查,比前面几个都要抽象,因为它不依赖某种固定的payload模式,而是依赖“上下文理解”。我整理过一套还算实用的体检流程,分享给大家。
先梳理系统的角色矩阵:普通用户、编辑、管理员、超管,每种角色能访问哪些功能、能操作哪些数据。然后拿着高权限账号,摸清每个接口参数的含义,特别注意业务数据的归属字段,如userId、orderId、fileId这类。再切换到低权限账号,逐个访问高权限才能访问的接口,看返回码和数据。最后针对所有接口,手动修改资源ID参数,越权访问其他人的数据。
修复方式也比较固定:后端在查询、更新、删除数据前,先做归属校验——当前登录用户的ID必须与资源的所有者ID匹配,或者当前用户角色在目标操作的授权范围内。测试时用得比较多的小技巧是:登录两个账号A和B,用A创建的某个资源,换成B的身份去操作;很多团队漏掉越权问题,就是因为测试用例永远只用一个账号在跑。
5.5 安全响应头:十分钟能完成的高性价比加固
还有一类“一劳永逸”的小加固,只需要改服务器或Web框架的配置就能立竿见影。建议至少加上以下响应头:
X-Frame-Options: DENY,禁止页面被内嵌到第三方<iframe>中,防点击劫持。X-Content-Type-Options: nosniff,禁止浏览器对响应内容进行MIME类型嗅探,降低内容混淆攻击。Referrer-Policy: no-referrer,控制外链时Referrer信息携带策略,减少信息泄露范围。Permissions-Policy,限制摄像头、麦克风、地理位置等敏感API是否可以被前端调用。
这些响应头配置的性价比极高——改动成本几乎为零,但能让浏览器侧的攻击面缩小一大截。我在给团队做基线检查时,会把“安全响应头缺失”列为中风险项,因为攻击者利用起来成本高,但缺失说明团队整体安全意识的成色。
6. 安全建设的实战工具箱:防御者的日常武器
6.1 信息收集与资产盘点
做防御,第一步也是摸清自己的家底。推荐几个常用工具组合:OneForAll做子域名收集,httpx或tlsx对子域名做存活探测与指纹识别,fofa/quake类平台做互联网资产测绘,gitleaks或trufflehog扫描代码仓库里的敏感信息泄露。周期性地把这些工具跑一遍,产出一份资产清单,标记好系统负责人、开放端口、所用框架、是否接入统一认证等属性。别小看这一步——不了解自己资产的安全建设,约等于闭着眼睛开车。
6.2 漏洞扫描与代码审计
自动化扫描工具只能解决部分问题。AWVS、Xray、Nuclei这类工具适合做基础漏洞扫描,能覆盖SQL注入、XSS、SSRF等常见漏洞类型的自动化验证。但自动化工具对越权、逻辑漏洞基本无能为力,必须配合人工测试和代码审计。
代码审计这条路,静态分析工具如Semgrep、CodeQL、Fortify能帮上不少忙,但同样需要人工确认误报,并沉淀自定义规则。比如你的团队用的框架是某个小众的MVC框架,静态分析器对它的规则覆盖可能非常弱,就需要根据框架的路由规则和输入处理机制,沉淀一套自己的扫描模式。
6.3 监控与应急响应:出现告警之后怎么处理
前面讲再多防御,总有堵不上的口子。所以监控和应急响应的价值就体现在“你即使被突破了,也能快速收拢损失”。
监控层面,Web日志、数据库审计日志、身份认证日志这三大源是核心。Web日志关注异常路径、异常参数(如明显的SQL关键字)、异常状态码分布;数据库审计日志关注大范围查询、慢查询、批量导出的行为;身份认证日志关注异地登陆、暴力破解特征(短时间内大量失败尝试)。将这些日志接入集中管理平台,配置好阈值告警。
应急响应的流程建议固化下来:一确认(确认告警是否真实攻击),二止损(隔离受影响系统、撤销已泄露的凭据、关闭异常账号),三溯源(看日志定位攻击入口和攻击者操作路径),四修复(修补漏洞、清理后门),五复盘(沉淀本次事件的时序表、根因、整改项)。这套流程跑得顺畅不顺畅,全靠平时演练——我见过很多团队第一次做应急响应时手忙脚乱,最后花了十几个小时还没定位到攻击入口。提前写剧本,比临时起意强十倍。
7. 常见问题与踩坑经验实录
7.1 常见高危情况速查表
| 常见问题 | 特征表现 | 排查方向 | 修复要点 |
|---|---|---|---|
| 接口未鉴权 | 直接请求接口URL能返回数据 | 逐个接口测试带/不带登录态访问 | 统一鉴权中间件、默认拒绝、白名单放行 |
| 登录爆破 | 同一IP短时间内大量失败登录 | 查看认证日志,统计失败次数 | 登录接口限流、验证码、账号锁定策略 |
| 敏感数据明文存储 | 数据库里直接存身份证、银行卡号 | 抽查数据库字段内容 | 字段级加密+密钥托管服务 |
| 日志泄露 | 搜索引擎能搜到日志文件名或“登录成功”字样 | 用site语法搜索目标域名 | 日志目录权限收紧、日志脱敏 |
| 接口返回信息过多 | 报错信息中包含SQL语句、堆栈信息、绝对路径 | 触发接口异常观察返回内容 | 全局异常兜底,统一返回脱敏的通用错误信息 |
| 越权访问 | 用A账号能看B账号订单/资料 | 双账号交叉访问测试 | 后端资源归属校验、权限矩阵测试用例 |
| 旧组件漏洞 | 框架或中间件版本多年未更新 | 指纹扫描识别组件版本 | 升级版本或至少将已知漏洞点单独隔离 |
| 文件上传被利用 | 上传目录出现可疑脚本文件 | 扫描目录下所有文件类型 | 上传白名单+目录禁止执行+随机文件名 |
7.2 我踩过的坑和日常习惯
做Web安全这几年,我总结出几条在实操中反复被验证的经验,就当是“避坑清单”分享出来。
第一,别迷信WAF。WAF是防线上非常重要的一环,但它存在绕过可能性,而且对基于业务逻辑的攻击无能为力。曾经有个客户把所有裸奔接口的防护都寄托在WAF上,结果攻击者用一个看似完全合法但业务逻辑拉满的请求流程,绕过了所有规则,硬生生把优惠券系统薅到负债。代码安全永远是第一层防线,WAF只能兜底。
第二,权限模型越简单越可靠。我见过最复杂的权限系统是一个RBAC(基于角色的访问控制)+ABAC(基于属性的访问控制)+数据权限过滤器三层叠加的巨型框架,看起来“强大”,实际上连写权限校验的开发都理不清规则,上线后越权漏洞层出不穷。如果你的团队没有充裕的安全工程资源,优先保证“谁登录、能看谁的数据、能改谁的数据”这三个问题能被明确回答,比花哨的框架更实在。
第三,安全测试要覆盖全流程。很多团队测试时只关心“功能能不能跑”,而忽略“功能被异常使用时的表现”。我在测试接入第三方支付的订单系统时,会多测几件事:下单接口是否可以重复提交,回调参数是否验签,金额参数能否篡改,订单状态能否被逆向流转。每个功能点都从“被恶意使用”的角度问一遍“那又怎样”,你会发现安全测试思路其实并不难,难的是养成这个习惯。
最后一条,及时关注安全和开发社区的新动态。Web安全技术迭代非常快,今日还在用SQL注入打天下的攻击者,明天可能就因为某个开源库的0day换了新招。除了新手入门教材,更重要的是持续跟踪少见漏洞的分析文章和实际案例。
这个领域说到底不是“学完就毕业”的静态学科,而是一场攻防双方不断升级的持久战。我把这些经验写下来,也是希望后面入行的人少走一点弯路——能从容地守住自己的地盘,而不是在事故复盘会上追悔“要是当时多写一个校验就好了”。