学习链接
前端安全系列(一):如何防止XSS攻击?(👍👍👍)
XSS 和 CSRF
XSS 攻击
XSS 攻击是什么
Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击。攻击者通过在网站注入恶意脚本,使之在用户的浏览器上运行,从而盗取用户的信息如 Cookie 等。
攻击者可以通过这种攻击方式可以进行以下操作:
- 获取页面的数据,如 DOM、Cookie、localStorage;
- DOS 攻击,发送合理请求,占用服务器资源,从而使用户无法访问服务器;
- 破坏页面结构;
- 流量劫持(将链接指向某网站);
本质
XSS 的本质是:网站没有对恶意代码进行过滤,与正常的代码混合在一起了,浏览器没有办法分辨哪些脚本是可信的,从而导致恶意脚本被执行。
XSS 攻击类型
类型 | 存储区* | 插入点* |
---|---|---|
存储型 XSS | 后端数据库 | HTML |
反射型 XSS | URL | HTML |
DOM 型 XSS | 后端数据库/前端存储/URL | 前端 JavaScript |
- 存储区:恶意代码存放的位置。
- 插入点:由谁取得恶意代码,并插入到网页上
存储型 XSS 的攻击步骤
- 攻击者将恶意代码提交到目标网站的数据库中。
- 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。
反射型 XSS 的攻击步骤
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。
反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等。
由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。
DOM 型 XSS 的攻击步骤
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL。
- 用户浏览器接收到响应后解析执行,前端 JavaScript 将恶意代码从 URL 中取出并执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。
防御 XSS 攻击
XSS 攻击有两大要素:
- 攻击者提交恶意代码。
- 浏览器执行恶意代码。
针对第一个要素,过滤提交的恶意代码:
- 用户输入时,前端过滤提交后端:不可行,一旦绕过前端过滤即可直接提交恶意代码
- 后端写入数据库之前过滤,把安全内容返回:不确定内容使用的地方,容易乱码
针对第二个要素,防止浏览器执行恶意代码:
预防存储型和反射型 XSS 攻击
存储型和反射型 XSS 都是在服务端取出恶意代码后,插入到响应 HTML 里的,攻击者刻意编写的“数据”被内嵌到“代码”中,被浏览器所执行。
- 改成纯前端渲染,把代码和数据分隔开。
- 对 HTML 做充分转义。
纯前端渲染
使用纯前端渲染的方式,不用服务器端拼接后返回(不使用服务端渲染)。
在纯前端渲染中,我们会明确的告诉浏览器:下面要设置的内容是文本(.innerText
),还是属性(.setAttribute
),还是样式(.style
)等等。浏览器不会被轻易的被欺骗,执行预期外的代码了。
但纯前端渲染还需注意避免 DOM 型 XSS 漏洞(例如 onload
事件和 href
中的 javascript:xxx
等)。
在很多内部、管理系统中,采用纯前端渲染是非常合适的。但对于性能要求高,或有 SEO 需求的页面,我们仍然要面对拼接 HTML 的问题。
转义 HTML
HTML 的编码是十分复杂的,在不同的上下文里要使用相应的转义规则,需要采用合适的转义库,对 HTML 模板各处插入点进行充分的转义。
预防 DOM 型 XSS 攻击
对于 DOM 型的攻击,主要是前端脚本的不可靠而造成的,对于数据获取渲染和字符串拼接的时候应该对可能出现的恶意代码情况进行判断。
DOM 中的内联事件监听器,如 location
、onclick
、onerror
、onload
、onmouseover
等,<a>
标签的 href
属性,JavaScript 的 eval()
、setTimeout()
、setInterval()
等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易产生安全隐患,请务必避免。
其他 XSS 防范措施
- 使用 CSP ,CSP 的本质是建立一个白名单,告诉浏览器哪些外部资源可以加载和执行,从而防止恶意代码的注入攻击。
- CSP 指的是内容安全策略,它的本质是建立一个白名单,告诉浏览器哪些外部资源可以加载和执行。我们只需要配置规则,如何拦截由浏览器自己来实现。
- 通常有两种方式来开启 CSP,一种是设置 HTTP 首部中的 Content-Security-Policy,一种是设置 meta 标签的方式
- 对一些敏感信息进行保护,比如 Cookie 使用 http-only,使得脚本无法获取 Cookie。
- 使用验证码,避免脚本伪装成用户执行一些操作。
CSRF 攻击
CSRF 攻击是什么
CSRF(Cross-site request forgery)跨站请求伪造,攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,冒充用户向服务器执行一些操作。
XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。
CSRF 攻击类型
常见的 CSRF 攻击有三种:
(是通过 HTML 标签进行攻击,故与同源策略无关)
- GET 类型的 CSRF 攻击,比如在网站中的一个 img 标签里构建一个请求,当用户打开这个网站的时候就会自动发起提交。
- POST 类型的 CSRF 攻击,比如构建一个表单,然后隐藏它,当用户进入页面时,自动提交这个表单。
- 链接类型的 CSRF 攻击,比如在 a 标签的 href 属性里构建一个请求,然后诱导用户去点击。
防御 CSRF 攻击
CSRF 通常从第三方网站发起,被攻击的网站无法防止攻击发生,只能通过增强自己网站针对 CSRF 的防护能力来提升安全性。
CSRF 的两个特点:
- CSRF(通常)发生在第三方域名。
- CSRF攻击者不能获取到 Cookie 等信息,只是使用。
针对这两点,我们可以专门制定防护策略,如下:
- 阻止不明外域的访问
- 同源检测
- Samesite Cookie
- 提交时要求附加本域才能获取的信息
- CSRF Token
- 双重 Cookie 验证
进行同源检测
- 服务器根据 http 请求头中 Origin 或者 Referer 信息来判断请求是否为允许访问的站点,从而对请求进行过滤。当 Origin 或者 Referer 信息都不存在的时候,直接阻止请求。
- 这种方式的缺点是有些情况下 Referer 可以被伪造,同时还会把搜索引擎的链接也给屏蔽了。
- 所以一般网站会允许搜索引擎的页面请求,但是相应的页面请求这种请求方式也可能被攻击者给利用。(Referer 字段会告诉服务器该网页是从哪个页面链接过来的)
设置 Samesite Cookie 属性
- 在设置 Cookie 属性的时候设置 Samesite ,限制 Cookie 不能作为被第三方使用,从而可以避免被攻击者利用
Strict
:在严格模式下,Cookie 在任何情况下都不可能作为第三方 Cookie 使用Lax
:在宽松模式下,Cookie 可以被请求是 GET 请求,且会发生页面跳转的请求所使用
用 CSRF Token 进行验证
- 用户打开页面的时候,服务器需要给这个用户生成一个 Token
- 对于 GET 请求,Token 将附在请求地址之后(
query
)。对于 POST 请求,要在 form 的最后加上<input type="hidden" name="csrftoken" value="tokenvalue"/>
- 当用户从客户端得到了 Token,再次提交给服务器的时候,服务器需要判断 Token 的有效性
- 这种方法解决了使用 Cookie 单一验证方式时,可能会被冒用的问题。
- 这种方法存在一个缺点就是,需要给网站中的所有请求都添加上这个 Token,操作比较繁琐。
- 还有一个问题是一般不会只有一台网站服务器,如果请求经过负载平衡转移到了其他的服务器,但是这个服务器的 Session 中没有保留这个 Token 的话,就没有办法验证了。这种情况可以通过改变 Token 的构建方式来解决。
对 Cookie 进行双重验证
- 服务器在用户访问网站页面时,向请求域名注入一个 Cookie,内容为随机字符串
- 当用户再次向服务器发送请求的时候,从 Cookie 中取出这个字符串,添加到 URL 参数中
- 然后服务器通过对 Cookie 中的数据和参数中的数据进行比较,来进行验证
- 使用这种方式是利用了攻击者只能利用 Cookie,但是不能访问获取 Cookie 的特点。并且这种方法比 CSRF Token 的方法更加方便,并且不涉及到分布式访问的问题
- 这种方法的缺点是如果网站存在 XSS 漏洞,那么这种方式会失效。同时这种方式不能做到子域名的隔离
补充
什么是中间人攻击?
中间人(Man-in-the-middle attack,MITM)是指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。在中间人攻击中,攻击者可以拦截通讯双方的通话并插入新的内容。
攻击过程如下:
- 客户端发送请求到服务端,请求被中间人截获
- 服务器向客户端发送公钥
- 中间人截获公钥,保留在自己手上。然后自己生成一个伪造的公钥,发给客户端
- 客户端收到伪造的公钥后,生成加密 hash 值发给服务器
- 中间人获得加密hash值,用自己的私钥解密获得真秘钥,同时生成假的加密hash值,发给服务器
- 服务器用私钥解密获得假密钥,然后加密数据传输给客户端
网络劫持有哪几种,如何防范?
网络劫持分为两种:
- DNS 劫持:(输入京东被强制跳转到淘宝这就属于DNS劫特)
- DNS 强制解析:通过修改运营商的本地 DNS 记录,来引导用户流量到缓存服务器
- 302 跳转的方式:通过监控网络出口的流量,分析判断哪些内容是可以进行劫持处理的,再对劫持的内存发起 302 跳转的回复,引导用户获取内容
- HTTP 劫特:(访问谷歌但是一直有贪玩蓝月的广告)
- 由于 HTTP 明文传输,运营商可用修改用户的 HTTP 响应内容(即加广告)
DNS劫持由于涉嫌违法,已经被监管起来,现在很少会有DNS劫持。
而 HTTP 劫持依然非常盛行,最有效的办法就是全站 HTTPS,将 HTTP 加密,这使得运营商无法获取明文,就无法劫持你的响应内容。