SQL 注入、XSS 攻击、CSRF 攻击
SQL 注入
什么是 SQL 注入
SQL 注入,顾名思义就是通过注入 SQL 命令来进行攻击,更确切地说攻击者把 SQL 命令插入到 web 表单或请求参数的查询字符串里面提交给服务器,从而让服务器执行编写的恶意的 SQL 命令。
对于 web 开发者来说,SQL 注入已然是非常熟悉的,而且 SQL 注入已经生存了 10 多年,目前已经有很成熟的防范方法,所以目前的 web 应用都很少会存在漏洞允许进行 SQL 注入攻击。 除非是入门开发人员,在开发的时候仍使用旧的技术去实现(比如 Java 的 Statement 和 PreparedStatement)
SQL 注入漏洞产生的原因
从上面可知,SQL 注入是通过让服务器执行了恶意的 SQL 命令从而进行攻击的,那么主要问题就出在于服务器是如何生成 SQL 语句的。其实绝大多数拥有 SQL 注入漏洞的 Web 系统,在生成 SQL 语句的时候,采用的是拼接字符串的方式,并且没有对要组装成 SQL 语句的参数进行检验和过滤。
常见场景
下面就以一个用户登陆的场景来讲解
现在我们的数据库中有一个用户表(t_user),假设表里面只有两个元素,分别是用户名和密码
然后在 web 应用中,一般在用户登录时,验证的方法一般都是通过账号和密码去获取数据表中是否存在这样的记录,存在则返回用户,不存在则返回 null;
那么我们的 SQL 语句大概就会像这样
SELECT username FROM t_user WHERE username = ‘xxx’ AND password = ‘xxx’
以 Java 为例(使用拼接字符串的形式)
public User login(User user) { // 第一步:构造 SQL 语句,在拼接字符串需要添加'' // 这是因为数据库中字符串值要用 '' 包住 String sql = "SELECT username FROM t_user " + "WHERE username = '" + user.getUsername() + "'" + "AND password = '" + user.getPassword() + "'"; // 第二步:执行查询并返回查询的结果 ... }
那么此时我在登陆页面输入以下信息
账号: 1’ or 1
密码: xxx(随意)
那么服务器会生成这样的 SQL 语句
SELECT username from t_user where username = ‘1’ or 1 and password = ‘xxx’
该sql等同于 SELECT username from t_user where username = ‘1’ or (1 and password = ‘xxx’)
知道一点关系逻辑的人都知道这样的条件查询总会返回记录,有记录则代表登陆成功,则用户不需要知道正确的账号密码就能登陆系统,如果是登陆前台,那可能还好;可如果他拿到了后台管理的链接,登陆进去了后台,那么你的系统可能就会被恶意破坏了。
从代码中可以看到若是采用这种拼接字符串的形式来生成 SQL 语句,那么这就会给 SQL 注入提供了机会。
总结
在开发 web 应用时,应当尽量避免使用拼接字符串的方式来生成 SQL 语句,而且要特别注意检查含有拼接字符串类型的参数的 SQL 语句,尽可能地去测试是否会有 SQL 注入漏洞。
XSS 攻击
什么是 XSS
XSS ,全名:cross-site scripting(跨站点脚本),是当前 web
应用中最危险和最普遍的漏洞之一。攻击者尝试注入恶意脚本代码到受信任的网站上执行恶意操作,用户使用浏览器浏览含有恶意脚本页面时,会执行该段恶意脚本,进而影响用户(比如关不完的网站、盗取用户的
cookie 信息从而伪装成用户去操作)等等。
他与 SQL 注入很类似,同样是通过注入恶意指令来进行攻击。但 SQL 注入是在服务器端上执行的,而 XSS 攻击是在客户端上执行的,这点是他们本质区别。
XSS 的种类
XSS 攻击的分类没有标准,但普遍分为三类:
- 反射型XSS(非持久性跨站攻击)
- 存储型XSS(持久性跨站攻击
- DOM Based XSS(基于 dom 的跨站点脚本攻击)
下面将直接以一些小例子来说明以上三种类别的 XSS 攻击分别是怎样的
反射型 XSS(非持久性跨站攻击)
一般是利用网站某些页面会直接输出请求参数的特性,通过在 url 的请求参数包含恶意脚本,导致用户打开该 url 的时候,执行恶意脚本。
例:http://localhost:8080/test.jsp?abc= <script>alert(“123”) </script>
用户在访问这个页面的时候,就会触发弹窗
当然,一般的 XSS 攻击不会这么简单的就让你弹个窗,可能他会让你不断弹窗(对你恶作剧),也可能会盗取你的信息等;而且一般这种形式的 url 会感觉很奇怪,哪有用户会去打开这种奇怪的 url,所以一般会经过一定的包装来欺骗用户。
存储型 XSS(持久性跨站攻击)
该种类型的攻击一般是通过表单输入(比如发布文章、回复评论等功能中)插入一些恶意脚本,并且保存到数据库,待其他用户加载对应的页面的时候,该段脚本就会被加载并执行。
与反射型
XSS
相比,该类的攻击更具有危害性,因为它影响的不只是一个用户,而是大量用户,而且该种类型还可进行蠕虫传播;就如之前的贴吧和微博事件,用户访问了含有恶意脚本的页面,用户的
cookie 信息被盗取了,并且立刻使用用户的账户去发表新的帖子或微博同时注入恶意脚本,使得该恶意脚本不断被传播下去。
DOM Based XSS(基于 Dom 的跨站点脚本)
基于 DOM 的跨站点脚本与前面两种类型有什么区别呢?其实它注入的方式是基于前面两种类型的方式的,只不过是注入的脚本是通过改变 DOM 来实施的。采用该种方式有一个好处就是从源代码中不易被发现而已。
XSS 攻击漏洞产生的原因
主要原因与 SQL 注入很类似,都是由于开发人员没有对用户输入进行编码和过滤。另一个原因是,这种攻击方法有很多变体,要设计出一个能完全防御的 XSS 过滤器是非常困难的。
如何去防护 XSS
基于上面漏洞产生的原因,我们若要想防御该种攻击,就需要从源头抓起(用户输入),制定一套合理且安全的 XSS 过滤规则。
以下介绍一些过滤方法
-
对 HTML 标签及一些特殊符号进行转义
该种方法是一种非常简单的过滤方法,仅仅是通过转义的方式将一些 HTML 标签和属性转义,比如 < 转义成 < ;, 这样像<script>xxx</script>的脚本就运行不了。当然简单的过滤方式也就代表很容易就会被绕过。
另外如果需要使用含有富文本的功能时,使用这样的过滤就会使富文本失去作用。 -
使用白名单、黑名单的方式进行过滤
白名单、黑名单顾名思义是要定义哪些东西是可通过的,哪些东西不可通过。比如常见 <b>、<p>; 、< 等等标签,不可通过的比如 javascript、<a>、<script>、<iframe>、onload 等等一些属性,将其进行转义。
当然使用该种方法也有自身的缺点,你并不可能穷举出所有元素,也可能会某些元素在黑名单内,但在某些情况它是需要使用的,这就需要我们在设计 XSS 过滤器的时候权衡好,取最合理最适合需求的设计。
总结
身为一名 web 开发人员,应该去了解一下如何能够进行 XSS 攻击,这并不是要你去成为一名黑客去攻击别人的网站,去盗取别人的信息,而是去了解有哪些 XSS 攻击场景,了解产生该漏洞的原因,从而去思考为什么会产生这个 bug,如何去修复这个 bug。要想设计出更好的 XSS 过滤器,就必须得知道有哪些攻击方式,才能这样思考更全面。
注:上面所写的例子,在浏览器中运行不一定能成功,浏览器拥有自身的防御机制,那么简单的攻击方式,一般浏览器自身都已经会拦截了,了,如果你想真的测试的话,自己去 google 一下高级的 XSS 注入方式来学习吧
CSRF 攻击
什么是 CSRF
CSRF,全名:Cross site Request Forgery(跨站域请求伪造)。一般的攻击方式是,通过请求伪造盗取用户的 cookie 信息,进而进行操作。
CSRF 攻击的原理
假设当前有用户 A,服务器 B,服务器 C(含有恶意脚本)
- 首先用户 A 请求登陆了服务器 B,这时服务器 B 响应了用户 A,并且会返回唯一标识的该用户的 cookie 信息。
- 用户 A 在未退出服务器 B 时(即仍与服务器 B 保持会话状态),访问了带有恶意脚本的服务器 C,服务器 C 响应给用户 A 一个恶意页面,并且通过恶意脚本伪装用户 A 向服务器 B 发送请求,此时服务器 B 误以为是用户 A 请求,故响应并返回了用户 A 的 cookie 信息。
- 服务器 C 收到用户 A 与 服务器 B 的cookie 信息后,保存起来,并利用该信息伪装成用户 A 去访问服务器 B,再进行相应的破坏(想干嘛就干嘛)
CSRF 漏洞产生的原因
其主要原因是服务器 B 没有对请求的发起源进行合理的检验,即不加分析地认为请求者一定是正常的用户,就响应了用户信息给非法分子。
如何去防御 CSRF
下面提供两种手段,从服务器端来防御 CSRF
- 验证 HTTP Referer 的值
- 使用请求令牌
验证 HTTP Referer
熟悉 HTTP 协议的人都知道,HTTP 的头部 有一个 Referer 信息的字段,它记录着该次HTTP 请求的来源地址(即它从哪里来的)。
既然 CSRF 攻击伪造的请求是从服务器 C 发送过来的,那么我们就禁止跨域访问,在服务器端增加过滤器的过滤,过滤掉那些不是从本服务器 B 发出的请求,这样可以在一定程度上避免 CSRF 攻击。
但这也是有缺点的:
- 禁止别人跨域访问你,那么如果别人通过百度等搜索引擎来搜索你的时候,此时的 Referer 是 百度,服务器将认为这是不被信任的,所以拒绝响应,这样你的 web 应用就很难推广了(这是我自己想的,我还没测试过)
- 还有一点就是 Referer 的设置问题,虽然一般的浏览器都不允许修改该值,但仍然是存在旧版的浏览器可以修改的(比如 IE6),所以这还是存在一定的安全问题
使用请求令牌
该种方式是参考同步令牌所设计的,同步令牌是用于防止表单重复提交的场景。
请求令牌的工作方式:
- 用户 A 访问服务器 B
- 服务器 B 以某种随机生成策略生成随机字符串,并作为令牌(token)保存在 session 里面,然后夹带着响应返回给用户,并以隐藏域的形式保存在页面中
- 用户每次请求都会夹带着 token 反馈给服务器
- 服务器接收到每次请求,都会先进行令牌验证,若令牌验证不通过,则认为该次请求是非法的,拒绝响应。
由于 CSRF 是通过在服务器 C 上伪造请求的方式来访问服务器 B,所以它是获取不了页面中的 token 字符串,所以在一定程度上是能防御的。
总结
CSRF 攻击是一种请求伪造的攻击方式,它利用的是服务器不能识别用户的类型从而盗取用户的信息来攻击。因此要防御该种攻击,因为从服务器端着手,增强服务器的识别能力,设计良好的防御机制。