XSS攻击
什么是XSS
Cross-Site Scripting (跨站脚本攻击)简称XSS,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如Cookie, SessionID等,今儿危害数据安全。
恶意脚本的注入方法:
1. 在HTML中内联的文本中,恶意内容以script标签形成注入。
2. 恶意内容包含引号,突破属性值的限制(字符串等),注入其他属性或者标签。
3. 在标签的href, src 等属性中,包含 javascript: (伪协议)等可执行代码。
4. 在onload,onerror,onclick 等事件中,注入不受控制代码。(因为字符串可当作代码运行)
XSS攻击的分类
存储型,反射型,DOM型。
存储型XSS
攻击步骤:
1. 攻击者将恶意代码提交到目标网站的数据库中。
2. 用户打开目标网站时,网站服务端将恶意代码从数据库中取出,拼接在HTML中返回给浏览器。
3. 用户浏览器收到响应后解析执行,混在其中的恶意代码也被执行。
4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
任何允许用户存储数据的web程序 (如论坛发帖、商品评论)都可能存在存储型XSS漏洞。当攻击者提交一段XSS代码后,被服务器端接收并存储,当所有浏览者访问某个页面时都会被XSS。它不需要被用户手动触发。
反射型XSS
攻击步骤:
1. 攻击者构造出特殊的URL,其中包含恶意代码。
2. 用户打开带有恶意代码的URL时,网站服务端将恶意代码从URL中取出,拼接在HTML中返回给浏览器。
3. 浏览器收到响应后解析执行恶意代码。
4. 恶意代码窃取用户数据或冒充用户执行操作。
反射型XSS的恶意代码存在URL里,存储型XSS的恶意代码存在数据库里。
反射型XSS常见与通过URL传递参数的功能,如网站搜索、跳转等。
需要用户主动打开恶意URL才生效,攻击者会诱导用户点击。
DOM型XSS
攻击步骤:
1. 攻击者构造出特殊的URL,其中包含恶意代码。
2. 用户打开带有恶意代码的URL。
3. 用户浏览器接收响应后解析执行,前端JavaScript取出URL中的恶意代码并执行。
4. 恶意代码窃取用户数据或冒充用户执行操作。
DOM型XSS中,取出和执行恶意代码都有浏览器端完成,属于前端JavaScript自身的安全漏洞。而存储型XSS和反射型XSS都属于服务端的安全漏洞。
防御
XSS攻击有两大要素:
1. 攻击者提交恶意代码
2. 浏览器执行恶意代码
我们将针对这两大要素,探讨XSS攻击的预防。
针对第一个要素,我们希望在用户输入的过程,过滤掉用户输入的恶意代码。
输入过滤
对用户的输入进行转义,把潜在的恶意脚本转义掉。但有可能用户正常输入的内容被转义成了乱码(5<7 => 5 < 7)。因此输入过滤并不完全可靠。
但对于明确的输入类型,例如数字、URL、电话号码、邮件地址等内容,还是有必要进行输入过滤。
既然输入过滤不可靠,我们就要通过“防止浏览器执行恶意代码”来防范 XSS。这部分分为两类:
- 防止HTML中出现注入(预防存储型和反射型XSS)
- 防止前端JavaScript执行时,执行恶意代码(预防DOM型XSS)
预防存储型和反射型XSS
这两种XSS都是服务端取出恶意代码后,插入响应HTML里的。
有两种常见做法预防这种漏洞:
- 改成纯前端渲染,把代码和数据分隔开
- 对HTML做充分转义
纯前端渲染
过程:
- 浏览器先加载一个静态HTML
- 然后浏览器执行HTML中的JavaScript
- JavaScript通过Ajax加载业务数据,调用DOM API更新到页面上
这样的话,响应HTML就不会拼接到页面上。
转义HTML
如果真的有必要拼接HTML,就要采用合适的转义库,对HTML模板各插入点进行充分的转义。
把 & < > " ' / 这几个字符转义掉,插入HTML中的恶意脚本就变得无效了。
预防DOM型XSS攻击
要防止前端JavaScript把不可信的数据当作代码执行。 小心使用 .innerHTML .outerHTML dociment.write(),防止执行不可信的数据 如果用Vue/React,就要在render阶段避免 .innerHTML .outerHTML带来的隐患 DOM中的内联事件监听器,如onclick, onload, onmouseover,标签的 href 属性,JavaScript的 setTimeout(), setInterval() 等,都能把字符串作为代码运行。使用.addEventListener() 更安全
<h1 onclick="alert("hello");">Click me</h1>
如果不可信的数据( ">)拼接到字符串中传递给这些API,很容易产生安全隐患。所以一定要小心使用。
其他防范措施
输入内容长度控制:对于不受信任的输入,都应该限定一个合理的长度。虽然无法防止XSS发生,但可以增加XSS攻击的难度。
HTTP-only Cookie: 禁止JavaScript读取某些敏感Cookie,攻击者完成XSS注入后也无法窃取此Cookie。
验证码:防止脚本冒充用户提交危险操作。
CSP
网页安全政策( Content Security Policy,缩写CSP)是一种防止XSS攻击的白名单制度。由开发者明确告诉客户端,哪些外部资源可以加载和执行。
由两种方法可以启用 CSP。一种是通过HTTP响应头信息的 Content-Security-Policy 的字段。另一种是通过网页的标签。
我们可以在CSP中添加多种限制来防范XSS。比如,
- 禁止加载外域代码
- 禁止外域提交。网站被攻击后,用户的数据不会泄露到外域。
- 禁止内联脚本执行
- 禁止未授权的脚本执行
CSRF
什么是CSRF
CSRF(Cross-site request forgery) 跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
CSRF的特点
- 攻击一般发起在第三方网站,而不是被攻击的网站。
- 攻击者利用受害者在被攻击网站的登陆凭证,冒充受害者提交操作。整个过程攻击者并不能获取到受害者的登陆凭证,仅仅是“冒用”。
- 跨站请求可以用多种方式:图片URL,超链接,CORS,Form提交等等。部分请求方式可以直接嵌入在第三方论坛、文章中。
防护策略
针对CSRF的两个特点:
- CSRF通常发生在第三方域名
- CSRF攻击者不能获取到Cookie等信息,只是使用
我们可以指定如下防护策略:
- 阻止不明外域的访问(使用Referer Header确定源域名)
- 提交时要求附加本域才能获得的信息(在HTTP请求中添加token验证)
验证 HTTP Referer 字段
在HTTP请求头中有一个字段叫Referer,它记录了该HTTP请求的来源地址。服务器可以通过解析这个字段中的域名,确定请求的来源域。如果结果显示请求来自第三方域名,则拒绝该请求;如果是来自本域,则认为是安全的。
缺点:
如果攻击者有权在本域发布评论(含连接,图片等),那么它可以直接在本域发起攻击。这样的同源策略无法达到保护的作用。
在HTTP请求中添加token验证
我们知道,CSRF攻击者无法直接窃取到用户的信息,仅仅是冒用。
那么我们可以要求用户都携带一个CSRF攻击者无法获取到的Token。服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击。
Token可以在产生后存放于Session之中 ,然后每次请求时把Token从Session中拿出 ,与请求中的Token进行比对。
缺点:
1. 如果网站同时存在XSS,可以让攻击者拿到token,这个办法也没用了
2. 比较复杂。需要给每一个页面都写入Token,每一个form及Ajax请求都携带这个Token,后端对每一个接口进行校验,不能统一拦截处理。工作量巨大且容易遗漏。
其他防御策略
- 验证码:强制用户必须与应用进行交互,才能完成最终请求。
- 双重Cookie验证:利用CSRF攻击不能获取到用户Cookie的特点,要求表单请求和Ajax携带一个Cookie中的值。
- SameSite Cookie:为 Set-Cookie响应头新增Samesite属性,标明这是个同站Cookie,只能作为第一方Cookie,不能作为第三方Cookie。可以设置Samesite=Strict,浏览器在任何跨域请求都不会携带Cookie;或Samesite=Lax,其他网站通过页面跳转过来可以使用Cookie。