Web前端安全之安全编码原则
随着Web和移动应用等的快速发展,越来越多的Web安全问题逐渐显示出来。一个网站或一个移动应用,如果没有做好相关的安全防范工作,不仅会造成用户信息、服务器或数据库信息的泄露,更可能会造成用户财产的损失,因此Web安全问题需要引起广大开发者的重视。接下来的几篇文章中,将会对Web常见的一些攻击以及相关的防范方法进行详细的介绍。
系列文章:
- Web前端安全之安全编码原则
- Web前端安全之跨站脚本攻击(XSS)
- Web前端安全之跨站请求伪造(CSRF)
- Web前端安全之点击劫持
- Web前端安全之文件上传漏洞
- Web前端安全之任意URL跳转漏洞
本文主要介绍了Web前端开发中需要遵守的一些安全编码原则,将会对Web安全以及安全编码原则进行介绍,欢迎大家交流讨论~
1. Web安全
在学习常用的Web安全编码原则以及掌握常见的攻击防范方法之前,我们有必要先了解一下什么是Web安全,以及我们面临的Web漏洞或攻击通常有哪些。
1.1 什么是Web安全
随着Web2.0、社交网络和移动应用等产品的诞生,越来越多的信息都被放到互联网应用上展示,而Web的快速发展也引起了黑客们的强烈关注。黑客们通常会利用操作系统或Web存在的漏洞,对各种各样的互联网应用发起攻击,轻则篡改网页内容,重则盗取重要内部数据,并使得应用访问者受到侵害。因此,作为Web应用的开发者,我们需要了解网站或应用中可能存在的漏洞,并且对各种可能遭到的攻击提前做好防范。
1.2 Web常见的漏洞
在前端面试中,我们可能经常会被面试官问到这个问题,“从在浏览器地址栏输入url到页面展示在我们面前,这个过程中发生了什么?",相信很多同学都能够很快回答上来。但是如果面试官问,"从在浏览器地址栏输入url到页面展示在我们面前,这个过程可能存在哪些漏洞或者会遭到哪些攻击?”,可能就会难住很多同学了。下面给出了一个图,展示了Web中常见的一些漏洞。
总的来说,我们在开发Web网站或应用的过程中,可能面临的Web安全威胁或漏洞包括以下这些:
- XSS跨站脚本攻击
- CSRF漏洞
- 点击劫持
- 文件上传漏洞
- 任意URL跳转漏洞
- SQL注入
- 命令注入
- XXE漏洞
- SSRF漏洞
- 逻辑漏洞
- 信息泄露
- ......
面对这么多漏洞或攻击,我们能够做的,就是在进行架构设计或功能开发时,就应当全面考虑用户、APP/浏览器、后台服务器以及各层通讯之间可能存在的安全问题,并且开展安全防护设计,提前做好防范工作。
由于笔者是前端开发,因此也会重点关注前端的内容,也就是和前端较为相关的漏洞。接下来,将会对前端开发中常用的一些安全编码原则进行介绍,并且在后续的文章中,也会对上述漏洞中的前5个漏洞的攻击和防范进行详细的介绍。
2. 安全编码原则
接下来,将会介绍一些常用的安全编码方法,帮助我们编写符合安全规范的代码。
2.1 登录注册安全
当用户想要访问我们网站或应用时,通常需要做的第一步就是进行注册和登录。而实现登录注册功能时如果没有做好安全防范,很可能会造成用户账户密码信息被盗取。因此,下面主要总结了在实现登录注册功能时需要注意的一些安全要点。
(1)注册时账户密码要求
注册时需要限制用户名合法字符和长度,密码需要禁止使用弱口令,密码长度应当大于8位且包含大小写字母,数字及特殊字符。
(2)登录失败时提示要求
登录失败时不应返回详细提示用户名不存在,防止猜解用户名。
(3)增加验证码机制
单个用户口令失败3次后应有验证码机制出现,验证码每校验过一次应当立即失效防止验证码重用。
(4)不在常用地登录时需要增加身份验证
当用户登录成功时,后台应该记录用户的用户名、IP和时间,当尝试登录IP不在历史常登录IP地理位置时,应进行多因素二次验证用户身份,防止用户因密码泄露被盗取账户。
(5)Cookie安全
Cookie中通常会包含用户的登录态标识,因此为了保障Cookie的安全,需要设置HttpOnly属性以防止被XSS漏洞/JavaScript操纵泄露。此外,实现全站HTTPS后,Cookie应当设置secure属性,使得浏览器仅在安全加密连接时才能传送使用该Cookie。
2.2 访问控制
当用户成功到登录网站或应用之后,接下来就可以开始访问相关的页面。但是我们的系统中通常包含不同身份的用户,比如有超级管理员、普通管理员、教师和学生等身份等,而不同身份的用户可以访问的页面应该是不一样的,因此我们还需要做好访问控制。
(1)权限控制
无论是Web页面还是对外的HTTP API接口,在系统设计之初就需要考虑身份验证和权限校验机制。除了官网静态页或新闻页等公开页面之外,当用户访问其它页面时,都需要添加权限校验机制,仅有权限的用户才能访问相应的服务和数据,防止水平越权和垂直越权。
如下面代码所示,给出了使用Vue进行权限控制的一个例子,我们可以通过添加全局拦截路由,在用户进入页面前先对用户身份进行判断,如果该用户有权限,才让用户访问对应页面。
router.beforeEach(async (to, from, next) => { // 先判断用户是否登录 checkIfLogin(); // 若用户登录成功,需要拉取该用户的信息 getUserInfo(); // 上面两个步骤完成之后,需要判断用户的权限 if (userInfo.role === 1 || userInfo.role === 2) { next(); // 跳转到用户想要访问的页面 } else { alert('您没有权限访问该页面); next('/'); // 跳转到默认页面 } });
2.3 输出转义
经过登录以及访问权限的校验,用户就可以访问到他们想看的页面了。而如Web网站的页面通常是需要经过浏览器的渲染才能最终显示在用户面前,但是在这个过程中,可能也会被黑客注入如XSS的攻击,因此对于输出到网页上的数据我们也需要进行安全防护,也就是对页面上的数据输出进行转义编码。
(1)转义编码
使用HtmlEncode转义特殊字符,比如将“>","<",单引号或双引号等特殊字符进行转义,可以避免从HTML节点内容、HTML属性或JavaScript注入而产生的XSS攻击。下面给出了一个例子,可以实现特殊字符的转义。
const htmlEncode = function (handleString){ return handleString .replace(/&/g,"&") .replace(/</g,"<") .replace(/>/g,">") .replace(/ /g," ") .replace(/\'/g,"'") .replace(/\"/g,"""); }
2.4 输入限制
除了输出的转义编码之外,对于一些需要用户提交信息的地方如表单,我们也需要对用户的输入进行校验和过滤,防止攻击者通过利用XSS等漏洞向服务器或数据库注入恶意脚本。
(1)输入校验
对于不可信的输入来源都需要进行数据校验,从而判断用户的输入是否符合预期的数据类型、长度和数据范围。有效的输入验证一般需要基于以下两点原则:
- 采用正则表达式(正则表达式应该限制^开头和$结尾,以免部分匹配而被绕过)
- 采用白名单思想,因为用户的输入集合是无限的,如果仅仅从黑名单进行过滤,会存在被绕过的可能性。所以应将用户的输入类型、字符集合和长度限制在安全范围之内。
下面给出了常用的一些字段的正则表达式校验防范。
日期:日期格式通常为:2018-12-21,2018-12-21 11:34:22,2017/12/21,2017/12/21 11:33:22,正则表达式参考如下。
(^\d{4}[-/]\d{2}[-/]\d{2}$)|(^\d{4}[-/]\d{2}[-
/]\d{2}\s+\d{2}\:\d{2}($|\:\d{2}$))
域名:域名都由英文字母和数字组成,每一个标号不超过63个字符,也不区分大小写字母。标号中除连字符(-)外不能使用其它的标点符号,完整域名不超过255个字符,正则表达式参考如下。
^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0- 9]{0,62})+(.)?$
邮箱地址:Email地址由"@"号分成邮箱名和网址两部分,其中邮箱名由单词字符、大小写字母、数字及下划线组成,并且可以出现"."号,正则表达式参考如下。
^[.0-9a-zA-Z_]{1,18}@([0-9a-zA-Z-]{1,13}\.){1,}[a-zA-Z]{1,3}$
用户名:用户名通常允许大小写字母、数字和下划线组成,最小6位最大12位的长度,正则表达式参考如下。
^[0-9a-zA-Z_]{6,12}$
手机号:国内手机格式为1开头的数字,长度为11位。
^1\d{10}$
当然,除了我们自己手动编写正则表达式对用户输入进行验证之外,现在一些常用的UI框架如Element UI,这些框架提供的表单控件,已经具备了自动校验的功能。
(2)数据过滤
除了对用户输入的数据进行数据类型等的校验外,对于用户提交的数据,还需要结合业务场景,对可能造成SQL注入、XSS和命令注入中常见的危险字符如"<",">","%”和"&"等字符进行过滤。
2.5 文件上传安全
文件上传现在也是用户在访问网站或应用时经常进行的一个操作,为了防止用户上传恶意文件,我们在实现文件上传功能时,也需要考虑下面这些原则。
(1)身份验证
文件上传前可以增加验证用户身份的步骤。
(2)文件校验
根据业务场景需要,必须采用白名单形式校验文件上传的文件类型,同时还需要验证文件的后缀名,并且限制合适的文件大小。
(3)文件存储
文件应保存在Ceph、对象存储或NoSQL等环境,若保存在Web容器内可能会产生WebShell风险被入侵。
此外,如果使用了第三方存储服务如腾讯云COS进行文件存储时,需要注意权限配置检查,避免由于使用默认配置而导致的文件可直接遍历泄露等问题。
2.6 数据传输安全
数据在网络的传输过程中,攻击者通过一些手段,可能可以获取到传输中的数据信息。因此,在数据的传输过程中,我们也有必要保证数据传输的安全。
(1)采用POST方法发送请求
增、删、改操作必须使用POST方法提交。
(2)采用HTTPS
所有的页面和HTTP API接口都通过HTTPS进行,用HTTPS代替HTTP,当用户以HTTP访问时,可以设置自动跳转到HTTPS。
(3)加密算法选择
如果在通信过程中涉及到使用加密算法,在选择加密算法时,对称加密算法可以使用AES-128以上,公钥加密可以使用RSA-2048以上,哈希算法采用SHA-2以上。
2.7 数据保护
在一些业务场景中,我们可以需要将某些信息存储在客户端或LocalStorage中,因此我们也应该加强对用户数据的保护,防止用户信息或隐私泄露。
(1)不在客户端存储敏感信息
不要在客户端或LocalStorage上明文保存密码或其它敏感信息。
(2)数据脱敏或加密
涉及个人隐私的敏感信息须加密存储并且脱敏后显示给用户。
(3)请求校验
用于标记资源的ID参数不能是数序数字以防止被遍历,对访问资源ID的每个请求做权限校验。
3. 总结
在上面的文章中,首先主要先介绍了什么是Web安全以及常见的Web漏洞,并且在后续的介绍中对平时开发中需要注意的一些安全编码原则进行了介绍。
总的来说,在进行编码时,我们不能太过信任用户的输入,凡是用户的输入需要渲染到页面的地方都需要进行转义和过滤,并在信息提交前做好校验。此外,还需要充分考虑到数据的传输安全和存储安全,并且做好权限的访问控制。接下来就让我们记住安全编码原则,开始编写符合安全规范的代码吧。