一个前端眼中的Web安全

523 阅读15分钟

security-265130__480.jpeg

哈喽,大家好,我是jiaynn~

这里分享一下我对于Web安全的理解。

全文概览

  • Web安全概述
  • 2021 OWASP TOP 10
  • XSS攻击与防护
  • SQL注入

进入正题

Web安全概述

有一句话我觉得特别有趣,先来分享给大家互联网本来是安全的,自从有了研究安全的人之后,互联网就变得不安全了

好了,真的要进入正题了

Web安全

我们所谈论的 Web,指的是所有基于 HTTP 或者其他超文本传输协议(RPC 等)开发的应用,包括:网页、App、API 接口等等。这类应用的共同点是:通过 HTTP 等文本协议,在客户端和服务端之间进行数据交换。客户端将服务端传送的数据渲染出来,服务端将客户端传入的数据进行对应的处理。而 Web 安全所涉及的正是这些应用中存在的各类安全问题。

其实在我们日常开发中,其实会遇到很多潜在的安全隐患。以 Web 应用为例,前端可能会存在 XSS、后端可能会发生注入、用户可能会遭遇 CSRF、算法上可能发生弱加密、证书验证上可能遭遇中间人攻击。

为什么平时开发中我们通常没有考虑到这些情况呢?

因为我们开发中使用的底层语言、应用框架、技术架构都从不同层面帮我们解决了这些问题,这些解决方案的背后是语言开发工程师、框架开发工程师、开源工程师和公司架构师的共同努力,站在巨人的肩膀上前行使得我们不需要掌握庞大的知识体系。

如果我比别人看得更远,那是因为我站在巨人的肩上——牛顿

但其实Web安全还是很值得我们去了解,去探索的,那有什么途径可以快速的了解Web安全,推开安全的大门呢🤔

答案是 OWASP(The Open Web Application Security Project)

这是一个是一个致力于 Web 应用程序安全的国际非营利组织

每隔几年 OWASP 都会发布十大安全漏洞。它代表了对 Web 应用程序最关键的安全风险的广泛共识。旨在提高人们对Web应用程序中突出网络安全风险的认识

它帮助我们去了解,在目前的技术趋势下,Web 业务系统中那些最常见、最高危的十类安全风险和漏洞。安全业内有许多人将 OWASP TOP 10 作为一种 coding 或测试的标准,但也因为它只包含了最具影响力的十类安全风险,所以在这些方面其实并不详实,但它依旧可以作为一个好的起点,帮助我们去了解 Web 安全,冲啊,进入安全的大门近在咫尺~

2021版 OWASP TOP 10

image.png

首先我们可以一一的来大致的了解一下他们,位于在咱们top1的是失效的访问控制,我们可以拆开来看,访问控制其实就是给用户一个边界,使得用户不能获得边界之外的权限操作,然后他失效了,就说明用户可以越权操作了,会导致敏感信息泄露,权限异常等问题

加密失败

其实就是密码学加密失败的问题,你使用sha-1/md5等加密你的数据库需要保密的信息,结果别人破解了,后果想想就很恐怖

注入

常见的有sql注入/命令注入等,我后面也会详细的讲一下有关sql注入的内容

不安全的设计

他是 2021 年新引入的一项安全风险,主要关注设计和业务流程上的风险。呼吁我们更多的使用威胁建模、安全设计模式和参考体系结构

威胁建模:将开发完成的软件会面临哪些安全威胁,由此在接下来的软件设计和软件实现等环节中来防范每一个安全威胁,通过抽象的概念模型对影响软件系统的威胁进行系统的识别和评价

安全配置错误

90%的web应用程序都经历过错误配置测试,这些将导致安全风险。

易受攻击和过时的组件

现在大部分产品都会不同程度的依赖各种第三方组件;可能是直接使用的组件或者间接使用的组件,如果客户端和服务器使用了易受攻击的组件版本,就可能成为被攻击者攻击的目标。

识别和认证失败

确认用户的身份、认证过程以及 Session 管理是预防认证相关风险的关键点,简单来说,这一风险类别可能面临的攻击包括暴力破解、密码喷洒、弱口令、Session 管理不当等诸多问题。

软件和数据完整性故障

这也是一个新增的类型,这一分类主要关注于软件更新、关键数据以及“CI/CD”流水线的完整性校验。用比较直白的话来说,现在大多数应用都不同程度依赖于外部的插件、库等,所有这些外部风险都可以通过一次不安全的“CI/CD”流水线直接引入到应用中;更进一步,应用的自动更新策略已经广泛普及,但是关于更新内容的完整性检测却没有跟上,这就导致软件更新可能直接引入安全风险。

安全日志记录及监控失败

它指的是在没有日志记录和监控下,将无法检测到漏洞,此类故障会直接影响可见性、事件报警和取证。

服务端请求伪造

也就是SSRF,与CSRF相反发生在服务端

大致了解了2021年十大安全威胁,我们来看看与2017年发生了哪些变化

image.png

我们可以从图中看出 注入和认证失败 这两类曾经风靡一时的安全风险排名都有了较大幅度地降低,其原因主要有两个方面。一方面,目前整个行业安全意识的提高,大大降低了线上运营中业务系统存在高危安全风险的可能性,因为现在大部分公司在 Web 业务系统上线之前都会经过详细的渗透测试,而这个流程在几年之前还没有这么普遍;另一方面就是第三方安全和认证组件的成熟,降低了认证失败的安全风险。

这里值得我们展开探讨的是上升幅度较大的三类风险——加密失败、失效的访问控制以及存在安全缺陷的组件,这三类风险从一个侧面显示了目前 Web 安全开发及测试过程中需要重点关注的风险种类:加密失败及失效的访问控制,这两类安全风险的共性都是非代码层技术性漏洞,无论是使用了不当的编码加密,还是业务逻辑分割不严密,都是传统安全领域扫描器难以发现的风险种类;存在缺陷的安全组件则是在目前技术多元化趋势下,一个必然出现的安全风险种类。

然后这篇文章我想主要从前端和后端两个方面去探索前端和后端在开发过程中容易遇到的两类攻击:XSSSQL注入

XSS攻击

XSS,即 Cross Site Script,中译是跨站脚本攻击;其原本缩写是 CSS,但为了和层叠样式表(Cascading Style Sheet)有所区分,因而在安全领域叫做 XSS。

下面我们来看一个场景:

有一天,有很多用户发送了同样类型的内容,而且这些内容都是一个带有诱惑性的问题和一个可以点击的链接。这些用户全部反馈说,这不是他们自己发的。前端开发表示,用户内容都是后端产生的,他不负责。后端开发表示,这些内容都是用户自己提交上来的,他也不负责。正当大家议论纷纷的时候,你作为学习过安全专栏的人,敏锐地发现了问题的原因:这是黑客发起了XSS攻击

XSS漏洞是指,应用程序没有对接收到的不可信数据经过适当的验证或转义就直接发给客户端浏览器。恶意用户编写恶意代码并通过输入表单提交。目标服务器包含这个脚本及其响应,客户机浏览器执行它。由于浏览器信任web服务器,它授予恶意脚本访问本地存储的cookie、会话令牌和其他敏感客户端信息的权限。

攻击者利用XSS漏洞将恶意脚本代码注入到网页中,当用户浏览网页时,便会触发执行恶意脚本

XSS漏洞主要危害如下:

  • 非法访问、篡改敏感数据
  • 会话劫持
  • 控制受害者机器向其他网络发起攻击。如果攻击者利用社交网站上的XSS漏洞,由于社交网站的用户规模巨大,将有大量的用户成为攻击对象,所造成的影响和破坏力巨大

XSS攻击可以分为3类:反射型(非持久型)、存储型(持久型)、基于DOM。

反射型XSS:我的理解是他一般位于服务端渲染,服务器直接使用客户端提供的数据,而没有对数据进行无害化处理,他一般需要用户点击某个恶意链接,这个链接去访问服务器服务器返回页面内容后,在用户的浏览器上执行恶意代码,这个可以是直接跳转到黑客准备的页面,也可以是获取用户的cookie等等,具体如下图(图是参考掘金某位大佬的)

image.png 基于DOM的XSS:一般出现在前后端分离模式,指的是通过恶意脚本修改页面的 DOM 结构,是纯粹发生在客户端的攻击,无需服务端的介入,具体如下图

图片1.png

存储型XSS:先将恶意代码上传或存储到数据库,只要用户浏览包含此恶意代码的网页便会遭受攻击 比较常见的一个场景是攻击者在社区或论坛上写下一篇包含恶意 JavaScript 代码的文章或评论,文章或评论发表后,所有访问该文章或评论的用户,都会在他们的浏览器中执行这段恶意的 JavaScript 代码。 存储型XSS攻击流程:

image.png

我学习XSS攻击的实战是直接在靶场进行的学习,可以参考dvwa.bihuo.cn/login.php 里面的XSS攻击,分析他的源码,调整不同的难度,观察他的过程以及防护

XSS攻击的防护

我这里从两个方面去分析如何进行防护,首先我们可以发现存储型反射型 XSS都是在服务端取出恶意代码后,插入到响应 HTML 里的,攻击者刻意编写的“数据”被内嵌到“代码”中,被浏览器所执行。而DOM型 XSS 攻击实际上就是网站前端JavaScript 代码本身不够严谨,把不可信的数据当作代码执行了。

预防存储型和反射型 XSS 攻击

  • 改成纯前端渲染,把代码和数据分隔开
  • 对 HTML 做充分转义。采用合适的转义库,对 HTML 模板各处插入点进行充分的转义

预防 DOM 型 XSS 攻击

  • 在使用.innerHTML、document.write() 等时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用.textContent、.setAttribute() 等。
  • 如果用 Vue/React 技术栈,并且不使用 v-html/dangerouslySetInnerHTML 功能,就在前端 render 阶段避免 innerHTML、outerHTML 的 XSS 隐患。

当然有这些防护可能还不够,我们也有一些通用的防护方法

  1. CSP(Content Security Policy,内容安全策略)

在服务端返回的 HTTP header 里面添加一个 Content-Security-Policy 选项,然后定义资源的白名单域名。浏览器就会识别这个字段,并限制对非白名单资源的访问。

  1. 输入内容长度控制

对于不受信任的输入,都应该限定一个合理的长度。虽然无法完全防止 XSS 发生,但可以增加 XSS 攻击的难度。

  1. 其他安全措施
  • HTTP-only Cookie: 禁止 JavaScript 读取某些敏感 Cookie,攻击者完成 XSS 注入后也无法窃取此 Cookie。
  • 验证码:防止脚本冒充用户提交危险操作。

SQL注入

SQL注入的核心其实是将用户输入的数据拼接到代码中,并被当成sql语句执行

下面我们直接用靶场来进行一个sql注入实战

靶场链接:hack.zkaq.cn/battle

我们先来看看SQL注入的一般流程

image.png

了解了其中的流程,我们就直接开始吧

1. 判断注入点

我们想进行sql注入,肯定要找到页面与数据库产生交互的地方

image.png

image.png 首页没什么特别的,我们点击查看新闻看看,发现他的url变成了这样

http://cntj8003.ia.aqlab.cn/?id=1

我们可以看到他后面跟了一个参数,而这个参数是我们可以控制的地方,我们现在来试着更改一下他的值

image.png 我们构造 ?id=1 and 1=1,发现页面正常

image.png

再构造?id=1 and 1=2,页面不正常,说明存在sql注入漏洞

image.png 好了,现在我们找到了注入点,进行第二步

判断字段数

我们可以使用order by语句判断字段数,order by语句使用如下

//假设有一张login表,里面存在usename和password两个字段
select * from login order by username //查询login表中的所有内容并按照username升序排序
select * from login order by 1 //与上一个语句等价
select * from login order by 3 //报错,因为没有第三个字段

由于我们现在并不知道数据库的信息,我们可以通过order by 1进行试探

image.png 页面正常,我们在进行order by 2

image.png 此时页面也正常,仔构造order by 3

image.png 发现页面返回内容不正常,说明字段数为2,因为查询3的时候没有返回内容

判断回显点

接下来我们要使用union select语句,联合查询,通过页面回显找到回显点,再利用其获取我们需要查询的数据。

union操作符用于合并两个或多个 SELECT 语句的结果集。而且联合查询前后的两个表,第一个表的字段数与第二个表的字段数必须相等,否则就会报错

注意:联合语句优先显示的是联合查询的第一个结果,而第二个结果未被显示
所以我们需要在这里使联合查询的第一个结果不显示(使前半段语句为假,则不返回任何内容),让我们构造的第二个查询语句显示出来

构造?id=1 and 1=2 union select 1,2

image.png

发现回显点,就在2这个位置

查询相关内容

查询当前数据库名: 构造 ?id=1 and 1=2 union select 1,database(),发现数据库名为maoshe

image.png

判断表名

//table_name列在information_schema库的tables表下
?id=1 and 1=2 union select 1,table_name from information_schema.tables where table_schema="maoshe"

image.png

查询到表名为admin,知道了表名,我们再来查看一下他有哪些字段

查询列名: 这里为了方便,我们直接使用group_concat()函数 一起输出所有列名

?id=1 and 1=2 union select 1,group_concat(column_name) from information_schema.columns where table_schema="maoshe" and table_name="admin"

image.png 这里我们查询到了他所有的列名,最后,我们只需要查询一下他的密码即可破解成功

?id=1 and 1=2 union select 1,password from admin

image.png 最后破解成功,拿到密码hellohack 🎉 🎉 🎉

SQL注入的防护

preparedStatement

首先我们可以通过preparedStatement来进行防护,他可以解决99.9%的注入问题,数据库在处理sql语句时,分为两个步骤,解析和执行,然后SQL注入是在解析的过程中生效的,用户的输入会影响 SQL 解析的结果,我们可以通过使用 PreparedStatement,将 SQL 语句的解析和实际执行过程分开,只在执行的过程中代入用户的操作,而且在set的时候对特殊字符进行转义。这样一来,无论黑客提交的参数怎么变化,数据库都不会去执行额外的逻辑,也就避免了 SQL 注入的发生。

//sql语句
String sql = "SELECT * FROM Users WHERE UserId = ?";
//创建preparedStatement对象
PreparedStatement statement = connection.prepareStatement(sql);
//在这一步替换占位符,并对特殊字符进行转移
statement.setInt(1, userId); 
//执行sql语句
ResultSet results = statement.executeQuery();
验证输入

防护的核心原则是,一切用户的输入皆不可信

  • SQL 注入的攻击发生在输入的时候,因此,我们只能在输入的时候去进行防护和验证;
  • 大部分数据库不提供针对 SQL 的编码,因为那会改变原有的语意,所以 SQL 注入没有编码的保护方案

对所有输入进行验证或者过滤操作,能够很大程度上避免 SQL 注入的出现。 比如,在通过 userId 获取 Users 相关信息的示例中,我们可以确认 userId 必然是一个整数。因此,我们只需要对 userId 参数,进行一个整型转化(比如,Java 中的 Integer.parseInt,PHP 的 intval),就可以实现防护了。

好啦,这就是我对于web安全的一些认识,其实还有满多比较重要的攻击没有谈到,这里主要讲解了XSS与SQL注入,希望能帮助到大家去理解~