当前位置:首页 > 焦点

WEB防护

去年冬天,一个做跨境电商的朋友凌晨两点给我打电话。他的网站被灌了12万条垃圾注册,数据库连接数爆满,前台直接502。攻击者没用什么高深漏洞,就是拿脚本对着注册接口狂刷。他问我:“我加了验证码啊,怎么还能刷?”我让他把验证码图片地址贴出来,一看——/captcha?ts=123456,时间戳参数直接暴露了生成规律,脚本把时间戳往前推几秒就能复用同一张验证码。这事让我意识到,很多团队对WEB防护的理解还停留在“装个WAF就完事”的阶段。

WAF确实有用,但它更像小区门口的保安,能拦住大部分翻墙进来的,拦不住伪装成业主的。SQL注入、XSS这些老牌攻击手法,现在依然活跃,只是换了包装。比如XSS,早些年大家盯着<script>标签过滤,现在攻击者用<img src=x onerror=alert(1)>,甚至把payload拆成多个参数在服务端拼接。我见过一个案例,某后台的搜索框对单引号做了转义,但排序参数orderBy直接拼进SQL,攻击者用orderBy=id,(select/**/case/**/when/**/(select/**/substr(password,1,1)/**/from/**/users/**/limit/**/1)=0x61/**/then/**/1/**/else/**/1/0/**/end),硬是把密码逐位猜了出来。这种盲注靠WAF规则很难覆盖,得在代码层做参数化查询。

防护得从输入和输出两头掐。输入侧,白名单比黑名单靠谱。比如用户ID参数,只允许数字,正则^\d+$直接卡死,别去猜攻击者会传什么。输出侧,HTML实体编码是底线,但要注意上下文——放进href里的URL得用urlencode,塞进JS变量的得用json_encode,混用等于没做。有个细节:很多框架的模板引擎默认转义,但开发者为了“灵活”手动关了,比如Django模板里的|safe过滤器,用之前得想清楚数据来源可不可控。

会话管理是另一个重灾区。我扫过一批中小型网站,超过三成的Cookie没设HttpOnly和Secure。这意味着一旦有XSS,攻击者直接document.cookie就能拿走会话ID。SameSite属性也常被忽略,设成Lax能挡掉大部分CSRF,但有些老系统还在用Referer校验,而Referer可以被meta标签伪造。更稳妥的做法是给每个敏感操作加CSRF Token,并且Token绑定用户会话,别用全局固定值。

文件上传的坑我踩过最深的一次。某项目允许上传头像,校验了MIME类型和扩展名,但没校验文件内容。攻击者传了个.jpg,里面嵌了PHP代码,然后利用本地文件包含漏洞去解析。后来我们改成:上传后重命名成随机字符串,存到Web根目录之外,用单独的域名提供访问,并且强制Content-Disposition: attachment。这样即使传了脚本,也执行不了。

日志和监控不能省。攻击者从扫描到利用通常有间隔,如果能在access.log里发现大量404集中在/.env、/wp-admin这类路径,就该触发告警。我习惯在Nginx层加个limit_req,对/login和/api做速率限制,单IP每分钟超过20次就返回429。别小看这个,能挡掉八成以上的暴力破解和CC攻击。

最后说个反直觉的:别过度依赖云WAF的“自动防护”模式。有些厂商默认开启宽松策略,把union select这种明显特征都放过了,因为怕误杀正常业务。我一般建议先开观察模式跑一周,看日志里哪些规则被触发,再逐步调成拦截。防护是个持续调参的活,没有一劳永逸的银弹。

分享到: