做报名链接的网站,军事新闻国际军事新闻,线上推广的方法,国外网站风格前言
前端关于网络安全看似高深莫测#xff0c;其实来来回回就那么点东西#xff0c;我总结一下就是 3 1 4#xff0c;3个用字母描述的【分别是 XSS、CSRF、CORS】 一个中间人攻击。当然 CORS 同源策略是为了防止攻击的安全策略#xff0c;其他的都是网络攻击。除了这…前言
前端关于网络安全看似高深莫测其实来来回回就那么点东西我总结一下就是 3 1 43个用字母描述的【分别是 XSS、CSRF、CORS】 一个中间人攻击。当然 CORS 同源策略是为了防止攻击的安全策略其他的都是网络攻击。除了这 4 个前端相关的面试题其他的都是一些不常用的小喽啰。
我将会在我的《面试题一网打尽》专栏中先逐一详细介绍然后再来一篇文章总结预计一共5篇文章欢迎大家关注
本篇文章是前端网络安全相关的第一篇文章内容就是 CSRF 攻击。
一、准备工作
CSRFcross-site request forgery跨站请求伪造是利用 iframe、img、a 、link、form等标签src、href 等不存在跨域问题的特点。CSRF 攻击的本质是利用 cookie 会在同源同站请求中携带发送给服务器的特点以此来实现用户的冒充伪造用户的请求。
1.1 拉取仓库
所以本篇文章的基础是需要一个服务端的项目可以跟着我的这篇文章搭建自己的服务端项目。或者直接克隆我的仓库代码在这个提交上拉一个新分支本篇文章所有的代码都是在这个提交基础上进行的。 在本篇文章之前我已经写了xss 攻击的文章所以在你拉取我的 git 最新代码的时候已经有很多更新的提交了不过无论是从上面我说的那个提交拉取新分支还是拉取最新的代码都可以我的仓库的所有的合并都是相互独立的。 不论你先看 XSS 教程还是先看 CSRF 教程都可以。 1.2 新增 CSRF 文件夹 二、攻击条件
目标站点一定要有 CSRF 漏洞用户要登录过目标站点并且在浏览器上保持有该站点的登录状态需要用户打开一个第三方站点可以是黑客的站点也可以是一些论坛
所以说这个攻击有两个网站
三、攻击类型
3.1 get 攻击
利用 iframe、img、a 等标签发起 get 请求时不存在跨域的问题利用 cookie 发起跨站请求注意是跨站不是跨域【协议域名端口号都相同才不跨域、有效顶级域名 1 相同才不跨站】
在当前访问的网站和请求服务的网站跨站的情况下第三方服务设置的 cookie 就称之为第三方 cookie比如当前网站A有一个 iframe 地址为 BB 请求服务B 的 cookie 对于网站 A 就是第三方 cookie跨站的概念和 cookie 的 domain 属性设置是差不多的
还可以利用 a 标签的 href 属性这个本质上也是 get 请求的攻击。 跨站和跨域的关系与区别 跨站 not samesite两个域名不属于同站域名-主机名/IP相同协议相同。跨域两个域名不属于同源域名-主机名/IP相同端口号相同协议相同。 Cookie 的同源主要用于防止 CRSFCookie 的校验较宽松Cookie 只关注域名忽略协议和端口只要两个 URL 的 eTLD1 相同即可【顶级域名1】eTLD 表示有效顶级域名注册于 Mozilla 维护的公共后缀列表Public Suffix List中例如.com、.co、.uk、.github.io 等。而 eTLD1 则表示有效顶级域名二级域名例如 taobao.com 等。 www.taobao.com 和 www.baidu.com 是跨站www.a.taobao.com 和 www.b.taobao.com 是同站a.github.io和b.github.io是跨站 所以跨站即两个 URL 顶级域名和二级域名不同就是跨站也称第三方Third-party相同则是同站本方First-party 跨站就一定跨域 跨域不一定跨站 3.2 post 攻击
构建一个表单 html 中的 form actionxxx methodget/post/form隐藏它当用户进入页面时自动提交这个表单一个 form 表单可以实现 post 请求或者get 请求 好久不用原生的 form 表单了都忘了怎么用了在提交 form 表单之后页面会自动跳转想要阻止自动跳转就不能用 form 自带的 submit 功能了需要手动使用 js 提交表单的内容但是那样就不会发起 csrf 攻击了。 四、get 类型 CSRF 攻击
get 类型 CSRF 攻击是利用 iframe、img 等标签发起 get 请求时不存在跨域的问题利用 cookie 发起跨站请求。
4.1 攻击的步骤
有一个安全的网站 Ahttps://safe.com用户登录的身份验证存在 cookie 中使用 get 请求保存敏感信息密码如https://safe.com/savePwd?pwd456在请求时会携带cookie的身份信息用户在 A网站正常登录useId 保存在 cookie 中A 的服务器接收到请求后验证 cookie 信息后就开始更改密码。有一个黑客伪造了一个恶意网站Bhttps://danger.com,此网站有一个有一个 iframe或者imgsrc 的值是 https://safe.com/savePwd?pwd456 黑客诱导用户打开这个网站并且A网站还没有退出登录。【也可以用 a 标签】用户打开 B 网站就会自动发送 更改密码的请求【因为会自动加载 iframe 里面的内容】并且携带了A网站的 cookie。【能携带是浏览器的功能SameSitenone的功能】cookie 不区分端口号但是区分 pathB 网站只能利用 cookie但是不能通过 document.cookie 获取 A 的 cookie因为在B网站上获取的只能是 B 的 cookie
4.2 代码实现
根据 4.1 的流程实现代码即可。
4.2.1 安装 cookie-parser
我们主要的目的是盗用 cookie所以要学会在 express 服务端代码中设置、获取 cookie我们使用 cookie-parser 这个安装包
pnpm i cookie-parser
4.2.2 安装 cors
因为 cookie 是不区分端口号的我们有一个不安全的页面 2并且页面 2 和页面 1 是跨站的而且跨站一定会跨域所以我们要保证我们的服务支持跨域请求express 中设置支持跨域的方法很简单就是使用 cors 这个 npm 包。
pnpm i cors
4.2.3 新增 csrf/index.html
!DOCTYPE html
html langenheadmeta charsetUTF-8 /meta nameviewport contentwidthdevice-width, initial-scale1.0 /titleDocument/titlestyle.button {cursor: pointer;}/style/headbodydiv classbutton onclicklogin()点击用户1登录设置cookie/divdiv classbutton onclicksavePwd()点击修改密码/divscriptfunction login() {fetch(/getInfo).then((res) {console.log(登录成功, res);});}function savePwd() {fetch(/savePwd?pwd456).then((data) {return data.json()}).then((res) {console.log(修改结果, res)})}/script/body
/html4.2.4 新增 csrf/index2.html
!DOCTYPE html
html langen
headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0titleDocument/title
/head
bodyh1恶意网站/h1a hrefhttps://www.safe.com/savePwd?pwd789点击修改密码/a!--img srchttps://www.safe.com/savePwd?pwd789/--!--iframe srchttps://www.safe.com/savePwd?pwd789点击修改密码/iframe--
/body
/html
4.2.5 新增 csrf/index.js
const express require(express);
const path require(path);
const cors require(cors);
const cookieParser require(cookie-parser);// 第一个服务
const app express();
// 允许跨域请求
app.use(cors());
// 解析 cookie
app.use(cookieParser());// 安全的网页用于用户登录
app.get(/, function (req, res) {res.sendFile(path.join(__dirname, /index.html));
});// 安全的网页的登录接口给网站设置 cookie
app.get(/getInfo, function (req, res) {res.cookie(userId, 111111, {expires: new Date(Date.now() 900000),sameSite: None, // 这个是必须设置的secure: true, //只要是 truthy 值就可以path: /,});res.send(end);
});
// 修改密码的接口
app.get(/savePwd, function (req, res, next) {const { query, cookies } req;// 能获取 cookie 才返回修改成功if (cookies.userId) {res.json({pwd: query.pwd,message: 修改成功,});} else {res.json({pwd: query.pwd,message: 修改失败,});}
});
app.listen(3000)// 第二个服务
// 不安全的网页用于用 iframe/ a 标签 等 嵌入安全的网页盗用 cookie
const app2 express();
app2.get(/, function (req, res) {res.sendFile(path.join(__dirname, /index2.html));});
app2.listen(3001)
4.2.6 运行代码
npm run dev csrf
4.2.7 使用 whistle
按照官网安装即可我们使用它的目的是
把 localhost:3000 映射到 https://www.safe.com把 localhost:3001 映射到 https://www.danger.com
这样我们就有 2 个跨站的域名了。 安装之后 在命令行运行 w2 start 然后访问http://127.0.0.1:8899/ 设置域名映射 4.2.8 结果 这里面有一个关键点是需要设置 sameSite none 、 secure true 1chrome 浏览器默认的 sameSite lax所以要手动设置 sameSite none 2要设置 sameSitenone那么就必须要设置 secure true而设置后 securetrue之后 只能通过https 请求携带cookie了这也是我们要使用域名代理的原因。 参考这篇文章 4.2.9 提交代码
五、防御攻击
5.1 进行同源检测
服务端进行同源检测【类似非简单请求的同源策略get请求不会跨域】在接收到请求之后判断 origin 或 referer 的值是否是安全的【如果是在恶意网站发起的请求origin、referer 会显示恶意网站如下图】【优先判断 origin】 Origin 或 Referer 的关系和区别 Origin 和 Referer 都可以服务端用来做来源验证来防止 csrf 攻击都是浏览器自动带在请求头的但是可以通过 Referrer Policy 来禁止请求携带 referer【请求头增加字段 Referrer-Policy: no-referrer/origin等】所以服务器验证请求中的 referer 不太可靠因此标准委员会又制定了 origin 属性在一些重要的场合比如通过 XMLHttpRequest 、fetch 发起跨站请求时都会带上 originOrigin 包含协议、主机和端口不包含路径Referer 只包含了源页面的 URI包含路径而且该字段可能在用户隐私方面存在一些敏感性问题。Origin 主要用于跨站请求的安全性检查而 Referer 则是提供请求来源信息的通用手段可用于日志记录、统计分析等。所以对于 csrf 来源验证服务器的策略是有优先判断 origin如果请求头中没有包含 origin 属性 再根据实际情况判断是否使用 referer Origin 用途 Origin 请求头用于表示一个 URI 的起源包括协议、主机和端口。格式 Origin: scheme://host:port安全性 Origin 是由浏览器自动设置的它通常用于跨站请求的安全性检查。在跨域请求时浏览器会检查目标服务器是否允许来自特定 Origin 的请求如果不允许浏览器会阻止此类请求。origin的值不能手动修改不能手动设置都是浏览器自动的 Referer 用途 Referer 请求头用于表示请求的来源页面的 URI。它指明了用户是从哪个页面跳转或提交请求的。格式 Referer: origin安全性 Referer 是由浏览器设置的但它不像 Origin 那样用于强制安全性检查。虽然 Referer 也可以在某些场景下用于防范 CSRF 攻击但它的值可由用户和服务端控制不是可靠的安全控制手段。可以手动修改document.write(meta namereferrer contenthttp://example.com)但是会收到安全限制不建议 5.1.1 修改 index.js
现在我们来根据请求的 referer 来进行同源检测我们的例子中使用的是a 标签浏览器不会自动携带 origin , 所以我们只能根据 referer 判断了。 这里面有一个关于 express 的知识就是使用 req.get 获取http的请求头 5.1.2 运行
这回我们再点击恶意网站的链接跳转就会显示失败 5.2 使用 CSRF token 验证
其实本质就是每一个请求都加一个 token 验证在该密码的接口中带上这个 token由服务器进行验证验证通过才进行下一步操作。token 的设置主要有两种方式
页面加载时硬编码在 html 中【要用服务端渲染服务端生成token嵌在html中比如form表单的隐藏字段中】【这种方法的问题是每一次请求都需要加上这个 token并且如果加上了 cdn 负载均衡服务器分布式服务器每个服务器的session 无法同步那就保证token的正确了】。很多教程中都是说的用这种服务端渲染的硬编码但是我们常用的vue等框架不是ssr的所以看的云里雾里如果是用的nuxt等框架就可以实现这种逻辑了前端动态获取 CSRF 令牌用新的接口获取 token【要保证这个token获取接口是安全且身份验证和授权不容易受到 CSRF 攻击】这种方式通常被称为 CSRF Token Refresh其中前端可以通过新接口请求一个最新的 CSRF 令牌并将其嵌入到后续的请求中。这可以提高安全性特别是对于那些用户会话可能变得很长时间的应用程序因为 CSRF 令牌的有效性通常是有限的但是这样的话逻辑循环了要怎么保证获取 token 的接口不会受到 CSRF攻击呢。
5.3 对cookie进行双重验证
cookie 进行双重验证是指在第一次访问服务器时设置一个 cookie后续的请求都需要把这个cookie 取出来添加到参数 URL 中然后服务器需要对 cookie 的数据和 URL 上的参数进行比较。恶意网站无法获取 cookie 自然就无法拼出正确的 URL 了。这种方法不涉及分布式访问的问题但是如果存在 XSS 漏洞就会失效这种方法不能做到子域名的隔离。
5.3.1 修改 index.js 5.3.2 修改 index.html
这样修改之后在恶意网站就无法请求成功了因为恶意网站请求的地址没变因为我们没有修改 index2.html 5.3.3 提交代码
5.4 cookie 设置 SameSite
在服务端设置 cookie 属性的时候设置 SameSite 限制cookie不能被第三方使用这个是服务端设置的。samesite 取值 none/Strict/Lax默认为 Laxchrome 浏览器的开发者工具中显示的空其实就是Lax。
严格模式 Strictcookie 在任何情况下都不可能作为第三方cookie使用
宽松模式下 Laxcookie可以被提交get表单、会发生页面跳转的请求使用a 标签/link标签但是如果在第三方中使用post方法或者使用 img/iframe 等标签加载 URL 这些场景不会携带 cookie。
将 Samesite 设为 Lax 这种模式称为宽松模式也是目前浏览器中的默认值。如果这个请求是个 GET 请求并且这个请求改变了当前页面或者打开了新的页面那么这个 cookie 可以作为第三方 cookie【和本篇文章 4.2 中的例子一样】其余情况下都不能作为第三方 cookie。使用这种方法的缺点是因为它不支持子域所以子域没有办法与主域共享登录信息每次转入子域的网站都需要重新登录。还有一个问题就是它的兼容性不够好。
5.4.1 设置 sameSite 因为chrome浏览器默认的 cookie 的 samesite 值是 lax虽然在开发者工具中显示的是空所以如果我们模拟的 demo 中使用的是 firame 或者 src 就不会跨站携带 cookie.。要想我们的demo生效需要在服务端设置 cookie的时候同时设置 samesitenone securetrue。但是 srcure 只有在https请求中可以设置成功所以我们需要使用代理工具将本地的ip地址映射成https:的域名使用whistle工具很方便 w2 start即可 经过这样设置基本上可以杜绝所有的 CSRF 攻击了。
总结
模拟 CSRF 攻击的代码已经完成我的仓库地址如下欢迎查看
yangjihong2113/learn-express
内容较多难免疏漏如有问题欢迎指正。
这是一系列的文章我已经总结完关于 XSS 攻击的内容关于网络安全的内容还有 CORS 和中间人攻击的内容没有总结持续更新中欢迎关注。