拾碎Web安全
XSS 跨站脚本攻击
实质:我们想在我们的程序中引用用户输入的数据,但是用户输入的数据被当成可执行的脚本代码来执行了
- 非持久XSS
利用系统的反馈行为漏洞,诱导用户主动点击,从而发起攻击,即把攻击代码直接写在URL中 系统通过url直接拿到内容进行无过滤渲染。
常见的当你的前端代码中有类似如下的:
location.href.substring(location.href.indexOf('default='))
即会根据直接获取用户发起输入查询请求的 url 的内容进行页面直接渲染在关闭了现代浏览器的默认开启的反射性XSS过滤(ctx.set(X-Xss-Protection:0))之后你就会看到当你输入:
http://xxx.com?default=<script>alert(document.cookie)</script>
或者,当用户的输入直接出现在了HTML属性中也会造成这种情况,即:
1 | <!-- content 直接取自用户输入 --> |
- 存储型XSS
最典型的场景就是黑客通过发表一篇博客,该博客里面含有恶意脚本,被后端无过滤存储到数据库中,每一个浏览该篇文章的用户都会中招。
防御策略:
- 前端渲染的时候对任何的字段都需要做 escape 转义编码。(
<
,>
,双引号,单引号转义) - 尽可能的选择对渲染数据都来自于后端,别从URL中取数据直接渲染
- 设置HttpOnly的 cookie 即document.cookie 获取不到了
- CSP 内容安全策略指定什么资源是可以执行的。
ctx.set('Content-Security-Prolicy','default-src:'self'')
关于富文本
- 黑名单
- 过滤Script标签
- 过滤
javascript:
- 过滤onerrer
- 白名单
- 只允许特定标签和属性
- 遍历富文本中的dom节点的每一个标签名,标签的属性
CSRF 跨站请求伪造
用户在登录了网站A的前提下,访问了一个危险网站B,危险网站B带着网站A的用户登录的cookies开始向网站A发起构造好的请求。
特点:
- 危险网站B并不需要访问网站A的前端
- 请求的 referer 是B网站。
策略:
- 禁止第三方网站带上本网站的cookie
- 在cookie里面新增了一个
same-site
属性(strict,lax),规定只有来自同一个站点的请求才会带有 same-site属性的cookie。 - 针对网站B不访问A的前端,可以设置关键信息的提交设置验证码
- ccap Node端生成验证码图片的第三方模块
- 针对网站B不访问A的前端,可以使用Token的方法
- 在服务端生成一个随机数(token),并且在服务端需要放到两个地方,cookies中,和页面表单中,二者缺一不可,只有表单中的 token那么B可以随便捏造一个假的发送,只有cookie那么无法比较。
- 前端的表单一般使用 type=hiden 的 input标签来存放这个后端来的token值
- 提交表单的时候进行校验,token值是否是空,校验表单中的token和cookie中的token是否一样。
- 在cookie里面新增了一个
Token的问题
将token放在表单中是可行的,但是如果一个页面要进行ajax请求的话,需要使用另外一种做法就是将 token 放到页面的meta标签中,在ajax请求的时候要从meta标签中获取那个随机数。
Cookie
特性:
- Cookie 遵守同源策略
- Cookie 在前端可以通过
document.cookie=
来进行追加,但是不能覆盖(未使用http-only)但是可以篡改以前的同名的cookie - Cookie 只能通过有效期设置为过期的时间才能删除
作用:
- 存储个性化设置(比如个性化皮肤)
- 存储未登录的用户的一些唯一标识
- 存储区已登录用户的凭证
- 存储其他业务数据
登录相关
防止用户篡改Cookie值
比如如果网站是使用给客户端使用 “userId=1” 的方式来进行登录态凭证的设置,那么当前端调用 document.cookie = 'userId = 2'
那么该用户就得到了用户2的身份。
解决办法1,在服务端再一次生成一个加密的签名,即使用 用户ID+签名 的Cookie组合
- 第一步生成加密签名序列号
1 | // 加密工具模块 |
- 因为签名是一个不可逆的算法,不可能根据签名的值倒推出原来的明文是多少。所以解决的办法是,将签名过的字符串和用户登录Id一起发去客户端,根据客户端带回来的 userId 和 sign 值在服务端算 userId 的加密签名是否等于用户带来的 sign 从而判断是不是已经被篡改之后的 userId 。
方法二:Session 认证
即在Cookie中只set进一个存在服务端中的 seesionID(随机字符串) ,用户请求带来sessionId之后再服务端判断用户身份。
点击劫持问题
利用一个透明度被设为 0 的 Iframe 引用目标网站,诱导用户真实点击,用户其实点击的操作是在操作目标网站的某些操作。
防御点击劫持的办法:
- 利用页面被 iframe 引用的时候他的 top 对象是不等于 window 对象的特点(一个正常页面的top === window)可以在网站的源 html 中使用 javascript 做判断。但是当 iframe 设置 sand-box属性时 是可以做到禁止原页面的JS脚本的功能的。
- 在服务端设置
X-Frame-Options
来禁止或者允许部分的网站对我的页面进行 iframe 内嵌
HTTP 传输窃听
“traceroute www.baidu.com” 查看经过多少节点
在一个浏览器到服务器中间会经过很多的链路以及代理服务器,他们都有机会去窃听你的请求和替换掉本来的响应。
如果把用户登录请求时的用户名和密码都设置为明文….
如果你想获得的响应中被中间商在响应中增加个大量的广告….
最常见的例子:当你连接了某一个Wifi之后去访问百度,最后你却得到的页面是 wifi 运营商让你输入用户名和密码,这就是运营商的http劫持
解决策略:HTTPS
密码安全
密码泄露渠道:
- 网站数据库被盗
- 服务器被入侵,得到密码就转发
- 通讯过程被窃听
- 撞库(人们习惯不同网站注册的时候使用同一套密码)
应对策略:
- 不用明文对密码进行存储,对密码进行单向变换
- 加强复杂度的变换测试,
md5(sha256(xxx+盐+花样字符串))
,去应对现在出现的“彩虹表”(根据md5值反推明文的表)。
- 加强复杂度的变换测试,
- 重视密码传输过程的安全性
- https 传输
- 频率限制(不允许连续多次的去试探密码登录)
- 前端加密(意义有限),但是可以有效防止撞库的情况出现。
SQL注入
select * from user where userId = ${inputId} and password = ${inputPasswod}
上述sql语句想根据获取的用户输入id直接准备去查数据库,用户精明的输入了 1' or '1'='1
select * from user where userId = 1 and password = '1' or '1'='1'
sql注入的原理非常想XSS攻击的原理,执行了不该执行的代码。
防御:
- 关闭错误输出(不要把后端的错误日志暴露给前端)
- 转义
- mysql提供的参数化查询(?数组的形式
- ORM(对象关系映射)
- Node 中的 sequelize框架
DOS(Denial of Service)拒绝服务攻击 & DDOS大规模分布式拒绝服务攻击
种类:
- TCP半连接
- 只进行两次握手就不理人家了
- http连接
- 攻击DNS服务器
本文为原创文章作为学习交流笔记,如有错误请您评论指教
转载请注明来源:https://isliulei.com/article/web-security/