信息化新核心课程(NCC) 融媒体专业系列教材
H5
安全开发实践教程
教育部教育管理信息中心◎组编
赵宇◎编著
人 民 邮 电 出 版 社
北 京
图书在版编目(CIP)数据
H5安全开发实践教程 / 教育部教育管理信息中心组编;赵宇编著. -- 北京:人民邮电出版社,2021.5
ISBN 978-7-115-55721-6
Ⅰ. ①… Ⅱ. ①教… ②赵… Ⅲ. ①超文本标记语言—程序设计 Ⅳ. ①TP312.8
中国版本图书馆CIP数据核字(2020)第262620号
组 编 教育部教育管理信息中心
编 著 赵 宇
责任编辑 罗 芬
责任印制 王 郁 彭志环
人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮 编 100164 电子邮件 315@ptpress.com.cn
网 址 https://www.ptpress.com.cn
北京市艺辉印刷有限公司印刷
开 本:800×1000 1/16
印 张:15.75
字 数:254千字 2021年5月第1版
印 数:1–3200册 2021年5月北京第1次印刷
定价:69.90元
读者服务热线:(010)81055410 印装质量热线:(010)81055316
反盗版热线:(010)81055315
广告经营许可证:京东市监广登字20170147号
本书为教育部信息化新核心课程(NCC)融媒体专业系列教材,从前端、通信及服务器端这3个方面对H5开发中的安全技术和技巧进行介绍。
本书共3篇,第1篇主要介绍在H5应用前端开发中对用户输入信息进行验证的方法;第2篇主要介绍H5应用通信方面的安全防护技术;第3篇主要介绍H5应用服务器端的安全防护技术,并结合两个实例对H5应用的安全防护进行整体介绍。
本书适合作为高校教材,供计算机、软件工程、信息安全、网络安全、通信工程、大数据等相关专业的师生阅读。此外,本书也适合从事开发工作的读者阅读,以提高其安全开发技术水平。
主 任:李建聪
副主任:石 凌
策 划:马 亮 彭 澎
总主编:彭 澎 马 亮
融媒体专业系列教材编委:
王 忆 王正言 玉汉友 王 志 王素艳 包 蓉 任秀芹 任鹏涛 刘 敏
李丽丽 杨金虎 肖 平 张临瀚 张 超 陈丽花 赵 宇 胡晓乐 姜 旭
徐 娟 郭 芹 涛 香
组长:
侯炳辉 清华大学 教授
成 员:(顺序不分先后,按照姓氏笔画排列)
吴小华 中国美术学院 教授
张 骏 中国传媒大学 教授
陈 禹 中国人民大学 教授
姜大源 教育部职业技术教育中心研究所 研究员
赖茂生 北京大学 教授
信息技术的飞速发展,对教育产生了革命性影响。以教育信息化带动教育现代化,是我国教育事业发展的战略选择。构建覆盖城乡各级各类学校的教育信息化体系,促进优质教育资源普及共享,推进信息技术与教育教学深度融合,对于提高教育质量、促进教育公平和构建学习型社会具有重要意义。
教育部教育管理信息中心作为教育信息化的实施和技术支撑部门,在教育部网络安全与信息化领导小组和教育部科学技术司的统筹领导下,重点推动面向学生、教师、学校管理的教育管理信息化建设,自2000年起开展了多项信息化人才培训工作,培养了一大批信息化人才,在教育、教学、管理及其信息化支撑保障中发挥了重要作用。
根据《教育信息化2.0行动计划》的有关要求,为全面提升教师和学生的信息素养,我中心于2019年4月着手开展“信息化新核心课程”(以下简称NCC)项目建设,以推进信息技术人才培养工作的转型升级。NCC项目将整合行业优质资源,重点关注新技术,联合高等院校、企业共同建设专业核心课程,并以高等院校学生及相关专业教师为主要培训对象,以促进信息技术与教育教学、教育管理的深度融合为着力点,以推动新技术与岗位职业能力、创业就业技能的应用发展为导向,突出创新性、实用性和可操作性,并逐步建成与之相适应的多层次、多形式、多渠道的新型培训体系。
信息化新核心课程系列教材按照NCC项目建设发展规划要求编写,能满足高等院校、职业院校广大师生及相关人员对信息技术教学及应用能力提升的需求,还将根据信息技术的发展,不断修改、完善和扩充,始终保持追踪信息技术的最前沿。为保障课程内容具有较强的针对性、科学性和指导性,项目专门成立了由部分高等院校的教授和学者,以及企业相关技术专家等组成的专家组,指导和参与专业课程规划、教材资源建设和推广培训等工作。
NCC项目一定会为培养出更多具有创新能力和实践能力的高素质信息技术人才,为推动教育信息化发展做出贡献。
教育部教育管理信息中心
H5最大的特点就是跨平台,在不需要开发者做太多适配工作的情况下,用户只需打开浏览器就能访问H5页面。然而,随着H5应用的普及和推广,H5应用面临的信息安全威胁也日益突出。H5安全问题的产生不仅来自开发阶段,还受通信、用户操作等多方面的影响。为了使H5页面开发者在开发过程中建立起安全意识,了解和掌握H5安全开发的相关理论和影响H5安全的主要因素,了解和掌握H5安全开发的方法和手段,以尽量避免安全问题的出现,教育部教育管理信息中心组织编写了本书。
本书从如何开发相对安全的H5应用入手,全面、系统地介绍H5安全开发的相关理论和方法。本书主要介绍H5输入安全、H5页面设计安全、H5通信安全、H5用户状态保持的安全、服务器架构与安全,以及服务器端的应用安全防护等内容。本书遵循理论结合实际,将复杂问题简单化的原则编写,案例丰富,非常适合教师教学和学生学习使用。
本书从开发的角度对H5应用的安全防护进行阐述,这与以往从安全漏洞修补的角度进行阐述有所不同。本书的内容涵盖了H5应用的前端、通信和服务器端的安全防护,内容相对庞杂,书中如有不足之处,欢迎读者朋友将您的宝贵意见和建议以电子邮件发送至我们的邮箱:luofen@ptpress.com.cn。
致谢:本书从规划、编写到出版,经历了很长一段时间,经过多次修改和逐步完善,最终得以出版。在此衷心感谢教育部教育管理信息中心和人民邮电出版社对本书的编写、出版给予的大力支持和帮助。此外,彭澎教授在全书的编写、创意及整体把控上给予了无私的支持和帮助,长期从事H5开发的Peter Ren为本书的编写提供了大量宝贵的建议和开发经验,在此对彭教授和Peter Ren表示由衷的感谢!
编者
2021年3月
在过去的数年时间里,各种以H5制作的小游戏和“卡片式”营销广告涌入了人们的工作和生活。随着H5应用的普及和推广,H5开发和应用面临的安全问题也越来越多。为此,H5开发必须重视安全问题的预防和解决。本绪论将简要讲解H5中的安全问题,以及H5开发中所涉及的HTML、CSS、JavaScript三大技术中的安全要点,使读者对H5开发中有可能出现的安全问题有初步的认识和了解。
H5是什么?H5会在什么场景应用?H5开发具体使用的是什么技术?在H5开发中可能会出现什么样的安全问题?了解这些问题背后的基本知识是进行H5安全开发的基础。
H5从最初的“惊艳亮相”到“井喷式爆发”只有短短几年的时间,活跃在包括微博、微信等在内的媒体中。H5的作品通常将内容、创意、设计、影视、音频、游戏、娱乐等融为一体,并具有较强的时效性。
如同JavaScript可以被简称为JS一样,在很多人眼里H5是HTML5(第5代 HTML标准)的简称。实际上H5不仅包括HTML5的技术,还包括CSS、JavaScript在内的诸多技术。H5并不是一项独立的技术,也不是一项独立的标准,而是用于实现网页互动效果的技术集合。
H5可以跨iOS、Android等多种操作系统,具有跨PC端、移动端等多种平台进行应用开发的优势,再加上其所具有的实时、自主、差量更新等特性,能够满足用户快速享受最新服务的需求,从而为用户提供良好的应用体验。
H5的应用范围很广,从应用场景角度来看,主要体现在以下两个方面。
1.融媒体
到目前为止,融媒体还没有一个统一的、人们公认的定义。但是,总体来讲,融媒体具有将线上、线下多种媒体形式进行整合,将资源、内容、宣传、利益等融合在一起的特点。融媒体在广告推广、市场营销、媒体宣传、新闻传播等诸多领域有广泛的应用,而H5则是支撑融媒体发展的主要技术。
2.信息系统
在基于浏览器/服务器(Browser/Server,B/S)架构的信息系统中,利用H5可以使图形、统计等的表现形式更加丰富,使用户操作更加便捷。此外,H5具有容易开发、实时更新、维护简单等诸多优点,使信息系统的开发和运维变得更加简单。
图0-1所示的是某企业的信息系统网站登录页面,该网站实质上是利用H5进行展示的响应式网站。
图0-1 某企业的信息系统网站登录页面
扩展阅读
响应式网站:响应式网站区别于传统的以静态页面为主的网站,它可以自动适应不同终端设备的访问,方便用户阅读、导航浏览及进行控制操作,从而提高了用户体验。
B/S架构:这是互联网兴起后的一种网络访问架构。B/S架构将所有系统实现的核心功能全部集中到服务器上,将浏览器作为客户端供用户进行访问和操作。
当前,H5在应用过程中所暴露出的安全问题存在着“一多一少”的现象:基于H5开发的信息系统显露出的安全问题比较多;而基于H5开发的融媒体应用显露出的安全问题相对较少。实际上,无论是基于H5开发的信息系统还是融媒体应用都会产生安全问题,只不过融媒体应用具有较强的时效性,使别有用心者发现其安全问题并可加以利用的时间较短,因此极大地减小了产生危害的概率,但这并不意味着融媒体应用的安全防护可以被忽略。
基于H5开发的信息系统与融媒体应用采用的技术相同,因此,后文介绍的H5安全开发技术,将以信息系统为主要分析对象。有关融媒体应用开发的H5安全开发技术可参照相关内容进行。
1.H5安全问题的成因
H5的安全问题大部分是在开发中产生的。这些安全问题是由技术、人员、制度等多个层面的因素综合形成的。
(1)技术层面
H5作为融媒体应用和信息系统的开发框架[1],包含了HTML5、CSS、JavaScript等多种开发技术和工具,而这些技术和工具存在不同程度的安全隐患与漏洞[2],因此从技术的层面来看,H5面临安全问题的本质就是框架内各种技术存在的安全问题。
(2)人员层面
不少开发者对开发过程中的安全问题不够重视,并且安全开发意识不强,安全开发知识也有所欠缺,从而不能有效防范安全问题。此外,在人员培养阶段,如果对安全开发意识和安全开发知识的培养不足、不够深入,也会导致安全问题的产生。
(3)制度层面
从软件开发的现状来看,一方面开发者的注意力大多放在快速实现软件的功能上,对软件的安全方面不太重视;另一方面不少开发者对安全开发感到力不从心。在这种情况下,如果在软件开发过程中缺乏确保安全开发的制度要求,就很容易导致所开发的软件出现安全问题。
2.H5安全问题的主要表现
H5安全问题一般会出现在客户端、服务器及通信过程等3个环节,并且往往不会孤立地出现在某个环节,而是会同时出现在多个环节。典型的H5安全问题如下。
(1)管理控制台
大多数利用H5开发的应用会存在一个默认管理控制台,而管理控制台往往会依靠某种框架运行。只要别有用心者能够收集足够多的与管理控制台相关的信息,就可能从中发现安全漏洞,然后利用漏洞对管理控制台的接口发起攻击。因此,最稳妥的方法是将H5应用的管理控制台接口与服务隔离,以避免运行过程中的安全隐患。
(2)身份验证和访问控制
许多H5应用是通过Web方式进行身份验证和访问控制的,然而这种方式会带来一些安全隐患。对于身份验证和访问控制,用户既希望身份认证和访问控制具备可靠性、严密性及隐秘性,又希望身份认证和访问控制的过程不复杂,不影响使用的舒适性和便捷性。因此,开发者根据实际情况平衡好上述两方面的关系非常重要。在实际应用中,通常的做法是在用户所能容忍的复杂度的范围内,为用户提供可靠的身份验证和安全的访问控制。
(3)输入参数验证
在H5应用中,通常会通过参数验证来实现身份验证。由于这种验证是通过识别代码规则进行的,而发现这些规则的缺陷往往是别有用心者乐于追逐的目标,因此通过一个安全机制来传递所输入的参数(如身份信息)是非常必要的。输入参数验证所面临的一些常见的安全威胁如下。
·目录遍历攻击
目录遍历是H5应用的服务器或H5应用所执行的程序对用户输入的文件名称的安全性验证不充分而导致的一种安全漏洞,使别有用心者可以利用一些特殊字符达到绕过服务器的安全限制访问任意文件(不局限于Web根目录中的文件),甚至执行系统命令的目的。防范目录遍历漏洞的方法中,最有效的是进行权限控制。
·XSS攻击
跨站点脚本[3](Cross Site Scripting,XSS)漏洞是Web程序中最常见的漏洞之一。XSS攻击是指攻击者向合法的Web页面插入恶意的脚本代码(通常用的是HTML代码和JavaScript代码),当用户浏览Web页面时,插入的恶意脚本代码就会被运行,从而实施恶意攻击。
·CSRF攻击
跨站点请求伪造(Cross Site Request Forgery,CSRF)攻击是发生在客户端(用户)的攻击,指在用户通过浏览器登录网站后(如网站A),并对该网站进行访问时,该用户又使用同一浏览器登录另一个网站(如网站B),如图0-2所示,如果网站B存在攻击性代码,那么网站B中的攻击性代码就会在用户毫无所知的情况下通过浏览器利用用户在网站A的合法身份向网站A发出请求。由于网站A无法判断出该请求不是由用户发起的,因此网站A会根据用户的身份信息依照合理权限请求,从而导致网站B的恶意代码被运行,如图0-3所示。如果此时用户登录的网站A是用户网银的网站,那么网站B的恶意代码运行后就可能在用户未授权的情况下对用户网银中的资金进行操作,这个操作也将被记录为用户的合法操作。
图0-2 用户登录网站A和网站B
图0-3 用户受到CSRF攻击
提示
XSS攻击和CSRF攻击都是跨站点攻击,它们的相同点是不攻击服务器,攻击的都是正常进行网站信息访问的用户。XSS攻击属于实现CSRF攻击的诸多途径之一。
·SQL注入攻击
SQL注入攻击是指通过现有的程序,将不属于程序本身的SQL语句注入后台数据库并对数据库进行攻击的行为。SQL注入攻击的目的是非法获取网站的控制权,因此表单提交、URL提交、cookie参数提交等程序模块往往是SQL注入的主要对象。
此外,H5常见的安全问题还包括会话管理、敏感信息泄露等,这里就不再逐一讲述。
H5应用的安全问题主要来自需求分析、总体设计、代码编写等过程中存在的缺陷,这些缺陷是导致软件性能不高和出现安全问题的主要原因。另外,由于开发过程中使用开源代码、公用框架及模块,因此会有意或无意地引入一些已知或未知的安全威胁。
1.代码开发引入的安全问题
H5应用(融媒体应用、信息系统等)有可能出现的安全问题在前端、服务器端及通信过程中都存在。这些安全问题涉及范围广、种类繁多。出现在前端和服务器端的安全问题,如XSS攻击、iframe风险、网页劫持、内容推断错误、第三方共享代码、静态资源完整性校验缺失、HTTPS访问、本地存储数据泄露等,基本上都与代码开发有关,因此完备的代码开发可以在很大程度上避免这些安全问题的产生。对于通信过程中的安全隐患,则需要采用各种技术手段进行防范。
2.交互设计产生的安全问题
H5开发的安全问题还会因交互界面设计不合理而产生。这是因为交互界面设计不合理会使用户产生误解,当用户产生误解后,往往会导致误操作,从而产生安全问题。
H5应用所面临的绝大部分安全问题与开发过程有关。开发者在开发过程中需要严格遵守开发的安全原则,并在此基础上,对开发中容易出现的安全问题的关键之处,利用安全保障手段进行有针对性的防护处理,只有这样才能开发出一个相对安全的H5应用。
对H5应用构架进行梳理、分析,可使开发者对H5应用的整体有一个清晰的认识。开发者将H5应用架构梳理得越清晰、越简单,就越有助于分析H5应用中的各种安全要素,就越能在后面的开发中做到有的放矢。
总之,分析H5应用的构架有助于从总体上减小H5应用开发中出现软件错误、逻辑缺陷、代码重叠等问题的概率,有助于协调、规范及统一开发者的编程风格,从而避免出现因编程风格不统一而造成的安全隐患。
H5应用设计和开发的安全原则需要从权限管理、安全防护等多个方面进行考虑。
1.与权限管理有关的原则
从表面上看,H5开发的产品,特别是融媒体应用,在大多数人的印象中是不需要权限的。这是由于融媒体应用所面向的环境大多是开放的,对权限的要求并不高。但实际上,融媒体应用不仅包括用户所看到的前端展示出的网页,还包括为产品提供技术支持的网络、服务器等服务。这些服务涉及服务器、通信、用户等方面,这些方面都存在安全问题,而权限管理是非常重要的安全防护手段。
对H5应用的权限管理来说,一般情况下,融媒体应用对权限的要求相对较少,而信息系统对权限的要求比较多。通常,信息系统的权限管理应遵循以下原则。
(1)权限分离原则
权限分离原则是根据实际应用条件和需求将权限分成不同等级,这是最小特权原则的基础。
(2)最小特权原则
最小特权原则是指主体访问权限的最低限度,即仅执行授权活动中必需的那些权利。
(3)最少共享机制原则
最少共享机制原则是指避免因多个主体共享一个资源,而导致资源被其他无关人员通过相同机制获得不应获得的信息的原则。
2.与安全防护有关的原则
安全防护不是仅依靠某一政策或某一设备就能实现的,应当是全方位、有层次、成体系的。安全防护涉及硬件、软件、数据、通信、管理等诸多方面的内容,在此仅就软件自身的防御体系加以说明。
(1)纵深防御原则
应当在信息系统中设置多重的安全防护措施,而不能仅仅依靠某一阻塞点[4]进行防护,特别要利用操作系统自身的安全防护形成纵深防御。
(2)木桶效应原则
保护信息系统中安全防护最薄弱的环节,防止攻击者从“短板”处找到突破口。
(3)攻击面[5]最小化原则
尽量减少信息系统暴露在外部的服务、功能、接口、协议等,以最大限度地降低别有用心者利用其对信息系统进行攻击的可能性。
此外,安全防护原则还包括经济机制原则、隐私保护原则、故障处理原则等,这里就不再逐一展开讲述。
3.心理可承受程度原则
安全是把“双刃剑”。为了安全的有效性,开发者期望对用户的每一次操作都进行身份验证,而在H5应用的实际使用过程中,用户除了对认证有严密和隐秘的需求之外,还有操作便捷的需求。如果因烦琐的安全防护操作使资源获取变得困难,就会导致授权用户不再愿意使用该系统,或运维人员被迫停用一些安全防护措施,从而使H5应用失去一些安全防护功能。在H5应用开发过程中应平衡好用户心理承受程度和安全防护功能强弱程度之间的关系,这就是H5应用开发中应遵循的心理可承受程度原则。
4.输入审核原则
H5应用输入验证除了要保证业务功能的合理性,还要保证信息的安全。在H5应用与外界交互的防线中最为重要的就是检查H5应用所接收到的每一条数据,无论是用户输入的数据,还是程序间进行交换的数据,都必须通过严格的输入审查,以避免恶意数据的引入,或者将其控制,不让恶意数据发挥作用,从而保障H5应用的安全。
开发者在开发过程中应该对应用中预期的输入数据采取不信任(甚至可以认为所有要输入的数据都是怀有恶意的)的态度进行安全开发工作,使开发出的系统能够做到有效地对数据输入进行严格的审查。有关这方面的内容,将在后文详加阐述,这里就不过多介绍了。
5.输出过滤原则
在H5应用与外界交互的防线中,除了要重视H5应用所接收到的每一条数据,还要重视H5应用会反馈什么信息。因为在反馈的信息中往往会夹杂着与H5应用的操作系统、版本、所使用的语言、传输协议、数据库等重要信息,所以这些信息一旦被别有用心者利用,就会给H5应用带来严重的安全威胁。因此,对预期和非预期输出的信息进行过滤处理,防止重要信息外泄而导致系统安全受到威胁是安全设计的重要原则之一。
在H5应用的开发过程中,不同开发阶段会采用不同的安全措施。即便如此,开发出来的H5应用还需要通过各种调试和测试来验证其是否安全。
1.代码调试
代码调试是指在代码投入运行前,将编写的代码或者代码片段,通过手工或自动脚本、编译等方法对其进行调试,根据调试所发现的问题,进行深入的分析,找出问题的产生原因和具体位置后对其进行修正。这是保证代码正确性不可或缺的步骤之一,也是开发者必备的基本功之一。
2.程序测试
程序测试分为程序代码测试、程序压力测试和程序安全测试等3种测试方式。
(1)程序代码测试
程序代码测试是发现产品内在错误和缺陷的主要手段,是产品开发周期内不可缺少的重要工作,是对一个完成了全部或部分功能模块的程序在交付前的检测,以确保其能够按预定的计划被正确执行。为了发现其中的错误,在进行程序代码测试时应竭力设计一些能够暴露出错误的测试用例。测试用例一般是由测试数据和预期结果构成的,好的测试用例是那些有可能发现不易被发现的错误的测试用例。
(2)程序压力测试
所谓程序压力测试就是给软件不断增加压力,使之在极限情况下运行。程序压力测试是对程序响应时间、并发用户数、吞吐量、资源(如内存、CPU可用性、磁盘空间及网络带宽等)利用率等性能指标进行监测,在得出测试结果之后对测试结果进行分析,找到影响性能的“瓶颈”,并加以解决。
提示
程序压力测试应当在实际应用环境下进行,否则程序压力测试无法得到真实的反馈信息,从而无法有的放矢地对程序进行优化处理或结构调整。
(3)程序安全测试
程序安全测试主要包括程序、数据库的安全性测试。安全指标不同,测试策略就会有所不同。如对用户进行安全认证方面的测试时,测试方案需要考虑口令[6]安全策略,包括用户登录账户的口令的安全性(口令是否可见或是否可被复制),以及用户权限划分等问题。对数据库进行测试时,则需要考虑数据的机密性、完整性、独立性、可管理性,以及容灾与恢复能力等。由此可见,测试内容不同、测试指标不同,所采取的测试策略和测试技术也不尽相同。
3.代码审计
代码审计以发现安全漏洞和不规范程序为目标,对源代码进行分析核查,通常是先分析出有问题的地方,然后找出有问题的代码。
在进行代码审计前,代码审计者要知道所有的代码,并了解代码中哪里是不会有问题的。代码审计者的主要精力放在对源代码的全面分析和有可能出现安全问题的地方,目的是发现有“bug”的代码、逻辑错误、安全漏洞,以及不规范的代码。
代码审计对象往往会被划分成不同的风险等级。代码审计的基本原则是先审计高风险对象,然后再审计低风险对象。代码审计对象的风险等级的划分不是一成不变的,会随着时间、环境的变化,以及开发的深入程度发生改变,可能会由高变低,也可能会由低变高。
对代码审计来说,尽早地、逐步地进行代码审计,比软件交付后再开始代码审计更为合理。开发者可以采用“自己对自己开发的系统进行攻击”的方式对自己开发的程序(或编写的代码)进行审计,或采用一些辅助工具对程序(或编写的代码)进行审计。
专业的代码审计服务通常会从以下3个层次进行。
(1)基础代码审计服务
基础代码审计服务利用源代码安全审计检测工具和人工检测,扫描和分析已有的代码,对导致安全漏洞出现的错误代码进行定位和验证,并提供补救建议。
基础代码审计服务适用于以C、C++、C#、Java、VB、VB.NET、ABAP等语言开发的应用,以及以Ruby、PHP、AJAX和Perl等Web技术编写的应用。
(2)中级代码审计服务
中级代码审计服务在基础代码审计服务的基础上,为用户提供以下服务:
➊ 针对所发现的代码安全漏洞做进一步分析;
➋ 结合技术和业务特性,给出安全漏洞等级评估建议。
(3)高级代码审计服务
高级代码审计服务在基础代码审计服务和中级代码审计服务的基础上,进一步为用户提供以下服务:
➊ 针对等级较高的安全漏洞进行技术分析;
➋ 指导用户完成漏洞修复;
➌ 对漏洞修复结果进行再次扫描确认;
➍ 定制安全编程培训服务。
4.等级保护
等级保护是为促进信息安全,从管理与技术两个方面开展的信息安全保护工作。目前已经进入了等级保护2.0阶段。我国法律法规中明确规定:所有的信息系统都要进行等级保护的定级备案,并进行相应级别的测评工作。因此,在H5应用的开发过程中,开发者应按照等级保护2.0中关于访问控制、身份鉴别、入侵防范、恶意代码防范等相关技术的安全要求对所开发的H5应用进行安全防护。
开发者应当遵守良好的代码编写规范。虽然代码编写是否规范与代码最终运行的结果没有必然联系,但在代码开发过程中,良好的编写规范对提高代码阅读效率,有效发现代码中存在的错误和安全问题是很有帮助的。代码编写最基本的原则是保持代码的一致性,即无论使用哪种风格编写代码,都应确保风格始终如一。通用的代码编写规范如下。
1.缩进排版
缩进排版是指代码编写过程中,无论是编写HTML代码、JavaScript代码,还是CSS代码,每一级嵌套都有一个制表符用于缩进。缩进排版能使整个代码的层次、嵌套关系清晰明了,能有效避免代码嵌套关系出错。
从图0-4与图0-5所示的代码可以看出:图0-4所示的代码编写相对规范,排版清晰明了,代码的可读性更强;而图0-5所示的代码前后风格不一致,且第59~63行代码编写不规范,让人无法快速判断出标签的嵌套关系,导致代码的可读性较差。
图0-4 某网站首页的部分代码1
图0-5 某系统首页的部分代码2
为保证代码的可读性,建议使用专业编辑器编写代码,因为专业编辑器可以自动完成代码的排版工作。如图0-6所示,专业的编辑器可以自动缩进代码,同时将代码中的关键字、变量、注释等都用不同颜色进行区分,使开发者可以快速阅读代码,并且更易于发现代码中的编写错误。另外,建议尽量不使用默认的制表符设置,将制表符的宽度设置为2或4,也不建议混合使用制表符与空格符两种方式进行缩进排版。
图0-6 专业编辑器的代码排版效果
2.区分字母的大小写
字母的大小写对使用C、C++、C#、Java、JavaScript等语言编写的代码来说非常重要,在这些代码中,相同的字符串,字母的大小写不同,其所代表的含义是不同的。虽然HTML和CSS不受代码字母大小写的限制,但仍推荐对HTML中的元素、属性名称,以及CSS中的选择器、属性名称均采用小写字母。如username和Username在HTML和CSS中被认为是相同的变量名,但在JavaScript中代表的则是两个不同的变量名。
3.变量与文件的命名
不论是HTML还是JavaScript,对变量而言,注意变量的命名方法,可以提高代码的可读性。常见的变量命名方法有匈牙利命名法、骆驼命名法及帕斯卡命名法等多种命名方法。
对文件名而言,建议命名时,对其中的英文部分全部采用小写字母;文件名可以使用连字符“-”或者下划线“_”等符号,如“c-101.html”“my_order”;尽量避免使用特殊字符,特别是有特定含义的非字母字符,如“!”“@”“%”等。
此外,不建议以软件版本号作为文件名。如在<script src="js/jquery/ jquery-1.9.1.min.js"></ script>中,jquery-1.9.1.min.js文件名含有软件的版本信息。这种命名方式的优点在于通过文件名就可直接获知软件的版本,缺点在于攻击者可以轻而易举地获得文件的版本信息,因此不建议使用。建议采取这样的命名方式:
<script type="text/javascript" src="//www.xxx.xx/js/global/ jQuery_latest.min.js"> </script>。其中,文件名jQuery_latest.min.js不包含明确的软件版本信息。虽然这样的命名方式会提高文件版本的控制成本,但也提高了攻击者的攻击成本,有助于信息安全防护。
对于上述编写规范,在实际工作中,开发者还需根据实际情况酌情处理。上述建议,仅供开发者参考。
注释
[1]框架:是软件框架的简称,软件框架是为了实现某个业界标准或完成某种特定基本应用功能的组件规范,同时也指为完成组件规范而提供的软件产品。
[2]漏洞:漏洞是由于计算机网络硬件、软件、协议中存在的设计或实践缺陷造成的。漏洞使得攻击者能够利用其在未授权的状态对系统进行访问、操作。
[3]跨站点脚本:本应缩写为CSS,但层叠样式表“Cascading Style Sheet”的缩写也为CSS,为示区别,故通常将其缩写成XSS。
[4]阻塞点:信息安全中的阻塞点是网络系统对外连接的通道,是监控连接的控制点。
[5]攻击面:软件环境中可能会被未授权用户(攻击者)输入或提取数据而受到攻击的点位。
[6]口令:口令即通常所说的登录密码,它相当于一把钥匙,在网络中通常用于用户登录。严格意义上的密码 是一种用来混淆信息的技术,通过这样的技术可以将正常的(可识别的)信息转变为无法轻易被识别的信息。
希腊神话“特洛伊战争”中那只“献给雅典娜的木马”,对特洛伊人而言,是导致城毁人亡的“输入性错误”——隐藏在木马肚子中的希腊士兵半夜打开城门,与城外的希腊军队里应外合,将久攻不破的特洛伊城攻破。
同样地,H5应用的前端防护也必须重视对输入信息的严格审核,避免系统被别有用心者从前端“攻陷”。就当今信息的重要性而言,信息泄露的后果很有可能堪比“特洛伊灾难”。
在前端页面中,用户的信息输入是否安全对整个H5应用的安全有很大的影响。在H5应用中,如果没有对用户输入的信息进行安全防护,H5应用就会像特洛伊城一样被别有用心者“攻陷”。特洛伊人如果对木马进行必要的检查,就会发现藏在木马中的希腊士兵,从而避免惨剧的发生。
本章主要介绍如何对H5前端页面内用户的信息输入进行安全验证,帮助读者了解关于信息输入的安全防护手段,并能够将其应用到程序开发中。本章主要内容如图1-1所示。
图1-1 本章主要内容
H5前端的主要支撑技术是HTML5、CSS及JavaScript,因此对H5前端安全漏洞的防范也主要是针对HTML5、CSS及JavaScript展开的。
HTML5是在HTML 4的基础上“进化”而来的,虽然HTML5的发展经历了不少曲折,但对整个行业和应用开发者来说,它代表了未来几年所能够依赖的技术。无论是移动电话、游戏控制台、汽车仪表板,还是物联网设备等都用得上HTML5,因此其应用是非常广泛的。
作为H5应用框架的重要组成部分,HTML5在H5应用框架中的作用主要体现在网页的结构(Structure)上,其决定了前端网页的结构和内容。HTML5常见的安全问题主要包括以下两个方面。
1.浏览器对HTML5的支持所产生的安全问题
浏览器对HTML5的支持不是绝对安全的。如2013年3月,一位开发者就发现了HTML5的一个漏洞。这个漏洞是浏览器允许网站利用垃圾数据对客户端展开“轰炸”,甚至可在短时间内将硬盘塞满。当时Safari、Chrome、IE等多款主流浏览器均受到了此漏洞的影响。
2.HTML5设计开发中产生的安全问题
HTML5的代码虽然简单,但是在设计过程中如果存在设计缺陷或者未考虑适用的环境就会引发安全问题。
(1)存在设计缺陷
HTML5在安全方面的设计缺陷主要是指开发者在开发的过程中没有对相关的安全问题加以考虑或考虑不周,而引发安全问题。如果在设计过程中开发者没有考虑到对用户输入的信息进行特殊字符限制,或者没有对用户的输入信息进行转义处理,就可能产生易被XSS攻击的安全漏洞。
(2)未考虑适用的环境
在HTML5开发中应该考虑应用的适配环境——PC、平板电脑、手机等设备,如要考虑用户在不同设备所使用浏览器的默认设置等。用户在登录系统时经常会被问及是否要保存用户名、登录密码等信息,这其实是源自浏览器的表单自动填充功能:当用户点击“提交”按钮时,浏览器会捕捉并记录用户所填的信息,当用户再次浏览该网页时,浏览器就会自动为用户填充相应信息。此功能还可为用户记录下曾经查询过的关键词,使用户无须重新输入关键词就能直接进行之前做过的查询操作。这个功能很人性化,在网络中用各种搜索引擎进行电商物品查询时非常实用。但如果将此功能应用到用户的用户名、登录密码这些涉及用户账户安全的信息中,就会产生安全问题。如用户A在登录时将自己的用户名、登录密码保存到了浏览器中,那么稍后使用同一台计算机、同一个浏览器的用户B就可能在不知道用户A的登录密码的情况下,利用该浏览器的表单自动填充功能登录用户A的账户,从而出现非授权登录的安全隐患。
层叠样式表(Cascading Style Sheets,CSS)是一种用来表现HTML或XML等文件的样式,是在HTML标准之外创造出的样式(Style),用来解决不同浏览器之间页面展示效果的问题。作为H5应用框架的重要组成部分,CSS决定了网页的表现样式,解决了网页“是什么样子”的问题。
在很多开发者的认知中,CSS就是设置样式的,所以对CSS没有过多安全方面的考虑。实际上,CSS不仅可以静态地修饰网页,还可以结合脚本代码对网页元素进行修饰。从本质上看,CSS在应用开发中的作用更接近于脚本,而且同脚本一样,其适用范围也覆盖了整个页面。目前,CSS的功能有删除、添加、修改页面信息,根据页面信息发起请求,响应多种用户交互等。虽然上述功能原则上不会引起安全漏洞,但不能因此就忽视有些CSS代码已经具有的修改本地存储和运行挖矿程序[1]等功能。
1.CSS并不安全
到目前为止,由于CSS危及安全的例子还不是很多,但由CSS引发的安全漏洞通常比较隐蔽。如曾经在Chrome、Firefox等浏览器中出现过一个旁路攻击[2]的CSS漏洞,它能够对跨来源框架的视觉内容进行泄露。出现这个漏洞的原因是2016年CSS3 Web标准中引入了名为“mix-blend-mode”的特性,它允许Web开发者将Web组件叠加在一起,并添加了控制混合效果。当用户访问存在该CSS漏洞的网站时,就会被别有用心者通过跨域iframe获取用户的信息,如用户名、照片等信息。别有用心者获取用户信息的整个过程无须与用户进行额外的互动。
还有一些CSS安全问题正在(有可能)形成,图1-2所示的是GitHub上的关于CSS Keylogger的介绍。这个插件可利用CSS属性收集器,在加载网页背景图像的时候,从外部服务器请求资源。把CSS Keylogger这个插件安装到Chrome浏览器上后,当用户打开一个使用了控制组件框架网站的网页时,点击浏览器窗口右上角的“C”图标,输入口令,Express服务器将会捕捉到用户所输入的口令。
图1-2 CSS Keylogger在GitHub的说明
从公开的资料看,CSS Keylogger目前只是以浏览器插件的形式存在,需要人工安装,其行为还处于用户控制之下。CSS Keylogger的出现引起了巨大反响,有人建议浏览器厂商修复漏洞,有人对其进行研究,但这都回避不了一个问题,即CSS正在变得不安全。
2.如何防范CSS的安全问题
对于CSS的安全问题,目前还没有相对成熟的防范方法,但在CSS引用时可以从技术方面加以防范。
(1)特别注意外部资源的请求
要仔细查看那些用于请求外部资源的代码,认真查看其请求的资源是否是安全的。特别要查看那些外部统一资源定位符(Uniform Resource Locator,URL)的请求,因为这些外部URL所指向的资源具有不确定性。CSS文件中引用的外部资源一旦被第三方删除,就会使当前页面的实现效果达不到预期需求;或者因CSS文件所引用的外部资源被替换,使得与CSS Keylogger类似的插件在用户毫无察觉的情况下被引入,从而出现安全问题。
(2)外部资源的本地化
为解决请求的外部资源不确定的问题,务必将CSS文件中请求的外部资源下载到本地服务器后再使用,而不是简单地直接指向第三方链接。不只是CSS文件,但凡涉及外部资源且不可控的JavaScript文件、图片文件都应当如此处理。
(3)规避不能本地化的外部资源
有些资源是难以避免外部请求的,如Google的font资源。如果要使安全保障得更好一些,最好尽量避免引入这些资源。没有Google的font资源,其结果仅仅是网页字体的显示效果差一些而已。
(4)慎重对待第三方CSS代码
开发者经常会使用第三方生成的CSS代码。这些第三方代码在投入使用之前,开发者应认真阅读其中的每一行代码,尽管因排版等原因使得代码的可阅读性可能很差。
(5)特别注意脚本的应用
要仔细查看源代码中那些包含脚本代码的部分,认真分析其所运行的脚本是否安全。
(6)精简代码
如有时间、精力,尽量删减CSS中不被引用的代码和内部注释信息。
JavaScript与Java从字面上看很有渊源,但实际上,Java是Sun公司的编程语言,而JavaScript是Netscape公司和Sun公司联合推出的。从运行方式、定位及数据类型等方面来看,JavaScript和Java是两种完全不同的语言。JavaScript作为一种网络脚本语言,已经被广泛应用于H5应用的开发,解决了“做什么”的问题。
1.JavaScript的安全问题
JavaScript解决的是“做什么”的问题,因此JavaScript代码若出现安全问题,其危害性远比在HTML和CSS中所出现的安全问题大。JavaScript的安全问题主要体现在以下两个方面。
(1)JavaScript代码泄露问题
由于JavaScript代码可以被下载、解读,因此很容易被别有用心者加以利用,其在编写中经常会出现安全问题。
为防止JavaScript代码被别有用心者解读,使用代码混淆的方法对代码进行处理是目前比较好的解决办法之一。通过代码混淆,JavaScript代码如同被加密一样变得难以读懂,这就增加了解读代码的难度。严格来说,代码混淆只是让代码的可读性变差,起不到防止代码被下载、解读的作用,因此建议在对JavaScript代码进行混淆的同时,应将其中涉及敏感信息的代码放到服务器中运行。
扩展阅读
代码混淆是一种以别人看不懂为目的的技术手段。实际上,不仅JavaScript代码需要进行代码混淆,HTML代码和CSS代码同样需要进行代码混淆。需要指出的是,代码混淆无法从根本上解决安全问题,只是提高了代码解读的成本。此外,代码混淆会增加程序运行的开销,降低应用的运行效率。
(2)开发中的安全问题
JavaScript代码在开发设计中如果存在不严谨的地方,也会导致安全问题。JavaScript常见的安全问题包括前文提及的XSS攻击、CSRF攻击,还包括URL重定向、客户端JavaScript cookies引用及JavaScript劫持等。
在开发过程中,由于需要考虑跨浏览器兼容问题,以及如何满足AJAX更好的特性需求等,很多开发者在使用JavaScript开发时都会引入第三方的JavaScript代码库。这些被引入的代码库的成熟度一般较高,存在的安全漏洞相对较少,引入这些代码库也避免了开发者编写同样功能的代码时可能引入的安全问题。不可否认,由于这些代码库进行了代码压缩,致使代码的可读性较差,人工阅读、审核代码变得极为困难,使一些安全问题难以发现。因此在实际工作中,通常采用的方法是使用JavaScript自动检测工具进行检测,并通过人工进行核验和修复。
2.JavaScript在安全中的作用
JavaScript在H5的框架中主要负责控制网页的行为,如用于生成动态HTML页面,对浏览器事件做出响应,读/写HTML标签,识别客户端浏览器信息,在信息被提交到服务器前进行验证,对cookies进行创建、修改等操作,以及基于Node.js技术编程等。因此,考虑到安全防护、设计完善的JavaScript代码会对H5应用的安全防护起着重要的作用。
对前端防护而言,JavaScript代码可以对用户输入的信息实现有效核查,以确保输入信息的有效性、合规性。下面以代码1-1为例,介绍在用户登录页面中如何实现用户输入信息的验证,以确保用户输入的信息不能为空。
代码1-1的第24~40行是嵌入HTML文件的JavaScript代码,该代码实现的主要功能是判断所输入的用户名(Username)与密码(Password)是否为空。其实现机制为:当用户点击“submit”后,在用户输入的信息提交给服务器前判断信息是否为空。
在代码1-1中,document.getElementById(ID)语句的功能:获得网页中相应的ID信息后,通过字符串比较得到是否为空的判断结果,并进行相应的处理或反馈。如在用户名信息为空的情况下直接点击“submit”时,该用户登录页面就会弹出“Please input Username”(请输入用户名)的提示,如图1-3所示。
图1-3 请输入用户名的提示
此外,除了使用JavaScript代码来确保非空输入以外,在实际开发中其实还有很多非常好的解决方法,这里不赘述。
3.调试工具的应用
由于JavaScript在报错这方面做得不是很好,因此在编写Web页面代码的时候,熟练使用调试工具,对页面的修改有很大的帮助。比较好用的调试工具有Firefox浏览器中的Firebug。下面以代码1-1的调试为例,介绍Firefox浏览器中的调试器和控制台的应用。
➊ 启用浏览器,访问代码1-1编写的网页后,按快捷键F12,或通过“菜单”中的“Web开发者中的调试器”,打开图1-4所示的调试器。
➋ 浏览器的底部出现3个分栏,在左侧栏的“来源”中选择当前的页面,中间栏就会出现当前页面的源代码及相对应的行号。
➌ 双击图1-4所示中间栏的if(form.uid==null || form.uid.value=="")对应的行号28,在右侧栏的断点处会自动增加该行作为调试的断点。
图1-4 Firefox浏览器的调试器
➍ 如果希望监视变量或者表达式,则可在相应的位置添加相应的监视内容。
➎ 设置完毕后,在打开的这个网页的用户名和密码输入框中输入相应的信息,点击“提交查询”按钮,浏览器在运行JavaScript代码时会自动停在断点处让开发者进行观察、调试,直至问题解决。
此外,开发者可以通过控制台,直接对变量或者表达式进行查询验证,如图1-5所示。
图1-5 使用控制台进行代码调试
思考与提示
本书中非特意提到的浏览器均默认为Firefox浏览器。调试器是Firefox浏览器中Web开发者中的工具(Chrome浏览器中也有功能与之类似的调试器,打开其调试器的快捷键也是F12,读者可以根据需求进行选择)。需要说明的是,本书除了介绍浏览器的Web开发者之外,还会介绍一些其他的调试工具。
在对漏洞进行分析后发现,大多数漏洞产生的原因在于H5应用中没有对输入的信息,特别是没有对用户输入的信息进行安全检查与验证。当这些信息作为参数被直接提交给系统后,就有可能产生安全问题。因此,在进行前端开发时,对信息进行验证将会有效减少诸如XSS、SQL注入等安全漏洞的产生。
对于输入信息,通常采取两种方式进行安全验证,即黑名单验证和白名单验证。
(1)黑名单验证
开发者先建立一个黑名单列表,如单引号、双引号、反斜线等字符作为非法输入字符,将这些字符放入一个名单列表中,完成黑名单列表的制作。接着进行黑名单验证,将输入的信息与黑名单中的信息进行对比,通过对比结果来判断输入的信息是否安全。
(2)白名单验证
白名单的概念与“黑名单”相对应,白名单中存放的是被允许执行的规则等信息。当用户输入的信息符合白名单的规则时,就会被允许通过。在用户输入的信息不可预知,但输入的信息又符合规则的情况下,往往采用白名单验证。
在开发过程中,通常会采取以白名单验证为主,结合黑名单验证的方式进行安全验证。在H5前端利用白名单、黑名单进行的安全验证,其主要实现路径如下:
➊ 利用input标签的各种属性进行安全验证;
➋ 利用form标签作为阻塞点进行安全验证;
➌ 利用用户键盘输入操作进行安全验证。
input标签是HTML5的重要标签之一,主要用于信息的录入。由于H5应用的使用者(普通用户和别有用心者)的使用目的未知,使用者的操作熟练程度未知、使用者录入的信息不可知,因此,如果没有针对input标签的安全验证,就非常容易造成XSS漏洞、SQL注入漏洞、文件上传漏洞等安全问题。通常情况下,可通过JavaScript对input标签进行安全验证,或通过input标签的自身属性进行安全验证。在这两种方法中,通过input标签自带的属性进行安全验证更为简单和安全。因此,熟练掌握和巧妙应用input标签所具有的属性对用户输入信息的验证工作有着较大的帮助。input标签有很多常用属性,如表1-1所示。
表1-1 input标签常用属性
续表
在1.1.1小节中,就浏览器的表单自动填充功能进行过分析,指出了其中可能存在的问题。解决这个安全问题的方法有两种。
1.手动关闭浏览器的自动填充功能
访问时,手动关闭浏览器的自动填充功能后,所有用户信息都不能进行自动填充,因而这种方法在有效避免口令自动填充的安全隐患的同时,也给用户带来不能自动填充信息的不便。
2.通过程序提示浏览器关闭自动填充功能
在网页中通过代码主动向浏览器声明某些需要保密的表单不要被记录,这就可以保证用户享受自动填充便利的同时,也解决了用户隐私的安全问题。
在保证了诸如口令等用户隐私信息不被自动填充的前提下,第2种方法显然更为人性化、更合理,且亦具有良好的可操作性。实现该功能最简单的一种方法就是利用input标签中,HTML5标准下新增的autocomplete属性,如代码1-2所示。
在代码1-2中,涉及用户输入的部分为第9~11行:
<input type="text" name="uid" placeholder="username" />
<input type="password" name="pwd" placeholder="password" autocomplete="off" />
它们的差别在于name为“pwd”的input标签使用了autocomplete属性,且取值为“off ”,即声明不允许浏览器对此标签使用表单自动填充功能,以保证该标签中的历史信息不会被浏览器所记录。name为“uid”的input标签则没有类似的声明,即输入过的信息会被浏览器记录下来。如图1-6所示,“a.com”和“b.com”都是在浏览器中输入过的用户名信息,则这些信息会被当作该网页input标签的信息显示出来。
正确设置autocomplete属性可以将安全风险降低到可接受的程度。在这里需要说明的是,在使用input标签时,对于口令、身份证号、银行卡号,以及用户名、电话这些涉及个人隐私和财产安全的信息,应当将autocomplete取值为“off ”,对其他信息的input标签而言,将autocomplete取值为“on”,即表示允许浏览器使用自动填充功能,这会便于用户使用。
图1-6 表单自动填充功能的效果
思考与提示
图1-7所示为某运维审计系统的用户基本信息输入界面(系统本身是基于HTML 4标准开发的)。其页面上需要进行各种信息的输入,如果使用HTML5标准进行代码编写,每个input标签的autocomplete将如何进行设置?读者可以尝试编写input标签的相关代码。
图1-7 某运维审计系统的用户基本信息输入界面
当用户进行信息提交的时候,如果不对用户输入的信息是否为空进行判断,在信息处理时可能会造成H5应用崩溃或信息泄露等。
input标签的required属性主要用于判断页面中当前input标签是否有输入信息。当required的值为“required”时,如果input标签中的信息为空,浏览器则自动向用户发出输入信息的提示。代码1-3就是实现这一功能的示例,其效果如图1-8所示,浏览器向用户发出了“请填写此字段”的提示。
图1-8 需要输入用户名的信息提示
在代码1-3中,判断输入的信息是否为空值的功能是由第15~18行代码通过required属性的设置来实现的,对比同样功能的JavaScript代码(见代码1-1中的第 24~40行)可以发现,在确保实现相同效果的情况下,应用HTML5的input标签属性能够有效减少代码数量,同时也间接地避免了应用JavaScript代码可能引入的安全隐患。
需要开发者注意的是,由于目前各浏览器对HTML5的支持并不统一,request属性没有被所有浏览器支持,如IE 8。如果有IE 8应用的场景,就必须采用JavaScript进行编写。这在使用其他HTML5新标签和标签的新属性时也应当加以考虑。有关浏览器兼容的问题,会在2.3.2小节中进行介绍。
input标签中type属性的主要作用是设置input标签的输入类型。HTML5为type属性增加了一些新的属性值,新增的属性值主要用于判断输入的邮件、URL之类的单一属性数据的格式是否正确,因此熟练掌握这些属性值并加以运用,对提高代码效率、增强安全性是十分必要的。Input标签type属性值如表1-2所示。
表1-2 input标签type属性值
续表
很多H5应用会将邮箱地址作为用户名,因此在用户登录应用时就必须对邮箱地址进行验证,这一功能可以通过input标签的type属性来实现。代码1-4所示为通过设置input标签的type属性值为“email”,对用作用户名的邮箱地址进行格式验证。
在代码1-4中,第15~17行代码的功能就是利用属性值为“email”的type属性对input标签中的用户名进行验证。通过“email”这个type属性值进行白名单验证,能够有效判断邮箱地址的格式是否正确。如图1-9所示,当输入“a.com”后,浏览器通过对邮箱地址的格式进行检查,向用户发出“请输入电子邮件地址。”的提示。
此外,该属性值还可以利用黑名单,对有可能引起XSS注入的字符发出“请输入电子邮件地址。”的提示,防止XSS注入的发生。
图1-9 用户登录页面
思考与提示
input标签的type属性虽然有一定的安全验证功能,但仅凭type属性进行验证,容易造成误判或疏漏。如针对type属性值为“email”的input标签,虽然输入“a.com”“@ com”“<a@com”“a@com&”都会被视为非法邮箱地址,但如果输入“a@com”就可以通过验证。这也是很多开发者仍倾向于使用JavaScript进行验证的原因。
一般利用type属性值对输入信息进行验证,但由于type属性值无法对各种类型的输入信息进行验证,因此需要利用正则表达式对可以被规范化的输入信息进行验证。
正则表达式是用于描述搜索模式的特殊文本字符串,正则表达式可以被视为某种通配符,正则表达式常被用来验证、检索、替换一些符合某个模式(通配规则)的文本。如^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$就是正则表达式。在正则表达式中,包含下面两种字符。
1.普通字符
在正则表达式中,仅能够描述自身的字符被称为普通字符,如所有的字母和数字。换言之,普通字符只能够匹配字符串中与其相同的字符。
2.特殊字符
在正则表达式中,有些字符被规定不按照字符自身的值进行匹配,而具有特殊的语义。如“.”被规定可以匹配任意的单个字符,而不是仅仅匹配“.”。这些字符被称为特殊字符,也称元字符。
特殊字符使正则表达式具有了验证、检索、替换的功能。正则表达式中常用的特殊字符如表1-3所示。
表1-3 正则表达式中常用的特殊字符
续表
思考与提示
思考在浏览器的控制台中输入下面的代码,会有什么样的结果?
在Firefox浏览器的控制台中输入:
var str='a b c';//使用空格切割字符串
alert(str.split(' '));//()方法
var reg=/\S/g;//使用任何非空白字符查询
alert(str.match(reg));
var reg=/[\w]/g; //使用字母、数字、下划线进行匹配查询
alert(str.match(reg));
常用的正则表达式如表1-4所示。
表1-4 常用的正则表达式
图1-10所示为一个用户注册的示例页面,其实现代码如代码1-5所示。在页面中提示为“telephone”的input标签设置手机号码的输入规则。手机号码虽然是由11位数字组成的,但由于13×、18×、14×、15×、17×等各个号段之间并不是连续的,因此还需要利用正则表达式进行验证。
图1-10 代码1-5编写的页面的效果展示
代码1-5中第22~25行代码的功能就是利用正则表达式对用户输入的手机号码进行验证。
需要说明的是,name为“number”的input标签,其中的type属性值设置为“text”“number”和“tel”时,均可以通过正则表达式对手机号码的输入进行验证,但实际效果都有所欠缺。
如果type值设置为“text”,用户在移动设备输入手机号码时,系统将自动弹出英文键盘,而不是预期的数字键盘,需要用户自己将英文键盘切换到数字键盘,就会使用户体验不佳。
如果type值设置为“number”,用户在移动设备输入手机号码时,系统虽然会自动弹出数字键盘,但是对手机号码的验证还需要相应的JavaScript代码,以便对输入信息中的小数点进行判断处理。此外,在iOS操作系统中弹出的不是九宫格形式的数字键盘。所以,这也不是一个好的解决方法。
如果type值设置为“tel”,用户在移动设备输入手机号码时,系统将自动弹出带字母的九宫格键盘,视觉上远不如纯数字的九宫格键盘让用户感到舒服。
权衡上述3种取值的结果,本示例最后还是将input标签的type属性值设置为“tel”。
代码1-5中还运用了input标签的min、max及patten等多个属性。这些属性同样可以对输入信息进行验证,利用这些验证功能能够实现:name对“uid”的input标签的输入进行非空核查;name对“email”的input标签中输入的邮箱地址的格式进行核查;name对“age”的input标签中输入3~99岁的年龄进行核查。由此可以看出,利用input标签的type属性,可以使验证变得更加简单、高效,提高了网页的安全性。
思考与提示
这里需要提醒读者的是,从另一个角度来看代码1-5,会发现它不是非常安全,原因在于:代码1-5虽然突出了name为“tel”的input标签的安全验证,但对name为“uid”的input标签仅使用了代码<input type= "text" name= "uid" placeholder="username" autocomplete="off" required ="required" autofocus="on" />,而没有对XSS进行安全防护,容易形成安全漏洞。
举这个例子的目的是提示读者:在特别关注某一安全问题的时候,不要放松对其他安全问题的防护,要从多个角度,系统地考虑代码的安全性。
文件上传是网页中最为常见的功能需求之一,如果开发者在开发过程中因对用户文件上传部分的控制力不足或处理存在缺陷,就可能会产生文件上传漏洞,引发文件上传的安全问题。文件上传漏洞是指别有用心者利用文件上传功能将可执行文件上传到服务器上并加以执行,从而获得网站非法控制权等的行为。
代码1-6利用input标签的file属性来实现文件上传,开发者在代码中没有对文件上传中的安全进行考虑,因此会出现如下安全问题。
1.没有对上传文件的类型进行控制
由于没有对上传文件的类型进行控制,使得如.sh、.exe、.php、.asp、.js等可执行文件被上传到服务器,因此可能引发非法控制服务器的情况出现。
2.没有对上传文件的大小进行控制
由于没有对上传文件的大小进行控制,使上传大文件成为可能,因此可能出现因无意或者恶意上传大文件,而造成服务器瘫痪的情况。
若要修复代码1-6中存在的安全漏洞,就要在实现文件上传功能时,对上传文件的类型和上传文件的大小进行限制。代码1-7在一定程度上解决了这两个方面的问题。
代码1-7对文件上传功能进行了以下安全防护。
代码1-7第14~16行代码利用了input标签的accept属性对上传文件的类型选择进行了约束,即只能上传.jpeg文件,无法上传可执行文件。
需要指出的是,代码1-7中对文件类型的约束仅仅是初步的。在运行代码1-7的过程中,网页上传的文件是依据文件扩展名,利用openfiledailog来进行选择,其无法判别上传文件的类型与文件内容是否匹配。如果用户在图1-11所示的文件类型下拉列表中选择“所有文件”,那么文件类型的约束就会失效。
在<input id="userfile" type="file" name="userfile" accept="image/ jpeg" onchange="checkSize()"/>中,onchange在上传前的这个阻塞点调用了JavaScript脚本对文件大小进行了约束:当确认有文件被选中后,利用代码document. getElementById("userfile").files[0].size;获得图片大小;通过代码if(imagSize<1024*1024*3){alert(“图片大小合格”);return true;对文件是否超过规定大小进行判断,如果图片大小不合格就会出现图1-12所示的提示。
图1-11 文件打开对话框
图1-12 图片大小不合格的提示
此外,代码1-7使用了外部CSS,为文件上传的表单装饰了一个边框,相关的CSS代码如代码1-8所示。
文件上传漏洞覆盖了前端、服务器等方面,代码1-7只是进行了初步的防护,远不能满足安全防护的需求。比较完整的文件上传漏洞防护示例将在6.2节中进行介绍。
input标签负责用户输入信息的收集工作,form标签则负责将input标签收集到的信息提交给服务器。正是将用户输入的信息提交给服务器的这个动作,才形成了安全上的一个阻塞点。开发者可以利用form标签的这个阻塞点对信息的安全性、合规性进行验证。如果放弃这个阻塞点,就有可能让“藏有敌人的木马进入特洛伊城”。
form通常有3个基本组成部分:form标签,包含处理form表单信息所要使用的URL和将信息提交到服务器的方法;标签域,包含input标签等;按钮,包括submit按钮等。form标签的表现形式通常如下所示。
form标签的常用属性如表1-5所示。
表1-5 form标签的常用属性
通常,form标签的处理是通过表单内部的<input type="submit"/>将信息提交到服务器。在form标签进行信息传输之前,要特别注意检查包含在form标签输入域内部的输入信息是否符合要求,否则就有可能出现安全漏洞。
在代码1-7中,第11~18行代码是form标签的标准使用方式,其中,<form id="form1" name="form1" action="" method="get" enctype="multipart/ form-data">规定了form标签的id、name,以及服务器上服务的名称和数据提交的方式。只有当文件被加载的时候,才会激活代码<input id="userfile" type="file" name="userfile" accept="image/jpeg" onchange="checkSize()" />。
代码1-9是对代码1-7中的核心代码(第11~18行代码)的改进,其在代码1-7中的第11行语句<form id="form1" name="form1" action="" method="get" enctype="multipart/form-data" >的基础上,增加了代码onsubmit= "return checkSize(),取消了代码1-7中第14~15行代码<input id="userfile" type=" file" name="userfile" accept="image/jpeg" onchange="checkSize()" />的onchange="checkSize()。其作用在于,取消代码1-7中读取文件后的阻塞点,将对文件大小的检查迁移到了form标签进行信息提交前的阻塞点。经过验证,用form标签信息提交前的阻塞点进行信息验证同样可以满足功能需求。
用户的信息是需要通过键盘输入来实现的。因此除了利用input标签的属性和form标签提交前的阻塞点对输入信息进行验证之外,对键盘输入的信息进行监控也是一个对用户所输入的信息进行验证的好方法。
键盘操作中最常需要的功能就是辨识输入键的种类和识别键盘是否被触发。键盘上的按键可以分为两类,一类是包括26个大小写英文字符和数字在内的ASCII值范围内的按键,另一类是键盘上的功能键。键盘操作包括3类事件:
➊ onkeydown,在用户按下任何键盘按键时触发;
➋ onkeypress,在用户按下并放开任何字母键、数字键时触发,但无法识别系统按钮(如方向键和功能键);
➌ onkeyup,当用户释放任何先前按下的键盘按键时触发。
三者的触发顺序:onkeydown最先执行,其次是onkeypress,最后是onkeyup。其中,onkeydown 和onkeypress会影响onkeyup的执行。
代码1-10是键盘事件处理的示例,其功能主要是说明onkeydown、onkeypress、onkeyup在用户输入过程中的响应。
在代码1-10中,当text1输入字符串后会产生以下连锁反应:
➊ 通过onkeypress响应(阻塞点1)在text2内同步显示text1的信息,以及在num2中显示text1的字符串长度;
➋ 通过onkeydown响应(阻塞点2)在text3内同步显示text1的信息,以及在num3中显示text1的字符串长度;
➌ 通过onkeyup响应(阻塞点3)在text4内同步显示text1的信息,以及在num4中显示text1的字符串长度。
在代码1-10中,num5会产生2个关于键盘操作的阻塞点:onkeypress(阻塞点4)用来判别当前输入的是否为数字,而onblur(阻塞点5)则是当num失去焦点后被激活。
此外,当text5输入字符后,通过oninput(阻塞点6)所指向的JavaScript代码,使得text7中同步text5的信息。而text6的信息发生改变后会激活onchange(阻塞点7),会在text7中同步text6的信息,其显示如图1-13所示。
从代码1-10的onkeypress、onkeydown、onkeyup可以看出,当用户进行信息输入,确切说是按下按键、按住按键、松开按键的时候,都是对用户输入信息是否为空、用户输入信息是否包含敏感字符、用户输入信息是否符合预期要求等进行判断的阻塞点。这些阻塞点对前端的安全防护非常重要。
图1-13 代码1-10的页面展示
此外,代码1-10中使用了许多JavaScript函数,具体的函数及其作用如表1-6所示。
表1-6 代码1-10 中使用的JavaScript函数及其作用
对用户输入信息进行安全验证时,使用键盘输入进行验证的优点主要体现在以下几个方面。
1.实时性强
无论是通过input标签的属性验证,还是利用form标签作为阻塞点验证,其本质都是利用提交用户信息之前的阻塞点作为验证的触发点。而键盘输入安全验证则完全依赖于键盘(含软键盘)操作进行触发,这样就会在用户输入信息的同时进行验证,具有很强的实时性。
2.效率高
由于键盘输入安全验证采用的是实时验证,因此同一时间内只能有一个标签进行验证。对多项输入信息的页面而言,将验证时间分散到多个标签中,可提高验证效率。
3.利用JavaScript代码验证
与采用input标签的type属性进行验证的方式不同,键盘输入信息验证通常是利用JavaScript来完成的,因此开发者能够通过键盘输入操作对输入的信息进行实时控制。
前文所涉及的示例大多数是基于单一标签属性所进行的安全防护,但在实际生产环境中,情况会因需求而变得复杂,需要更多的防护来保证H5应用的安全。
在生产环境中,为方便用户,应用往往会提供普通字符串、邮箱地址、手机号码等不同选项供用户作为用户名认证选项,用户只需选择其中的任意一项作为用户名认证选项即可,图1-14所示的登录页面就是如此。有些应用的登录还采用了如微信、微博等第三方认证登录的方式作为用户名认证选项,如图1-15所示。
图1-14 中国铁路12306登录页面(局部)
图1-14提供的登录方式,用户只能选择其一进行登录,即用户无法既选择“邮箱”作为用户名登录,又选择“手机号”作为用户名登录,这是因为开发者无法使用input标签的type属性对两类不同属性的值同时进行验证。也就是说,当用户使用input标签的type属性值为email时,可以验证邮箱地址,但无法验证手机号码;而当用户使用input标签的type属性值为tel或者number时,可以验证手机号码,但无法验证邮箱地址。如果用户要使用input标签type属性值为text,并利用pattern正则表达式进行验证时,所需设计的正则表达式会相当复杂,其结果是不仅要面对未来运维中会出现的各种问题,而且维护也相当困难。因此,建议使用JavaScript对输入的信息进行验证,如代码1-11所示。
图1-15 搜狐网登录页面(局部)
代码1-11应用了form标签的一个事件:onsubmit事件。当表单中的“确认”按钮被点击后将触发这个事件,调用JavaScript代码中的function toVaild(),使input标签中输入的信息在提交到服务器之前就可以在客户端被预先处理。
JavaScript代码中的function toVaild()(代码1-11中的第26~41行)定义了3个正则表达式。用于邮箱地址验证的正则表达式如下所示。
用于手机号码验证的正则表达式如下所示。
r e g e x _ p h o n e =/ ^ ( 1 3 [ 0 - 9 ] | 1 4 [ 5 | 7 ] | 1 5 [ 0 | 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/;
用于XSS字符串验证的正则表达式如下所示。
regex_xss=/^[^%\*\^~\'\"\/\\\<\>\|]+$/;
通过regex_mail.test(uid)、regex_phone.test(uid)及regex_xss.test(uid)等JavaScript代码进行验证,在实现对邮箱地址和手机号码进行验证的同时,防止了XSS漏洞的出现。
思考与提示
如果示例中登录的用户名规则改为“可以采用邮箱地址、手机号码、用户名当中的任意一种的格式”,那么将如何对用户输入的信息进行审核?请修改代码1-11。
双因子认证(Two-Factor Authentication,2FA)是指结合口令和实物(信用卡、SMS手机、令牌或指纹等生物标志)两种因子对用户进行认证的方法。简单地说就是除了口令,还要用另一种非口令形式的验证因子来确认使用者的身份,如短信验证、邮件验证、生物识别等。其优点在于:别有用心者无法直接通过用户名对其中的口令进行暴力破解。
代码1-11虽然对用户输入的用户名进行了验证与防护,但如果有人想要恶意破解用户认证,采用枚举的方式就可以破解。如先设定一组6位的数字密码,然后通过枚举类似银行卡卡号的方式进行破解。这种恶意破解所造成的后果是极为严重的,因此在考虑用户名认证方式的时候,可以考虑采用组合鉴别的方式认证。
此外,验证码也是一种重要的验证因子,即Completely Automated Public Turing test to tell Computers and Humans Apart。验证码通常是随机生成的。当用户访问登录页面或者点击随机码图形时,系统都会产生一个新的验证码,供用户填写。系统能够借此区分用户是手动登录系统,还是使用脚本登录系统,在一定程度上能防止有人通过自动脚本进行恶意破解,包括防止诸如恶意破解口令、“刷票”“论坛灌水”等行为。防范脚本自动破解登录认证的手段有多种,比较常见的防范方法有:快速拼图验证法,登录页面如图1-16所示;物品选择验证法,登录页面如图1-17所示;扫描二维码验证法,登录页面如图1-18所示;手机验证码验证法,登录页面如图1-19所示;验证码验证法,登录页面如图1-20所示。
图1-16 快速拼图验证登录页面
图1-17 物品选择验证登录页面
图1-18 扫描二维码验证登录页面
图1-19 手机验证码验证登录页面
图1-20 验证码验证登录页面
生成随机验证码的程序veryfication.php是用PHP编写的,并存放在服务器中,具体代码如代码1-12所示。开发者在前端的网页中可以采用代码<img class="formcontrol span4" id="code" name="code" src="veryfication.php" onclick="create_code()" title="点击刷新" onclick=”change_rand();"/>从服务器调用veryfication.php,并通过对验证码的验证来阻止别有用心者利用脚本对应用中的用户名和密码进行暴力破解。
对代码1-12内的关键代码的相关解释如下。
➊ function show_code_jpg传递的参数及其作用分别为:$num用于传递需要几位验证码;$w用于传递图形的宽度;$h用于传递图形的高度;$r、$g、$b用于传递颜色。
➋ $_SESSION['yzm_code']=$code;的作用是将验证码写入session。
➌ header ("Content-type: image/PNG”")的作用是输出PNG格式的验证码图片。
➍ imagesetpixel ($im, rand(0, $w), rand (0, $h), $black)的作用是为验证码图片增加干扰点。
➎ imageline($im, 0, $y1, $w, $y3, IMG_COLOR_STYLED)的作用是为验证码图片增加干扰线。
➏ imagedestroy($im)的作用是销毁验证码图片。
思考与提示
生成随机数字验证码的函数完全可以用JavaScrip进行编写,代码1-12用PHP编写的目的在于提示读者:H5不仅包含了HTML5、CSS、JavaScrip,还融入了包括PHP在内的很多技术,其已经实现了将不同的技术按照需求(对公司和创业者而言,技术并不是他们首要关注的因素,他们经常会优先考虑成本和需求等)进行融合,而不是仅局限于“HCJ”的组合。在一定程度上,验证码的确可以降低自动脚本对用户认证破解的风险,但如果被别有用心者发现验证码的获取可以通过脚本实现,就会使验证码失去对脚本自动破解认证的防范作用,验证将形同虚设。
图1-21所示的是某品牌早期安全产品的登录页面,实现代码如代码1-13所示。
通过对登录页面代码(代码1-13)的解读可发现,其中有一段代码如下。
document.getElementById("APCPic").src="getImg?ID="+response;
该段代码的功能是为页面生成随机码图形,并将这个随机码图形以数字的形式返回给页面。如果用户在Chrome浏览器中按快捷键F12对登录页面进行调试跟踪,就可以清晰地看到页面是如何以字符串形式获得验证码(附加码)的,如图1-22所示(注:漏洞曝光后已经得到了公司的有效修复)。
图1-21 某品牌早期安全产品的登录页面
图1-22 查看页面是如何获得验证码(附加码)的
思考与提示
之所以在前文介绍Firefox浏览器的调试器,而在这里使用Chrome浏览器的调试器,是因为希望读者能够熟悉每一款浏览器,并能够使用浏览器调试工具进行调试。因为H5在不同浏览器中所表现出的结果不一定是相同的,开发者进行应用开发时应该有能力将应用与每一款流行的浏览器进行适配。
随着光学字符识别(Optical Character Recognition,OCR)技术的发展和大数据的应用,过去由于干扰处理、字符变形等原因无法通过非人工手段识别的验证码能够被自动识别出来,导致自动脚本被恶意破解成了需要解决的、很重要的安全问题。目前,字符、数字随机图形化验证码的方法已经不是一个相对安全的验证方法,因此越来越多的应用采取了邮件验证、语音验证、短信验证,以及其他多种验证方法进行验证。
思考与提示
认真、深入地读代码,认真调试代码是避免发生安全问题的好办法。安全绝不是一蹴而就、一劳永逸的,以往被视为安全的技术,也可能变成安全隐患。