背景

前段时间工作时,安全同学特意提醒要注意安全问题,如:

坦白说,安全这块关注比较少,于是跟安全同学交流学习,推荐一本叫白帽子讲 web 安全书籍,本文的内容是基于该书籍的介绍进行修改调整。

书籍信息

本书全名:白帽子讲 Web 安全
作者:吴翰清
出版社:电子工业出版社
版次:2015 年 1 月

安全的三要素

安全评估过程

简单分为 4 个阶段:

资产等级划分

威胁分析

威胁分析就是把所有的威胁都找出来,一般是采用头脑风暴法。
但大部分会采用威胁建模,把尽可能多的风险列出,好处是避免遗漏,常用的方法叫STRIDE模型,如下:

主要包括伪装、篡改、抵赖、信息泄露、拒绝服务、提升权限

风险分析

风险由以下因素组成:

Risk = Probability * Damage Potential

影响风险高低的因素,除了造成损失的大小外,权衡事件发生的可能性,才能正确地判断出风险。

微软提出的DREAD模型可以更科学的判断威胁的风险程度:
image.png-382.4kB

安全方案的特点

优秀的安全方案应该具备以下特点:

设计方案时的方法

浏览器安全

同源策略

A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源"。所谓"同源"指的是"三个相同"
定义如下:

协议相同
域名相同
端口相同

举例来说,http://www.jb.com/dir/page.html这个网址,协议是http://,域名是www.jb.com,端口是80,它的同源情况如下。

http://www.jb.com/dir2/other.html:同源
http://www.jb.com/dir/inner/another.html:同源
http://jb.com/dir/other.html:不同源(域名不同)
http://v2.www.jb.com/dir/other.html:不同源(域名不同)
http://www.jb.com:81/dir/other.html:不同源(端口不同)

设想这样一种情况:
用户登录 A 网站以后,又去浏览网站 B。如果网站 B 可以读取 A 网站的 Cookie,会发生什么?

很显然,如果Cookie 包含隐私(比如金额、个人信息等),这些信息就会泄漏。

由此可见,同源政策的目的是为了保证用户信息的安全,防止恶意的网站窃取数据

浏览器沙箱

沙箱模型技术是浏览器和其他应用程序中保护安全的一种组件关系设计模式,设计目的一般是为了让不可信任的代码运行在一定的环境中,限制不可信任的代码访问隔离区之外的资源

恶意网址拦截

工作原理一般是,浏览器周期性地从服务器端获取一份最新的恶意网址黑名单,如果用户上网时访问的网址存在于此黑名单中,浏览器就会弹出一个警告页面

跨站脚本攻击(XSS)

XSS攻击通常指黑客通过HTML注入篡改了网页,插入了恶意的脚本,从而在用户浏览网页时,控制用户浏览器的一种攻击

例子

某天,公司需要一个搜索页面,根据 URL 参数决定关键词的内容。jb 很快把页面写好并且上线,代码如下:

<input type="text" value="<%= getParameter("keyword") %>">
<button>搜索</button>
<div>
  您搜索的关键词是:<%= getParameter("keyword") %>
</div>

然而,在上线后不久,就接到了安全组发来的一个神秘链接:

http://xxx/search?keyword=">

带着一种不祥的预感点开了这个链接,页面中弹出了写着” XSS” 的对话框。

当浏览器请求 http://xxx/search?keyword="><script>alert('XSS');</script> 时,服务端会解析出请求参数 keyword,得到 "><script>alert('XSS');</script>,拼接到 HTML 中返回给浏览器。形成了如下的 HTML

<input type="text" value=""><script>alert('XSS');</script>">
<button>搜索</button>
<div>
  您搜索的关键词是:"><script>alert('XSS');</script>
</div>

浏览器无法分辨出 <script>alert('XSS');</script> 是恶意代码,因而将其执行。

这里不仅仅 div 的内容被注入了,而且 inputvalue 属性也被注入, alert 会弹出两次。

本质

恶意代码未经过滤,与网站正常的代码混在一起;
浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。

分类

存储型

存储型XSS会把用户输入的数据存储在服务器端,具有很强的稳定性。

攻击步骤:

反射型

把用户输入的数据反射给浏览器,黑客往往需要诱使用户点击一个恶意链接,才能攻击成功。

攻击步骤:

反射型跟存储型的区别

DOM 型

通过修改页面的DOM节点形成的XSS,称之为DOM Based XSS

攻击步骤:

跟前两种的区别:

预防

XSS 攻击有两大要素:

输入过滤

常见的Web漏洞如XSS、SQL Injection等,都要求攻击者构造一些特殊字符,这些特殊字符可能是正常用户不会用到的,所以输入检查就有存在的必要了。

输入检查的逻辑,必须放在服务器端代码中实现。如果只是在客户端使用JavaScript进行输入检查,是很容易被攻击者绕过的。

目前Web开发的普遍做法,是同时在客户端 JavaScript 中和服务器端代码中实现相同的输入检查。客户端JavaScript的输入检查,可以阻挡大部分误操作的正常用户,从而节约服务器资源。

输出过滤

在变量输出到 HTML 页面时,可以使用编码或转义的方式来防御XSS攻击。

跨站点请求伪造(CSRF)

CSRF(Cross-site request forgery)跨站请求伪造:
攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的

攻击流程

攻击类型

特点

防护策略

Token

上面提及到CSRF的特性,攻击者无法直接窃取到用户的信息(Cookie,Header,网站内容等),仅仅是冒用 Cookie 中的信息

CSRF攻击之所以能够成功,是因为服务器误把攻击者发送的请求当成了用户自己的请求。那么可以要求所有的用户请求都携带一个CSRF攻击者无法获取到的Token。服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击。

Token 的值必须是随机生成的

点击劫持

点击劫持是一种视觉上的欺骗手段。
攻击者使用一个透明的、不可见的iframe,覆盖在一个网页上,然后诱使用户在该网页上进行操作,此时用户将在不知情的情况下点击透明的iframe页面。

image.png-149.7kB
一眼看这个按钮没啥问题,但实际会跳到jb.html,还可以模拟一些博客的点赞、关注功能,但因为需要诱使用户与页面进行交互,实施的攻击成本更高,一般来说都比较少见。

用户以为点击的是下层页面内容,但真正操作的是上面的透明的一层。而因为它是透明的,真正的意图是由攻击者在控制。
image.png-399.9kB

注入安全

介绍

注入安全应该是安全领域中最常见的攻击方式,一般测试同学也是比较关注这类,前面提交到的xss本质也是对HTML的注入攻击。

注入攻击的本质,是把用户输入的数据当做代码执行。

关键的条件:

SQL 注入

例子

有这么一段逻辑:

var Shipcity;
ShipCity = Request.form ("ShipCity");
var sql = "select * from OrdersTable where 
ShipCity = '" + ShipCity + "'";

变量ShipCity的值由用户提交,在正常情况下,假如用户输入Beijing,那么SQL语句会执行:

SELECT * FROM OrdersTable WHERE ShipCity = 'Beijing'
但假如用户输入一段有语义的SQL语句,比如:

Beijing'; drop table OrdersTable--
那么,SQL语句在实际执行时就会如下:

SELECT * FROM OrdersTable WHERE ShipCity = 
'Beijing';drop table OrdersTable--'
这里看到,原本正常执行的查询语句,现在变成了查询完后,再执行一个 drop 表的操作,而这个操作,是用户构造了恶意数据的结果。

攻击行为

1. 猜测数据库名,备份数据库
2. 猜解字段名称
3. 遍历系统的目录结构,分析结构并发现WEB虚拟目录,植入木马
4. 查询当前用户的数据库权限
5. 设置新的数据库帐户提权得到数据库管理员账户权限
6. 利用存储过程获取操作系统管理员账户
7. 客户端脚本攻击:通过正常的输入提交方式将恶意脚本提交到数据库中,当其他用户浏览此内容时就会受到恶意脚本的攻击。
8. 客户端脚本攻击:通过SQL注入方式将恶意脚本提交到数据库中,直接使用SQL语法UP

如何防御

采用 sql 语句预编译和绑定变量
String sql = "select id, no from user where id=?";
       PreparedStatement ps = conn.prepareStatement(sql);
       ps.setInt(1, id);
       ps.executeQuery();

如上所示,就是典型的采用 sql语句预编译和绑定变量 。为什么这样就可以防止sql 注入呢?

因为采用了PreparedStatement,就会将sql语句:"select id, no from user where id=?" 预先编译好,也就是SQL引擎会预先进行语法分析,产生语法树,生成执行计划.

后面输入的参数,无论输入的是什么,都不会影响该sql语句的语法结构了,因为语法分析已经完成了,而语法分析主要是分析sql命令,比如 select ,from ,where ,and, or ,order by 等等。

所以即使后面输入了这些sql命令,也不会被当成sql命令来执行了,因为这些 sql 命令的执行, 必须先的通过语法分析,生成执行计划,既然语法分析已经完成,已经预编译过了,那么后面输入的参数,是绝对不可能作为sql命令来执行的,只会被当做字符串字面值参数。所以sql语句预编译可以防御sql注入。

使用安全函数

有一些场景必须的采用字符串拼接的方式,此时需要严格检查参数的数据类型,还有可以使用一些安全函数,来防止 sql 注入。

安全函数的使用,比如:

        MySQLCodec codec = new MySQLCodec(Mode.STANDARD);
        name = ESAPI.encoder().encodeForSQL(codec, name);
        String sql = "select id,no from user where name=" + name;
ESAPI.encoder().encodeForSQL(codec, name)

该函数会将 name 中包含的一些特殊字符进行编码,这样 sql 引擎就不会将name中的字符串当成sql命令来进行语法分析了。

检查数据类型

比如 String sql = "select id,no from user where id=" + id;

在接收到用户输入的参数时,我们就严格检查 id,只能是int型。复杂情况可以使用正则表达式来判断。这样也是可以防止sql注入的。

XML 注入

XML是一种常用的标记语言,通过标签对数据进行结构化表示,进行攻击需要满足两大条件:

解决方案跟上面类似,对用户输入数据中包含的保留字符进行转义即可。

代码注入

代码注入与命令注入往往都是由一些不安全的函数或者方法引起的

对抗代码注入、命令注入时,需要禁用eval()、system()等可以执行命令的函数。如果一定要使用这些函数,则需要对用户的输入数据进行处理。

代码注入往往是由于不安全的编程习惯所造成的,危险函数应该尽量避免在开发中使用,可以在开发规范中明确指出哪些函数是禁止使用的。

小结

了解安全评估流程,xss、csrf、sql注入攻击的原理及常用的预防措施,点击劫持常用的手段信息。


↙↙↙阅读原文可查看相关链接,并与作者交流