第二部分 产品安全架构
第3章 产品安全架构简介
产品安全架构要解决的问题是“如何打造一个安全的产品”。
从这一章开始,我们将探讨安全架构在产品中的落地实现,也就是如何保证产品自身的安全性。本章先概述产品安全架构的内容,然后分析典型的产品架构与数据访问流程,从而为以后的分析打下基础。
3.1 产品安全架构
产品安全架构就是构建产品自身安全特性的主要组件及其关系,如图3-1所示。
图3-1 产品安全架构
对于一款具体的产品来说,安全仅作为产品的质量属性,而不是独立存在的元素,与其他质量属性如性能、可扩展性、可维护性等并列,可用逻辑模块来描述。我们将基于安全架构5A方法论(第2章讲述的安全架构的5个核心元素)来讨论产品安全,保障所交付产品的保密性、完整性和可用性。
从这里开始,我们将着重强调尽可能地从源头做起,从产品自身做好安全质量的提升,防患于未然。至于产品外部的安全防御基础设施,如抗DDoS、入侵检测系统、WAF(Web Application Firewall, Web应用防火墙)、RASP(Runtime Application Self-Protection)等,将在第三部分讲述。
本书所讨论的产品,其范围主要集中在互联网及相关行业,包括但不限于Web应用、网络服务、特定功能的服务器软件、客户端软件等,是最终交付运营的最小单元。
安全体系也是由一个个安全领域内的产品所构成,是安全体系的最小单元,如果这个最小单元的安全性都得不到保障,则整个安全体系的安全性更是无从谈起。
由于各企业的产品形态是不同的,有些产品不在本书的讨论范围之内,包括但不限于:
● 智能终端产品,如物联网终端、智能穿戴、手机等。
● 网络设备本身,如防火墙、路由与交换设备等。
● 专用通信系统(如蓝牙、无线电、专用协议等未采用TCP/IP协议的通信系统)。
在涉及这些产品安全性的时候,可参考本书提到的安全要素,基于实际业务场景进行风险评估和安全设计。
3.2 典型的产品架构与框架
前面已简单介绍了架构有关的基本概念,在服务器生产环境和安全的日常评估工作中,还会经常听到一些术语,如三层架构、B/S架构、C/S架构、框架、MVC等。
安全架构师了解这些术语,有助于方案评估等工作的开展,避免跟业务团队之间缺乏共同语言。
3.2.1 三层架构
三层架构从安全角度看是比较推荐的一种逻辑架构,如图3-2所示。
图3-2 三层架构
其中:
■ 用户接口层(User Interface Layer,即通常所说的UI),也可以称为表示层(Presentation Layer);对Web应用来说,即在用户浏览器界面呈现的部分,跟用户交互,也常常被称为前端。
■ 业务逻辑层(Business Logic Layer),业务的主要功能和业务逻辑都在这一层。
■ 数据访问层(Data Access Layer, DAL),位于业务逻辑和数据中间,封装对数据的操作(添加、删除、修改、查询等),为业务逻辑提供数据服务。
构成三层架构的元素(逻辑模块),中间的箭头表示它们之间的访问顺序关系。
在实际分层中,三层仅仅是逻辑上的三层,实际也可能是多层,如七层架构(在逻辑上与三层架构是一致的)。
三层架构也不是指物理上需要三台服务器,在一台服务器上也可以实现三层架构。不过通常为了更好地执行访问控制,建议还是将它们分布在不同的服务器上为好。
三层架构也不局限于B/S架构,C/S架构也可以有三层架构。
3.2.2 B/S架构
B/S架构,即Browser/Server(浏览器/服务器)架构。在三层架构概念出现之前,开发人员一般是将UI、业务逻辑、数据访问混合在一起开发的,如下所示:
<?php @header('Content-type: text/html; charset=UTF-8'); require_once('config.ini.php'); $id=$_GET['id']; if($id) { mysql_query("SET NAMES 'UTF8'"); $conn=mysql_connect($host, $dbreader, $dbpassword) or die('Error: ' . mysql_error()); mysql_select_db("vulnweb"); $SQL="select * from userinfo where id=".$id; $result=mysql_query($SQL) or die('Error: ' . mysql_error()); $row=mysql_fetch_array($result); if($row['id']) { ?> Username: <?php echo $row['username'] ?> <br> Description: <?php echo $row['description'] ?> <br> Query OK! <br> <?php } else echo "No Record! "; mysql_free_result($result); mysql_close(); } ?>
这份代码存在很多问题,首先就是三层混合在一起开发的问题(在后面还会提到数据和指令混合在一起导致SQL注入的问题、将数据库异常展示给用户的问题等)。
提示
至于如何按照三层架构的最佳实践来开发,留待读者思考(参考关键词:MVC框架、ORM模型,让每个脚本文件只能属于三层架构中的一层)。
这里先基于三层架构理念,给出推荐的B/S产品的三层架构,如图3-3所示。
图3-3 推荐的B/S三层架构
有关B/S三层架构的简介先到这里,我们会在本书第13章进行进一步分析。
3.2.3 C/S架构
C/S架构,即Client/Server(客户端/服务器)架构。这里先基于三层架构理念,给出推荐的C/S产品的三层架构,如图3-4所示。
图3-4 推荐的C/S三层架构
有关C/S三层架构的进一步分析,会在第13章详细讲述。
3.2.4 SOA及微服务架构
SOA架构(Service-Oriented Architecture,面向服务的架构)概念曾经非常火热,在复杂的企业网络环境中有大量使用。它采用XML(EXtensible Markup Language,可扩展标记语言)格式通信,采用ESB(企业服务总线)对服务进行集中式管理,是一个中心化的面向服务架构,如图3-5所示。ESB对不同的服务做了消息的转化解释和路由工作,让不同的服务互联互通。
图3-5 SOA架构
由于SOA的复杂度较高,也令诸多企业望而却步。如果你所在的企业目前还没有SOA的相关实践,建议直接跳过,直接考虑微服务等新型架构。
微服务代表了一种新的应用体系结构,是SOA的轻量化发展,去掉了企业服务总线,将过去的大型单一应用程序分解成多个小型的独立的功能或服务套件,不再采用集中式的服务管理。微服务使用轻量级的HTTP Restful API或Thrift API进行通信,是一个去中心化的面向服务架构。
在微服务架构模式下,可以引入API网关(Gateway),来执行服务管理、统一接入,服务之间的调用也经过API网关。有了API网关,所有非业务领域的功能都可以交给API网关来完成,如服务间身份认证、路由、负载均衡、缓存等。API网关不仅可以提供给移动APP客户端访问,也可以让后台业务访问,如图3-6所示。
图3-6 微服务架构与API网关
在微服务架构中,我们可将其中的每一个服务都当作一个独立的产品进行分析。
网关就是不同网络间的互联设备,如同“一夫当关,万夫莫开”,是不同网络之间的流量的必经之地。
应用网关是应用层流量的必经之地,可以通过硬件来实现,也可以通过软件来实现。
API网关与应用网关类似,主要区别在于API网关是给应用访问的,也包括手机APP访问;而应用网关通常是提供给人通过浏览器访问的。
3.2.5 典型的框架
框架是对架构的可重用部分的实现(半成品软件)。使用框架之后,可以节省大量人力(避免了重复劳动),让程序员将主要精力聚焦在业务上。
典型的框架包括:
■ .Net Framework(微软)
■ Spring(Java MVC框架)
■ Django(Python语言编写的Web框架,采用了MVC思想)
MVC即Model View Controller,是模型-视图-控制器的缩写,是一种框架设计模式(就是设计框架的经验总结,是一种知识或思想),这种思想要求把应用程序的输入、处理和输出分开。
MVC和三层架构并不冲突,对应关系如图3-7所示。
图3-7 MVC与三层架构的对应关系
■ 模型(Model):属于业务逻辑层,以Django为例,主要用于定义各个类(Class)的数据结构以及不同类之间的关系。
■ 视图(View):对应UI层的输出(即响应,Response),解决如何呈现的问题。
■ 控制器(Controller):对应UI层的输入(即请求,Request),解决“如何处理用户的输入”,并交给正确的业务逻辑模块去处理。
提示
上述概念暂时没有理解的话,没有关系,可以先跳过。
3.3 数据访问层的实现
在上面列出的典型的分层架构中,主要涉及:
■ 前端跟业务逻辑的分离,这一部分相对成熟,例如采用MVC框架,或采用Angular、Vue、React等前端框架构建的单页面应用(前后端使用JSON通信)等方式。
■ 业务逻辑跟数据访问层的分离。
■ 数据访问层跟数据库的分离。
为了实现这些分离,其中最重要的一个部分就是数据访问层(DAL)的实现。
数据访问层在实现方式上,可以采取多种方式,如自定义DAL编码、ORM框架、DB Proxy、配合统一的数据服务简化DAL等。无论采用哪一种方式,都应当满足一个条件:数据库口令只在一个地方(通常为配置文件)出现,且加密存储。
3.3.1 自定义DAL
自定义DAL(数据访问层),就是自行编码来实现数据访问层,通过数据访问层完成所有对数据库的操作。
我们以Janusec Application Gateway(https://github.com/Janusec/janusec)为例,来看看它是如何体现分层访问以及对数据库口令的保护的。Janusec使用了PostgreSQL数据库来统一管理(存储、添加、查询、修改、删除)数字证书并对证书私钥加密保护。
首先,数据库口令只在一个文件(/config.json)中出现,且被加密了,如图3-8所示。
图3-8 配置文件中对数据库口令加密
其次,在data模块(/data)中,定义了各种对数据库的操作,其中操作数字证书数据的部分为/data/backend_certificate.go,在这个文件里面可以看到,用于更新证书的SQL语句为:
const sqlUpdateCertificate = `UPDATE certificates SET common_name=$1, pub_cert=$2, priv_key=$3, expire_time=$4, description=$5 WHERE id=$6` func (dal *MyDAL) UpdateCertificate(commonName string, certContent string, encryptedPrivKey []byte, expireTime int64, description string, id int64) error { stmt, err := dal.db.Prepare(sqlUpdateCertificate) defer stmt.Close() _, err = stmt.Exec(commonName, certContent, encryptedPrivKey, expireTime, description, id) utils.CheckError("UpdateCertificate", err) return err }
这里的$1到$6均表示参数化操作,在后面的章节中,我们会介绍为什么要这么做,暂时只需要知道它可以很好地防止SQL注入就可以了。
在更新证书操作的业务逻辑部分(backend/certificate.go)可以看到,只需要简单地调用DAL中的函数,即可完成证书更新操作。
err = data.DAL.UpdateCertificate(commonName, certContent, encryptedPrivKey, expireTime, description, id)
上述编码让业务逻辑和数据访问分布在不同的模块中,实现了业务逻辑与数据访问的分离。
3.3.2 使用ORM
ORM(Object Relational Mapping)即对象关系映射,ORM将关系数据库中的一行记录与业务逻辑层中的一个对象建立起关联,如图3-9所示,可以让开发人员聚焦于业务,从频繁地处理SQL语句等重复劳动中解放出来。
图3-9 ORM将数据库记录与业务逻辑中的对象关联起来
使用ORM之后,通常只需要定义好模型,配置好数据库账号,即可使用ORM内置函数操作数据库,而不再手工书写SQL语句。比如Django内置的ORM模块。
在实际使用ORM的时候,通常还存在一个安全问题,那就是数据库口令在配置文件中是明文存储的。为了解决这个问题,可以使用解密函数替换原来的口令,我们会在后面第14章介绍如何对第三方框架配置文件中的口令进行加密。
ORM自身的类型校验以及参数化机制,对SQL注入等风险具有较好的预防作用。
3.3.3 使用DB Proxy
如果是新业务,可以采用自定义DAL、使用ORM等方式,但对于大量的存量业务来说,改进量很大,且随着时间的推移,很多业务原来的开发人员已经不在原岗位上了,需要寻找一种适合大规模推行、业务改进量较少的方法。
DB Proxy是一种数据库访问中间件,可以实现如下特性:
■ 读写分离。
■ 分库分表(又称为Sharding),如果是按列分表,每一个目标数据库中只包含部分数据,可以降低数据泄露的风险。
■ 收敛访问路径,让所有访问数据库的流量都经过DB Proxy。
■ 收敛数据库账号,让数据库账号只在DB Proxy上加密存储。
■ 参数检查,检查SQL语句的合法性,如检测到恶意请求,可加以阻断,充当数据库防火墙的作用。
用于存量业务改进时,可以在业务上采取跟DB Proxy在应用层集成的方式,如图3-10所示,也就是具体的业务不使用数据库账号(应用层账号将在后台身份认证章节讲述)。
图3-10 DB Proxy原理
3.3.4 配合统一的数据服务简化DAL
这是笔者最为推崇的一种方式,将数据库封装为数据服务,让数据库不再直接向业务提供服务,如图3-11所示。这一思路会在后续章节中展开。
图3-11 数据服务
数据服务可以是基于HTTP的Restful JSON API(请求和返回的内容格式都是JSON格式),也可以是RPC框架下的二进制数据流,还可以是基于HTTP的RPC通信。在这种模式下,数据访问层主要包含RPC访问函数,让业务只需要像操作本地函数一样,即可访问到远程的数据。