动态网站的各种脆弱面,是很多教你如何编程的书中不会涉及的,这些编程类书籍中,通常只会教你一些最基本的 CRUD,及有限的性能优化。一些好的框架中会有一些这方面的考虑,但是如果你对各种攻击没有概念,那么你其实也很难把框架中的防攻击功能用好。
举个例子,如果登录页面上对尝试登录的次数没有限制的话,那么攻击者就可以不停地尝试,直到发现一个正确的密码。而如果用户用了弱密码,那么或许这个密码很快就可以被尝试出来。
而另外一个例子是,如果所有的 POST 请求,不去检测发起者究竟是真实用户还是机器,那么网站很可能瞬间就被各种用程序发出的修空调或是网络赚钱的垃圾给填满了。
又比如,如果一个新账号,在注册完毕后,马上就开始发布大量重复的内容,那么只会有一种可能——Spammer。
这种事情如果经常发生,那么用户就会对这个网站逐渐失去兴趣。
所以,一种最基础的保护方式就是,对于所有的涉及数据写入的页面,比如所有的 POST/PUT 请求,都应该有频次限制机制。如果是针对 IP 来做这样的限制的话,可以用 Redis 来实现计数。用 timestamp + IP + 页面的 URI 来作为 key,每有一次访问 value 就 +1,如果在特定的 timestamp 内访问的次数超过限制,就可以禁止这个 IP 在接下来一段时间的后续访问。限制访问的机制同样可以借助 Redis,比如在所有的请求的构造函数中,检查 deny:1.2.3.4 这个 key 是否存在,如果存在,就拒绝 1.2.3.4 的访问。因为 Redis 可以为每个 key 设置 TTL,所以如果你为 deny:1.2.3.4 设置了 3600 的 TTL,那么也就可以实现在 1 个小时内拒绝这个 IP 对于站点内任何动态页面的访问。
这样的保护方式也可以适用于 API 的频次限制,或是保护一些生成代价比较大的页面不被 flood。
上面讨论的是应用层面的攻击,而另外一类攻击就是协议层面的攻击,比如 UDP Flood 和 SYN Flood 之类。对于这类攻击,如果只靠调整 iptables 和内核的一些参数,基本上是无济于事的。应对这类攻击你需要的是机房的基础设施。如果机房本身有比如 Juniper 或是华为的防火墙设备,那么像 SYN Flood 这样的最基础的攻击方式,应该是机房层面就可以自动帮你过滤掉的。只是可惜的是,很多 VPS 和 Colo 提供商在机房里只放了一些最基础的网络设备,当这类攻击到来时,他们能做的就是停服务或者拔网线。
把网站放到 CDN 背后也是一个办法,这样当攻击来时,首先遇到的是 CDN 的服务器。不同的 CDN 厂商在防攻击这件事情上能做到的程度不同,如果预算充足的话可以考虑 Akamai 的 KONA,如果没有预算的话,就用 Incapsula 吧。
CloudFlare 也是一个经常被谈论起的方案,但是这个方案要求你将整个域名的管理,即 NS 也托管到他们。但是如果你需要的某种 DNS 功能 CloudFlare 不提供怎么办?这就是这个方案的纠结之处。
说到 DNS,一个确实值得推荐的方案就是 DynECT Managed DNS。Dyn 建了一个由全世界接近 20 个数据中心构成的 Anycast 网络,这样可以保证你的域名在全世界大部分地区的初次解析耗时都在 100ms 以下,甚至 40ms 以下。同时,如果有人想攻击你的 NS 的话,Dyn 的 NS 被打下的难度也是比较大的。