前端旧约

今天做别人不愿意做的事, 明天做别人不能做的事

浅析常见的 Web 安全预防

1. CSRF

跨站请求伪造,英文名:Cross-site request forgery

CSRF 攻击流程

结合下面这张图说明 CSRF 的攻击流程。

李四在网站 A 注册了用户名,并且登录到了网站 A,在登录之后网站 A,就会给用户李四的浏览器下发一个 cookie,李四在访问网站 A 的时候,就会将本地的 cookie 上传到网站 A,如果有这个 cookie 就判断李四处于登录状态,如果没有就判断李四处于未登录状态。

现在李四处于登录状态,再访问网站 A 的时候,cookie 会被上传到 网站 A,因此网站 A 会认定 李四 处于登录状态。

现在李四又去访问网站 B,网站 B 的页面上放了一个在网站 A 上成为张三粉丝的链接,当李四点击这个链接的时候,就会去访问网站 A,这个时候网站 A 经验证发现要李四处于登录状态,并且请求要成为张三的粉丝,合情合法,因此,李四在不知情的条件下就成为了张三的粉丝。

成为粉丝还是小事,如果是你设置了某个免密支付网站的付款链接呢,所以不要轻易去点击不可信的链接。

一句话总结 CSRF 就是,利用你的身份在你不知情的情况下去做一些事情。

CSRF 的特点

  • 攻击一般发起在第三方网站(网站 A),而不是被攻击的网站(网站 B),被攻击的网站(网站 A)无法防止攻击发生,只能去防御。

  • 攻击利用受害者(李四)在被攻击网站(网站 A)的登录凭证,冒充受害者(李四)发出请求,而不是直接窃取数据。

  • 整个过程攻击者(张三)并不能获取到受害者(李四)的登录凭证(cookie 等信息),仅仅是“冒用”。

    这一点很重要,由于浏览器的同源策略,那么攻击者只是去使用受害者的 cookie 信息,并不能获取到 cookie。

  • 跨站请求可以用各种方式:图片URL、超链接、CORS、Form提交等等,部分请求方式可以直接嵌入在第三方论坛、文章中,难以进行追踪。

CSRF 的攻击类型

1)GET 类型的 CSRF

GET 类型的 CSRF 攻击只需要一个链接就可以了,例如用户访问了一个链接,这个链接下的 html 文件中有下面这个 img 标签,这个时候会自动向 img 标签 src 指向的恶意链接发出请求。

<img alt='#' src='恶意链接' />

2)POST类型的 CSRF

POST 类型的 CSRF 可以通过在页面中嵌入一个表单来实现。

<form action='恶意链接' method='POST' >
  <input type="hidden" name="account" value="codingOrange" /> 
</form>
<script>document.forms[0].submit()</script>

document.forms 表示获取到页面中所有的 form 表单元素,document.forms[0] 表示获取到页面中第一个表单元素,submit() 表示提交表单

CSRF 防御措施

1)尽量使用 POST 请求

通过上面的描述可以发现,使用 GET 请求更容易伪造链接,但是其实都差不多,不能依靠这种方式来防范 CSRF 攻击。

2)使用验证码

对某些请求需要人为地加入验证码,确保这次的请求时用户许可的行为。例如在付款时,需要用户再次输入密码,在发起某个请求时,需要用户手动输入验证等。

3)验证来源站点

既然 CSRF 攻击一般是从另一个网站发起的,那么服务端可以通过 http header 中的 referer,orgin 验证这次请求的来源站点,如果不是本站点那么就可以将这次请求拦截。

4)CSRF Token

在用户向服务端第一次请求页面时,服务端会返回一个 Token,前端拿到这个 Token 之后,可以将所有的(通过遍历 DOM 元素)能发出请求(a ,img,form 等标签)的链接上都加上这个 Token 信息,CSRF 攻击者并不能获取到 Token(浏览器的同源策略),所以也就不能伪造出恶意请求链接,伪造出的恶意链接中没有 Token 信息,服务端就能分辨出这不是用户的请求。

举个例子吧:

在网站 A 上,正常点击时发出的请求的链接是

https://www.a.com/?token=tokenMessage

CSRF 攻击者由于获取不到 tokenMessage,所以并不能伪造出上面能正常发出请求的链接。

5)双重 cookie

利用浏览器的同源策略,攻击者是不能获取到 cookie 信息,因此在用户第一次访问时,服务端可以在 setCookie 中写入一个 CSRFMessage 信息,因此前端通过解析 cookie 就可以获取到这个 CSRFMessage,然后在发送的所有请求的 url 中都加上这个 CSRFMessage,服务端在接收到请求后通过请求中是否有 CSRFMessage 信息来判断是不是用户的请求。

2. XSS

下面只是说了很少的一部分,更多内容请参考 前端安全系列(一):如何防止XSS攻击?

XSS(cross-site scriping)跨站脚本攻击。

XSS 攻击原理

向页面注入脚本。例如在评论区写评论时,写上下面这段评论

<script>
	恶意代码
</script>

也可以通过某个标签绑定事件等方式向页面中注入 JS 代码。

XSS 防御措施

使用 xss 库中的 xss 函数,在 react,vue,node 中都可以使用。

# 安装 xss
npm install xss --save

使用 xss

import xss from 'xss'
xss('<script>恶意代码</script>')

CSRF 和 XSS 的区别

XSS 是通过在页面注入 JS 代码(恶意代码)来攻击

CSRF 是通过借用别人的身份去做一些事。

3. sql 注入

  • 最原始、最简单的攻击,自从有了 web2.0 就有了 sql 注入攻击

  • 攻击方式:输入一个 sql 片段,最终拼接成一段攻击代码

    比如输入用户名和密码的时候不是输入的用户名和密码而是 sql 片段,这样的话后端拿到用户提交的用户名和密码之后和数据库通信进行验证的时候,有可能就是一段攻击代码。

  • 预防措施:使用 mysqlescape 函数处理输入内容即可

下面以自己开发的博客项目中登录时的代码为例进行说明 :

博客项目中登录时的关键代码:

const sql = `
select username, realname from user 
where username ='${username}' and password='${password}';
`;

项目代码中登录的实现:利用用户传递过来的 usernamepasswordMySQL 数据库中查询,如果 usernamepassword 在数据库中存在,那么返回 username,并将其存入 redis 中,通过 req.session.username 是否存在来判断用户是否登录,如果登录不成功 req.session.usernameundefined

上面的代码是漏洞百出,如果一个用户这样写用户名和密码,想一下能不能登录成功?

username = zhangsan' --  password = 111

MySQL 中对应的代码:

select username, realname from user where username ='zhangsan' -- ' and password='111';

可以看到 -- 后面的语句都被注释掉了,等价于下面语句:

select username, realname from user where username ='zhangsan'

不用多想,登录肯定成功呀,只要能够知道用户名就可以登录上去。

如果黑客想删除你的数据库也很容易:

select username, realname from user where username ='zhangsan'; delete from user -- ' and password='111';

这样在执行完查询用户名为 zhangsan 的信息之后,紧接着会删除 user 表中的所有内容。

sql 注入防御措施

利用 MySQL 提供的 escape 函数将所有出现在 sql 语句中的变量都执行一遍,以上面的代码为例。

注意 const sql = `` 中 ${username} 在使用 escape 函数之后已经不需要再加引号了。

username = escape(username);
password = escape(password);
// 使用 escape 函数运行完之后的函数就不需要在 sql 中加引号了
const sql = `
select username, realname from user 
where username =${username} and password=${password};
`;

现在 username 还是 zhangsan' --password 还是 111

利用 escape 函数之后生成的 sql 语句:

sql = select username, realname from user 
where username ='zhangsan\' -- ' and password='111';

这时候 username 中间的 '\ 转义了, -- 也不能起注释作用了。

不只是登录这个地方只要是由用户输入的而且要在 sq 语句中出现的变量,都要在 escape 函数中执行一遍,注意在 sql 语句中 ${变量} 两边就不需要加引号了。

posted on 2020-05-17 15:14  前端旧约  阅读(348)  评论(0编辑  收藏  举报

导航