Web 安全是互联网中不可或缺的一个领域,这个领域中诞生了大量的黑帽子与白帽子,他们都是安全领域的王者,在平时里,他们利用各种巧妙的技术互相博弈,时不时就会掀起一场 Web 安全浪潮,真可谓神仙打架,各显神通。
本文从一个吃瓜群众的角度,聊一聊 Web 安全的一些有趣故事。
安全世界观
安全攻防案例
总结与思考
在互联网发展之初,IE 浏览器垄断的时期,大家上网的目的都很单纯,主要通过浏览器分享信息,获取新闻。但随着互联网的不断发展发展,一个网页能做的事情越来越多,除了看新闻,我们还可以看视频、玩游戏、购物、聊天等,这些功能都大大丰富了我们的生活。
随着网页功能的逐渐增多,就开始出现了一些黑帽子,他们试图通过一些技术手段来牟取利益。在我小的时候,印象最深的就是木马病毒,它可以监控你的键盘,将你在键盘上敲打的内容发送到黑客的机器上,黑客通过分析这些内容,很容易就能得到你的游戏账号和密码。
在这之后,就诞生出了一些杀毒软件,致力于解决网络上的各种病毒,随着不断地发展,杀毒软件已经成为一台电脑必不可少的软件。
为什么会出现这样的安全问题?
安全归根到底是信任的问题,如果所有人都按照正常的流程去上网,不去谋取私利,也就没有安全问题可谈了。
安全的根本在于信任,但要让所有人互相信任谈何容易。在当前阶段,我们可以做到:持续做好安全防护,让漏洞越来越少,非法攻击越来越困难,这样就能逐渐减少黑帽子的数量,让病毒制造者越来越少。
要做好安全,首先得理解安全问题的属性,前人通过无数实践,最后将安全的属性总结为安全三要素,分别为:机密性、完整性、可用性。
机密性
保护数据内容不被泄露。
通常使用加密的方法。
完整性
保护数据内容是完整的、没有被篡改。
通常使用数字签名的方法。
可用性
数据随时都能够使用。
通常是在防御 DOS。
有了安全 3 要素之后,我们就可以对安全问题进行评估了。
资产等级划分
找出最重要的数据。
找出最重要数据的宿主空间,如:在数据库里,那么数据库就得重点防御。
找出数据库的宿主空间,如:在一台服务器上,那么这台服务器就得做次等防御。
找出服务器的宿主空间,如:在 OSI 网络层级上,那么在网络层面就得做一般防御。
威胁分析
找出威胁(可能造成危害的来源)。
找出风险(可能出现的损失叫做风险)。
风险分析
采取多标准决策分析,即:风险 = 威胁等级 * 威胁可行性。
计算所有的威胁,将最终的风险进行排序,优先解决风险大的问题。
确认解决方案
找出不安全的实现方式,并确定解决方案。
解决方案不要改变商业需求的初衷。
解决方案需对用户透明,不要改变用户的习惯。
做好安全评估之后,我们就有了一份安全解决方案,后续的安全工作只需按照这个方案去做,就没有任何问题。
有了安全解决方案之后,我们还可以制定一些安全原则,遵守原则做事,可以让我们事半功倍。
黑名单、白名单原则
白名单方案指的是给安全的资源授权。
黑名单方案指的是禁用不安全的资源。
我们应该优先使用白名单方案,因为黑名单通常统计不完所有的不安全资源。
如:XSS 攻击的方式非常多,可以通过 script、css、image 标签等,尽管你将这些标签都加入黑名单,也不能保证其他的标签都没有 XSS 的攻击隐患。
最小权限原则
只授予必要的权限,不要过度授权,减少出错机会。
如:普通权限的 Linux 用户只能操作 ~
文件夹下的目录,如果有人想删库跑路,在执行 rm -rf /
时,就会提示无权限。
纵深防御原则
这条原则类似 木桶理论
,安全水平往往取决于最短的那块板。
即:不要留下短板,黑帽子们往往可以利用短板为突破口,挖掘更大的漏洞。
数据与代码分离原则
当用户数据被当成代码执行时,混淆了数据和代码的边界,从而导致安全问题。
如:XSS 就是利用这一点去攻击的。
不可预测性原则
这条原则是为了提高攻击门槛,有效防止基于篡改、伪造的攻击。
如:数据库中使用 uuid 代替 number 型的自增主键,可以避免 id 被攻击者猜到,从而进行批量操作。
token 也是利用不可预测性,攻击者无法构造 token 也就无法进行攻击。
有了这些安全原则,我们就可以开干了,接下来介绍几个常见的攻防案例。
安全攻防的案例非常多,这里主要介绍几个出镜率比较高的安全问题。
XSS 攻击
CSRF 攻击
点击劫持
XSS 攻击的本质是将用户数据当成了 HTML 代码一部分来执行,从而混淆原本的语义,产生新的语义。
如图所示,我们注册了一个 的用户名,所有能看到此用户名字的页面,都会弹出当前浏览器的 Cookie,如果代码的逻辑是将 Cookie 发送到攻击者的网站,攻击者就能冒充当前用户进行登录了。
XSS 攻击方式有很多,所有和用户交互的地方,都有可能存在 XSS 攻击。
例如:
所有 input 框。
window.location。
window.name。
document.referrer。
document.cookie。
localstorage。
...
由于页面中与用户交互的地方非常多,肯定还有一些 XSS 的攻击方式没有被发现,而一旦被黑帽子发现,就可能造成严重的影响,所以我们务必引起重视。
被 XSS 攻击成功后,攻击者就可以获取大量的用户信息,例如:
识别用户 UA。
识别用户浏览器扩展。
识别用户浏览过的网站。
通过 CSS 的 Visited 属性。
获取用户真实的 IP。
通过 WebRTC 等。
盗取 Cookie
伪造用户登录,窃取用户资料。
XSS 钓鱼。
向页面注入一个登录弹窗,让用户认为是网站内的登录弹窗(其实是钓鱼网站的),一旦用户登录,账号密码就泄露给了钓鱼网站。
目前来说,XSS 已经得到了互联网行业的重视,许多开发框架都内置了安全的 HTML 渲染方法。
我们也可以自定义进行一些安全配置。
配置 HTTP 中的 http-only 头,让前端 JS 不能操作 Cookie。
输入检查,在用户提交数据时,使用 XssFilter 过滤掉不安全的数据。
输出检查,在页面渲染的时候,过滤掉危险的数据。
CSRF(Cross-site request forgery)跨站请求伪造,是一种利用用户身份,执行一些用户非本意的操作。
如图所示:
用户先登录了服务器 B,然后去访问服务器 C。
服务器 C 通过恶意脚本,冒充 A 去调用服务器 B 上的某个功能,
对于服务器 B 来说,还以为这是 A 发起的请求,就当作正常请求处理了。
试想一下,如果 C 冒充 A 进行了一次转账,必定会造成大量的经济损失。
防御 CSRF 主要有以下几种方式:
验证码
每一次请求都要求用户验证,以确保请求真实可靠。
即:利用恶意脚本不能识别复杂的验证码的特点,保证每次请求都是合法的。
Referer 检查
检查发起请求的服务器,是否为目标服务器。
即:HTTP 请求中的 Referer 头传递了当前请求的域名,如果此域名是非法服务器的域名,则需要禁止访问。
Token
利用不可预测性原则,每一请求必须带上一段随机码,这段随机码由正常用户保存,黑帽子不知道随机码,也就无法冒充用户进行请求了。
点击劫持是一种视觉欺骗的攻击手段。攻击者将需要攻击的网站通过 iframe 嵌套的方式嵌入自己的网页中,并将 iframe 设置为透明,在页面中透出一个按钮诱导用户点击。
就像一张图片上面铺了一层透明的纸一样,你看到的是攻击者的页面,但是其实这个页面只是在底部,而你真正点击的是被攻击者透明化的另一个网页。
如果所示,当你点击了页面上的按钮之后,本以为会...... ,而真正执行的操作是关注了某人的博客。
由于点击劫持主要通过 iframe,所以在防御时,主要基于 iframe 去做。
方案一:frame busting
if (self !== top) {
// 跳回原页面
top.location = self.location;
}
正常网站使用 JS 脚本判断是否被恶意网站嵌入,如:博客网站监测到被一个 iframe 打开,自动跳转到正常的页面即可。
方案二:使用 HTTP 中的 x-frame-options 头,控制 iframe 的加载,它有 3 个值可选:
DENY,表示页面不允许通过 iframe 的方式展示。
SAMEORIGIN,表示页面可以在相同域名下通过 iframe 的方式展示。
ALLOW-FROM,表示页面可以在指定来源的 iframe 中展示。
配置 iframe 的 sandbox 属性
sandbox = "allow-same-origin" 则只能加载与主站同域的资源。
服务器端的攻击的方式也非常多,这里列举几个常见的。
SQL 注入攻击
文件上传漏洞
登录认证攻击
应用层拒绝服务攻击
webServer 配置安全
SQL 注入和 XSS 一样,都是违背了数据和代码分离原则导致的攻击方式。
如图所示,我们利用 SQL 注入,就能在不需要密码的情况下,直接登录管理员的账号。
攻击的前提是:后端只用了简单的拼接 SQL 的方式去查询数据。
# 拼接出来的 sql 如下:
select * from user where username = 'admin' or 1=1 and password = 'xxx'
# 无论密码输入什么,这条 sql 语句都能查询到管理员的信息
除此之外,SQL 注入还有以下几种方式:
使用 SQL 探测,猜数据库表名,列名。
通过 MySQL 内置的 benchmark 探测数据库字段。
如:一段伪代码 select database as current if current[0]==='a',benchmark(10000,'猜对了')
如果表明猜对了,就延迟 10 s 并返回成功。
使用存储过程执行系统命令
通过内置的方法或存储过程执行 shell 脚本。
如:xp_cmdshell、sys_eval、sys_exec 等。
字符串截断
如:MySQL 在处理超长的字符串时,会显示警告,但会执行成功。
注册一个 admin + 50 个空格的用户,会触发截断,最终新增一个 admin 用户,这样就能拥有管理员权限了。
防止 SQL 注入的最好的办法就是,不要手动拼接 SQL 语句。
最佳方案,使用预编译语句绑定变量
通常是指框架提供的拼接 SQL 变量的方法。
这样的语义不会发生改变,变量始终被当成变量。
严格限制数据类型,如果注入了其他类型的数据,直接报错,不允许执行。
使用安全的存储过程和系统函数。
在注入攻击中,换行符注入也是非常常见的一种攻击方式。
如果在 HTTP 请求头中注入 2 个换行符,会导致换行符后面的所有内容都被解析成请求实体部分。
攻击者通常在 Set-Cookie 时,注入换行符,控制请求传递的内容。
上传文件是网页开发中的一个常见功能,如果不加处理,很容易就会造成攻击。
如图所示,攻击者上传了一个木马文件,并且通过返回的 URL 进行访问,就能控制服务器。
通常我们会控制上传文件的后缀名,但也不能完全解决问题,攻击者还可以通过以下方式进行攻击:
伪造正常文件
将木马文件伪装成正常的后缀名进行上传。
如果要避免这个问题,我们可以继续判断上传文件的文件头前 10 个字节。
Apache 解析方式是从后往前解析,直到找到一个认识的后缀名为止
如:上传一个 abc.php.rar.rar.rar
能绕过后缀名检查,但在执行时,被当成一个 php 文件进行执行。
IIS 会截断分号进行解析
如:abc.asp;xx.png
能绕过后缀名检查,但在执行时,被当成一个 asp 文件进行执行。
HTTP PUT 方法允许将文件上传到指定位置
通过 HTTP MOVE 方法,还能修改上传的文件名。
通过二者配合,就能先上传一个正常的后缀名,然后改为一个恶意的后缀名。
PHP CGI 路径问题
执行 http://abc.com/test.png/xxx.php
时,会把 test.png 当做 php 文件去解析。
如果用户正好是把一段恶意的 php 脚本当做一张图片进行上传,就会触发这个攻击。
防御文件上传漏洞,可以从以下几点考虑:
将文件上传的目录设置为不可执行。
判断文件类型
检查 MIME Type,配置白名单。
检查后缀名,配置白名单。
使用随机数改写文件名和文件路径
上传文件后,随机修改文件名,让攻击者无法执行攻击。
单独设置文件服务器的域名
单独做一个文件服务器,并使用单独的域名,利用同源策略,规避客户端攻击。
通常做法是将静态资源存放在 CDN 上。
登录认证攻击可以理解为一种破解登录的方法。攻击者通常采用以下几种方式进行破解:
彩虹表
攻击者通过搜集大量明文和 MD5 的对应关系,用于破解 MD5 密文找出原文。
对于彩虹表中的 MD5 密码,我们可以加盐,进行二次加密,避免被破解。
Session Fixation 攻击
利用应用系统在服务器的 SessionID 固定不变机制,借助他人用相同的 SessionID 获取认证和授权。
攻击者登录失败后,后端返回了 SessionID,攻击者将 SessionID 交给正常用户去登录,登录成功后,攻击者就能使用这个 SessionID 冒充正常用户登录了。
如果浏览器每一次登录都刷新 SessionID 可以避免这个问题。
Session 保持攻击
有些时候,后端出于用户体验考虑,只要这个用户还活着,就不会让这个用户的 Session 失效。
攻击者可以通过不停发起请求,可以让这个 Session 一直活下去。
多因素认证
密码作为第一道防御,但在密码验证成功后,我们还可以继续验证:动态口令,数字证书,短信验证码等,以保证用户安全。
由于短信和网页完全是 2 套独立的系统,攻击者很难获取到短信验证码,也就无法进行攻击。
除此之外,前端登录认证还有多种方式,如果你对此感兴趣,可以参考我之前写的《前端登录,这一篇就够了》。
应用层拒绝服务攻击,又叫 DDOS 攻击,它指的是利用大量的请求造成资源过载,导致服务器不可用。
通常有以下几种 DDOS 攻击方式:
SYN Flood 洪水攻击
利用 HTTP 3 次握手机制,消耗服务器连接资源。
如:攻击者发起大量的 HTTP 请求,但并不完成 3 次握手,而是只握手 2 次,这时服务器端会继续等待直至超时。这时的服务器会一直忙于处理大量的垃圾请求,而无暇顾及正常请求。
Slowloris 攻击
以非常低的速度发送 HTTP 请求头,消耗服务器连接资源。
如:攻击者发送大量 HTTP 请求,但每个请求头都发的很慢,每隔 10s 发送一个字符,服务器为了等待数据,不得始终保持连接,这样一来,服务器连接数很快就被占光了。
HTTP POST DOS
发送 HTTP 时,指定一个非常大的 Content-Length 然后以很长的间隔发送,消耗服务器连接资源。
CC 攻击
针对一些非常消耗资源的页面,不断发起请求。
如:页面中的某些页面,需要后端做大量的运算,或者需要做非常耗时的数据库查询。在大量的请求下,服务器的 CPU、内存等资源可能就被占光了。
Server Limit DOS
通过 XSS 注入一段超长的 Cookie,导致超出 Web 服务器所能承受的 Request Header 长度,服务器端就会拒绝此服务。
ReDOS
针对一些缺陷的正则表达式,发起大量请求,耗光系统资源。
对于应用层拒绝服务攻击,目前也没有特别完美的解决方案,不过我们还是可以进行一些优化。
应用代码做好性能优化
合理使用 Redis、Memcache 等缓存方案,减少 CPU 资源使用率。
网络架构上做好优化
后端搭建负载均衡。
静态资源使用 CDN 进行管理。
限制请求频率
服务器计算所有 IP 地址的请求频率,筛选出异常的 IP 进行禁用。
可以使用 LRU 算法,缓存前 1000 条请求的 IP,如果有 IP 请求频率过高,就进行禁用。
其实,处理 DDOS 核心思路就是禁用不可信任的用户,确保资源都是被正常的用户所使用。
我们在部署 web 应用的时候,经常会用到 Nginx、Apache、IIS、Tomcat、Jboss 等 Web 服务器,这些服务器本身也存在一些安全隐患,如果配置不当,很容易收到攻击。
在配置 Web 服务器时,可以参考以下几点:
以用户权限运行 Web 服务器
遵守最小权限原则,以最小权限身份运行 Web 服务器,限制被入侵后的权限。
删除可视化后台
运行 Tomcat、Jboss 等 Web 服务器时,默认会开启一个可视化的运营后台,运行在 8080 端口,并且第一次访问是没有认证的。
攻击者可以利用可视化后台,远程加载一段 war 包或者上传木马文件,进行控制。
及时更新版本
主流的 Web 服务器,每隔一段时间就会修复一些漏洞,所以记得及时更新版本。
本文介绍了 Web 安全的基本概念,以及大量的攻防技巧,其实这只是 Web 安全中的冰山一角,如果你对此感兴趣,不妨在安全领域继续深耕学习,一定能看到更广阔一片天。
对于一个开发者来说,我们应该在写代码时就将安全考虑其中,形成自己的一套安全开发体系,做到心中有安全,时时考虑安全,就能无形之中化解不法分子的攻击。
最后
1.看到这里了就点个在看支持下吧,你的「在看」是我创作的动力。
2.关注公众号程序员成长指北
,「带你一起学Node」!
3.添加微信【ikoala520】,拉你进技术交流群一起学习。
你点的每个在看,我都认真当成了喜欢