系统架构之于软件,就像设计图纸之于楼房,架构设计对它非常重要。

架构设计方法

架构设计都有哪些方法呢?架构设计遵循特定的方法,比如 TOGAF(The Open Group Architecture Framework,开放组体系结构框架)、五视图方法等。其中 TOGAF 主要针对复杂的企业系统架构,比较重,不大适合迭代速度非常快的互联网产品,所以互联网公司常用的主要是五视图方法。

什么叫五视图方法?

它是指从业务逻辑、开发环境、运行状态、物理部署、数据关系等方面绘制出相应的逻辑视图、开发视图、运行视图、物理视图、数据视图来设计架构。 其中,逻辑视图、开发视图、运行视图属于软件架构的内容,物理视图、数据视图属于系统架构的内容。

秒杀系统架构设计

采用五视图方法来介绍下

逻辑架构

物理架构

首先,为了实现动静分离,我们需要部署 CDN 节点,将秒杀系统静态页面和静态数据利用 CDN 缓存起来,以便利用 CDN 的就近访问能力提供更高的性能。比如我们可以在中国和新加坡都部署 CDN 节点,以便为中国用户和海外用户提供更好的访问速度。

第二,动静分离意味着除了 CDN 外,我们还需要部署秒杀后端节点。

为了实现秒杀后端节点的高可用,我们需要使用云架构保障其基础设施层的高可用。而且需要部署到云的多个可用区,防止单一可用区发生故障影响服务。

第三,为了将不同可用区作为一个整体对外提供服务,我们需要部署路由器、防火墙、交换机、SLB(Server Load Balancer,负载均衡器)。

其中,路由器是负责接收外网请求并转发到内网;防火墙负责过滤掉有风险的数据,比如过滤掉黑客发起的网络攻击;交换机负责将请求转发到具体的可用区内;SLB 是负载均衡器,它可以将可用区内多个节点作为一个整体对外提供访问,并将请求均衡地转发到后端各节点。

SLB 之间采用主备的方式部署,如果可用区 1 的 SLB 挂了,可用区 2 的 SLB 会自动接管属于可用区1 的流量,并将流量转发给可用区 1 内的云主机。

第四,部署完各节点后,还需要设置域名解析,将域名解析到我们部署的 SLB 外网 IP 上。通常,对于流量大的系统,会采用多个负载均衡器,然后使用动态 DNS 解析的方案做多个 SLB 的 DNS 负载均衡,以防单个 SLB 无法扛住大流量。

 数据架构

数据架构通常用 E-R 图(Entity Relationship Diagram,实体-联系图)表示,我们通常用它来表示数据对象与属性、用户之间的关系。

在需求分析过程中,我们了解到秒杀系统主要有两大主要数据:活动信息和商品信息。除了这两大主要数据外,还有两大次要数据:订单和黑名单。其中订单是用户抢购商品并下单生成的,而黑名单是为了反黄牛而设计的,通常通过大数据分析统计生成,如果用户 ID 在黑名单中,则不允许抢购商品。

结合需求分析过程,我们得知:

  •     活动信息,包括专题、场次、描述、时间、限购策略、商品列表、状态等信息;
  •     商品信息,包括商品 ID、名称、描述、规格、原价、活动价、活动库存等信息;
  •     订单信息,包括订单 ID、用户 ID、商品 ID、商品价格、商品数量、总价、收货地址、创建时间等信息;
  •     黑名单,包括用户 ID 列表。

这些数据中,活动信息、商品信息、黑名单都是由管理员在管理后台管理的,只有订单信息是由用户在商城下单创建的,管理员可以通过管理后台查看订单信息。

其中物理架构用于指导运维工程师部署软件,以便实现基础设施的“三高”;数据架构用于指导开发人员做数据库表设计,以及指导测试人员进行软件功能测试时检查数据准确性。

严格来讲,逻辑架构属于软件架构而不是系统架构。那为什么我要给你介绍逻辑架构呢?主要是因为它能帮助我们从顶层了解系统的功能划分,从而更方便进行系统架构设计。

DDD 原理及秒杀系统领域模型

在电商系统中,我们通常把完整的购物流程划分为下单前和下单后。下单是由用户在商城发起的,下单后订单涉及关单、支付、退款、筛单、投递、售后等流程。

其中,关单涉及库存系统,支付流程涉及支付系统,退款流程涉及客服、支付和财务系统,筛单涉及风控系统,投递涉及物流和仓储系统,售后涉及售后系统。

1.什么是 DDD?

DDD(Domain Drive Design,领域驱动设计)是一种软件设计方法,是指在软件设计的过程中始终围绕领域来构建模型。构建领域模型的过程就叫“领域建模”。

DDD 就是为了解决各个业务系统的职责划分,围绕业务对象而进行模型建构。

2.如何使用 DDD 对秒杀系统领域建模?

  • 战略建模:是指从宏观上构建领域模型。

  • 战术建模:从具体细节上构建领域模型,它是对战略建模中限界上下文的具体实现。

还是以秒杀系统为例,秒杀系统的战术建模就是分析活动领域中各个对象的类型,针对类型特点做抽象设计。

高可用架构设计

云架构分层设计及其高可用

云架构分为:

  • IDC(Internet Data Center,互联网数据中心) 基础设施层
  • 物理主机层
  • IaaS 层
  • PaaS 层
  • SaaS 层

它们分别提供了 IDC 基础设施高可用、物理主机高可用、虚拟主机高可用、基础组件高可用、应用服务高可用等能力。

1.IDC 基础设施层

IDC 基础设施层主要包括电源、网络、空调、消防等设备,甚至包括 IDC 机房所在的大楼的物理条件。

电源和网络的高可用是如何做的呢?通常是采用冗余的方案:配备多条线路。

2.物理主机层

将存储和计算分离,并提供足够的并发性能,一般采用 NAS(Network Attached Storage:网络附属存储),通过高速网卡和交换机提供高并发和高性能访问能力。

NAS 机器上的磁盘采用 RAID (Redundant Arrays of Independent Disks,磁盘阵列) 提升性能和可用性。如:用两块磁盘组成 RAID 1,其中一块磁盘当作数据镜像磁盘。假如单个磁盘故障率为 0.1%,那么两块磁盘组成 RAID 1 后,整体故障率为 0.0001%。

3.IaaS 层

IaaS 是 “Infrastructure as a Service” 基础设施即服务的缩写,通过虚拟化技术在宿主机上虚拟出多个运行环境,应用部署在虚拟出来的运行环境里。目前比较常用的是主机虚拟化和容器虚拟化。

什么是主机虚拟化技术呢?主机虚拟化是指利用软件来虚拟整套计算机硬件,也就是 VM (Virtual Machine,虚拟机) 。操作系统可以运行在虚拟机上。

业内比较成熟的方案主要有 VMware、KVM、Xen,配合 OpenStack 之类的云计算管理平台来管理、调度虚拟机。我们经常听到的云主机就是利用了主机虚拟化技术。

虚拟机通常配合 SDN (Software Defined Network,软件定义网络) 和 SDS (Software Defined Storage,软件定义存储) 等技术来使用,用来实现虚拟机的网络高可用和存储高可用。

4.PaaS 层

PaaS 是“Platform as a Service” 平台即服务的缩写,云产商将通用组件打包部署在已有的主机上,并提供访问组件的 SDK 或者 API,开发者修改应用的代码引入 SDK 或者调用 API 来访问云产商提供的通用组件。比如:在阿里云上购买数据库产品,修改应用程序代码访问购买的数据库产品。

5.SaaS 层

SaaS 是“Software as a Service” 软件即服务的缩写,不仅提供软件的后端,还提供软件的前端页面,达到开箱即用的效果。

SaaS 系统可以通过同城双活、异地备份、多云部署等方案来降低单云单机房故障的风险。比如:将系统同时部署在阿里云和 AWS 上,即使 AWS 故障了,也能快速切到阿里云。不同云之间使用专线同步数据。

先来看一个案例:

    假如某电商创业公司,在起步阶段业务 DAU (Daily Active User,日活跃用户) 10 万左右,团队 20 人;发展阶段 DAU 100 万左右,团队 50 人;成熟阶段 DAU 500 万左右,团队 200 人。公司计划在快速发展阶段加入秒杀功能,应该如何基于云架构来设计系统架构呢?

在起步阶段,公司业务用户比较少,系统风险也小。可以考虑在 IaaS 云产商的两个机房分别购买 1 台云主机组成双活来部署业务服务。如果云产商有容器云,可以优先考虑使用容器。业务服务依赖的基础组件可以使用 PaaS,一些云产商会提供免费额度。

到了发展阶段,此时业务处于快速发展时期,通常会引入一些新的营销手段刺激消费,比如秒杀。

由于这个时候用户量已经非常可观,再加上秒杀功能会带来庞大流量,需要注重系统可用性,比如要求系统可用性达到 99.99%。

为了防止单一可用区出现故障,核心业务服务和秒杀系统最好部署在多个可用区,每个可用区部署多个服务器实例,并由 SLB 组件做服务器的负载均衡。那如何防止单一 SLB 实例出现故障呢?通常是采用多个 SLB 实例,并由 DDNS 做 SLB 实例的负载均衡。

另外,在秒杀系统内部做限流的时候,也可以选用云厂商提供的 MQ (Message Queue,消息队列) 组件限制并发流量,以便减少下游系统的压力,提升整体稳定性。

最后是成熟阶段,此时业务核心功能基本稳定,用户量初具规模。如果发生事故,很容易影响公司营收,这也就要求诸如秒杀这类涉及交易流程的系统,具有更高的可用性,比如高达 99.999%。

那么,我们可以做哪些事情呢?这个时候就可以考虑多云架构、异地多活、异地备份等措施避免更极端的故障,比如地震、洪水破坏可用区。

故障转移和恢复:如何通过主备切换缩减故障时间?

1.什么是主备切换

所谓主备切换,按主从关系划分的话,有狭义和广义之分。

狭义上的主备切换,有明确的主从角色划分,主节点承担了主要的集群管理工作,当主节点故障,从节点变成主节点,接管主节点的工作。这种主备切换比较适合有状态的系统,也就是有数据存储功能的系统,比如存储系统等。

广义上的主备切换,它没有明确的主从角色划分,任何一个节点发生故障,它的工作就会分配到其他任何一个正常节点上,不需要指定主节点。这种主备切换比较适合无状态的系统,也就是没有数据存储功能的系统,比如 http 服务。

主备切换在转移故障的时候,主要分三步:

第一步故障自动侦测(Auto-detect),采用健康检查、心跳等技术手段自动侦测故障节点;

第二步自动转移(FailOver),当侦测到故障节点后,采用摘除流量、脱离集群等方式隔离故障节点,将流量转移到正常节点;

第三步自动恢复(FailBack),当故障节点恢复正常后,自动将其加入集群中,确保集群资源与故障前一致。

2.云架构各系统中的主备切换

 

在云架构拓扑图中,总体可以分为三大类节点:网络节点、计算节点、存储节点。

其中,存储节点属于存储系统,主要用于将数据存储到磁盘,在云架构中属于最底层,是整个云架构的基石。

计算节点主要是云主机,用于运行业务系统,它的数据由存储系统负责存储。

网络节点包括:DNS、CDN、路由器、防火墙、交换机、SLB。它们主要用于将外网流量接入到内网的可用区内,最终转发到各个计算节点。

从这个云架构拓扑图的组成部分我们就知道,存储系统、业务系统、SLB 和 DNS 非常重要。

存储系统的主备切换

通常,NAS 存储系统中会用到一种叫 DRBD (Distributed Replicated Block Device,分布式复制块设备) 的技术。该技术能将存储节点的磁盘作为块设备,通过互相映射的方式实现互相备份。如下图所示:

Heartbeat 是什么呢?Heartbeat 是 Linux-HA 工程的一个组成部分,用于实现高可用集群。简单来说,Heartbeat 部署在集群内节点上,用于给节点提供心跳机制,各节点通过侦测其他节点心跳来判断其他节点是否有故障。如果侦测到故障节点,则由正常节点接管故障节点的流量,然后继续侦测故障节点的心跳。

除了 Heartbeat 外,使用 Keepalived 也能为集群节点提供心跳和主备切换功能。与 Heartbeat 用于存储系统的主备切换不同, Keepalived 常用于负载均衡器的主备切换。

业务系统的主备切换

以 Nginx 为例,为了给后端服务做健康检查,我们可以在 Nginx 配置文件中添加如下配置:

upstream test_web {
    server 192.168.1.21:80;
    server 192.168.1.22:80;
    check interval=3000 rise=2 fall=5 timeout=1000 type=http;
}
  • interval:用于配置健康检查的间隔,如 3000 毫秒;
  • rise:用于配置连续几次请求成功后表示该节点恢复了,如 2 次;
  • fall:用于配置连续几次请求失败后表示该节点出故障了,如 5 次;
  • timeout:用于配置健康检查请求的超时时间,超时后表示请求失败,如 1000 毫秒;
  • type:用于配置请求协议的类型,如 HTTP 请求。

SLB 和 DNS 的主备切换

案例三:

某电商平台初期用户量较少,系统部署在单一可用区内,只使用了一个 SLB 对外提供服务。有一天,运营人员配置了一场促销活动,但是系统所在的可用区网络故障,导致整个活动受影响无法正常进行,错失了活动的最佳时机。

后来,研发人员和运营人员对系统架构进行了调整,将系统部署在两个可用区,每个可用区有两个 SLB,由 DDNS 负责 SLB 的负载均衡。在后续促销活动中,虽然也发生了类似的故障,但由于有多个 SLB,以及用 DDNS 为 SLB 做域名解析负载均衡和健康检查,DDNS 自动将流量调度到了正常的可用区,保障了促销活动的顺利进行。

在案例三中,SLB 负责可用区内业务节点的健康检查和负载均衡,确保流量始终转发到正常的节点上。DDNS 负责多个 SLB 的健康检查和负载均衡,确保域名解析始终解析到正常的 SLB 的 IP 上。

实际上,DDNS 可能并不一定完全满足需求。DNS 中 IP 更新是有时间限制的,通常默认是 5 分钟。也就是说,假如 SLB 故障,IP 列表更新了,由于本地 DNS 有缓存,可能会导致 5 分钟内请求都失败。而且用户的本地 DNS 可能会被恶意劫持,导致客户端请求失败。

针对这种情况,有的公司会选择实现 HTTPDNS 接口给客户端下发 IP 列表。

通常,一个 HTTPDNS 返回的结构是这样子的:

{
	"www.mi.com": {
    	"ips": [
        	"103.104.168.101",
        	"116.211.122.1"
    	],
    	"ttl": 57
    }
}

客户端拿到域名与 IP 列表的对应关系后,缓存在本地,然后随机选择一个 IP 建立连接。如果连接失败则从剩下的 IP 中再随机取 IP 来建立连接,直到连接成功或者达到最大重试次数。假如发现该数据过期了,则客户端会重新发起 HTTPDNS 请求获取最新的 IP 列表。

过载保护:如何通过熔断和限流解决流量过载问题?

1.过载保护的重要性

什么是过载保护呢?所谓过载保护,是指负载超过系统的承载能力时,系统会自动采取保护措施,确保自身不被压垮。

熔断的基本原理

什么是熔断?熔断就是在系统濒临崩溃的时候,立即中断服务,从而保障系统稳定避免崩溃。 它类似于电器中的“保险丝”或“断路器”,当电流过大的时候,“保险丝”会先被烧掉,断开电流,以免电路过热烧毁电器引起火灾。软件系统中的熔断也是如此,当系统满足某个判断条件时,就会拒绝处理请求,避免系统压力过大被压垮。

限流的基本原理

它的目的是确保系统高效、稳定地运行,确保请求能够快速处理的同时,保障系统不被流量压垮。

限流算法主要有:

  • 计数器限流:也叫固定窗口限流算法

首先,选定一个时间窗口作为一个周期,假设为 5 秒;

第二步,设定 5 秒内允许通过的流量,如 1000 个请求;

第三步,每次请求,计数器都加 1;

第四步,判断计数器数值是否超过 1000 ,超过了就触发限流策略,如:拒绝或者延迟处理请求等;

最后,如果时间过了 5 秒,则重置计数为 0,开始一个新的周期。

该限流算法的优点是实现简单,缺点是面对突发流量时不够精确。面对瞬时流量时,会存在资源利用率的剧烈抖动。

  • 滑动窗口限流

滑动窗口限流算法是对计数器限流算法的优化。它的主要原理是将计数器限流算法中的一个周期拆分成很多等分,比如将 5 秒的周期拆成 5 个 1 秒,每次统计从当前时间开始过去 5 秒内的流量,每隔 1 秒往后滑动 1 秒。

  • 令牌桶限流

令牌桶算法的基本原理是,使用一个定时器以恒定速度往桶里颁发令牌,桶满了则丢弃多余令牌。请看示意图:

在令牌桶算法中,一般只有拿到令牌的请求才会被处理,没拿到的将会被拒绝。这个过程就像景区的人工售票窗口售票,只有买到票了才能检票进入景区。这其中,令牌就是门票,令牌桶就是售票窗口,负责发令牌的线程就类似于售票员,处理请求的线程就是检票员。

  • 漏桶限流。

漏桶算法的原理跟令牌桶有点相似,只不过漏桶算法采用“生产者-消费者”模型。在“生产者”一端,所有请求进队列,队列满了则丢弃请求。在“消费者”一端,以恒定速度消费队列并处理请求。

以上这几种限流算法中,流量控制效果从好到差依次是:漏桶限流 > 令牌桶限流 > 滑动窗口限流 > 计数器限流。

其中,只有漏桶算法真正实现了恒定速度处理请求,能够绝对防止突发流量超过下游系统承载能力。不过,漏桶限流也有个不足,就是需要分配内存资源缓存请求,这会增加内存的使用率。而令牌桶限流算法中的“桶”可以用一个整数表示,资源占用相对较小,这也让它成为最常用的限流算法。

正是因为这些特点,漏桶限流和令牌桶限流经常在一些大流量系统中结合使用。比如秒杀系统中就同时使用了这两种限流算法。

过载保护设计实战

前面我为你介绍了一些常用过载保护手段的原理,那具体怎么实现呢?接下来我就以秒杀系统为案例来介绍下吧。

    在秒杀系统中,瞬时并发能达到 100 万 QPS。由于自身资源的限制,秒杀系统的核心业务逻辑处理请求的能力为 50 万 QPS,而下游的 Redis 的并发能力为 10 万 QPS。假如一场秒杀活动中预计会有 300 万次以上请求,请问该如何设计过载保护?

我们来分析一下。从这个案例中,我们能提取到 4 个参数。它们分别是:总请求量 300 万次、瞬时并发 100 万 QPS、秒杀系统自身承载能力 50 万 QPS、下游 Redis 承载能力 10 万 QPS。如果要做过载保护,这几个数据就是关键。

我们先来看,哪个是秒杀系统的流量瓶颈,先解决它。你找到了吗?这个瓶颈就是下游 Redis 的并发能力,仅为 10 万 QPS,这在秒杀活动当中很容易被流量压垮。那该怎么办呢?我们可以采用漏桶限流来控制 Redis 的请求,让它保持在恒定速度。

接下来,当请求进入秒杀系统后,需要确保核心逻辑处理的请求不超过 50 万 QPS。为此,我们需要选择一种既稳定又快速的限流方法,比如令牌桶限流算法。因为令牌桶不会像计数器限流和滑动窗口限流那样会导致资源使用率抖动,也不需要像漏桶限流那样由于缓存请求而无法得到立即处理。

在实际当中,流量进入系统前,令牌桶中有一部分预先分配的令牌,可能会出现瞬间流量超过 50 万 QPS的情况。为了以防万一,我们可以把令牌桶限流上限稍微调低,比如 45 万 QPS,确保瞬时流量不超过 50 万 QPS。

最后,由于总请求量在 300 万次以上,整个秒杀过程会持续数秒。为了避免因下游服务网络延迟问题导致请求堆积压垮系统,还需要加上熔断措施。当请求延迟超过某个临界值的时候,拒绝后续请求,直到延迟恢复正常。

总的设计方案如下图所示:

KV 存储:etcd 和 Redis 高可用原理和部署方法

什么是 KV 存储?

KV 是 Key-Value 的缩写,KV 存储也叫键值对存储。简单来说,它是利用 Key 做索引来实现数据的存储、修改、查询和删除功能。

常用的高性能 KV 存储主要有 Redis、Memcached、etcd、Zookeeper 等,其中 Redis 和 Memcached 主要用来缓存业务数据, etcd 和 Zookeeper 主要用来存储元数据。

1.Redis 高可用原理及部署方法

它主要通过支持主从模式哨兵模式集群模式这三种模式,来满足不同业务特点和可用等级的需求。 其中,主从模式部署最简单,用得也最多,集群模式比较复杂,但可用性最高。

Redis 的部署方法

主从模式

主从模式最大的优点是部署简单,最少两个节点便可以构成主从模式,并且可以通过读写分离避免读和写同时不可用。不过,一旦 Master 节点出现故障,主从节点就无法自动切换,直接导致 SLA 下降。所以,主从模式一般适合业务发展初期,并发量低,运维成本低的情况。

哨兵模式

哨兵模式适合读请求远多于写请求的业务场景,比如在秒杀系统中用来缓存活动信息。

集群模式

虽然集群模式避免了 Master 单节点的问题,但集群内同步数据时会占用一定的带宽。所以,只有在写操作比较多的情况下人们才使用集群模式,其他大多数情况,使用哨兵模式都能满足需求。

etcd 高可用原理及部署方法

etcd 支持 HTTPS 访问、划分命名空间、 RBAC 权限控制,可以有效保证数据安全。

Raft 协议是 etcd 中保证分布式系统强一致性的算法。Raft 协议维护了集群节点的角色状态——Leader(领导者)、Follower(跟随者)、Candidate(候选者)。

 

Web 安全:如何解决重放攻击和 XSS 注入?

Web 安全风险有哪些?

什么是 OWASP 呢?OWASP 是 Open Web Application Security Project 的缩写,它是一个组织,中文名称叫“开放式Web应用程序安全项目”。OWASP 每年都会发布排名前十的 Web 安全风险,也就是前面提到的 OWASP Top 10 。比如 2020 年 OWASP Top 10 如下:

  1.     注入
  2.     失效身份验证和会话管理
  3.     敏感信息泄露
  4.     XML 外部实体注入攻击(XXE)
  5.     存取控制中断
  6.     安全性错误配置
  7.     跨站脚本攻击(XSS)
  8.     不安全的反序列化
  9.     使用具有已知漏洞的组件
  10.     日志记录和监控不足

“注入”允许攻击者上传一段可解释执行的代码片段,而该代码片段可能帮助攻击者获取系统管理权限或者隐私信息。比如,黑客可能通过“SQL 注入”获取数据库表中所有的用户名和密码,或者通过“shell 脚本注入”等方式获取系统管理权限,进而危害整个系统的安全。

XXE 主要是将恶意代码注入 XML 文件中,利用服务器解析 XML 文件的漏洞来获取服务器上的额外数据。而 XSS 主要是利用浏览器渲染前端页面的漏洞,将恶意代码注入页面的数据中,窃取用户信息或执行恶意操作。

“注入”的攻击对象主要是两类:

  • 针对系统的注入,主要用于在系统中执行恶意代码,如利用“shell脚本注入”上传获取系统权限的脚本;

  • 针对用户的注入,主要用于获取用户身份信息并执行损害用户利益的操作,如利用 XSS 获取用户登录信息并发起转账请求。

除了以上因系统自身缺陷导致的风险外,攻击者还有一些利用网络缺陷发起的攻击手段,比如:中间人攻击、重放攻击、DDoS 攻击。

中间人攻击, 简单来说就是攻击者通过技术手段,侵入发送方和接收方之间的计算机(如网络代理服务),拦截网络请求窃取并篡改数据。我们通常可以利用加密等方式有效防止中间人攻击,如使用 HTTPS 代替 HTTP。

什么是重放攻击呢?所谓重放攻击,就是把以前发过的数据原封不动地重新发送给接收方。这个发过的数据,可能是攻击者窃听到的别人的数据,也有可能是攻击者曾经自己发过的数据。

重放攻击通常用来恶意消耗 Web 系统有限的资源,比如某电商平台的竞争对手或者恶意用户可能利用重放攻击来恶意占用爆款商品的库存,或者在秒杀活动的时候快速刷走活动商品。我们熟悉的“黄牛”,就是利用“秒杀器”之类的工具,快速提交重复的请求来刷走秒杀商品。

DDoS 攻击又是什么呢?DDoS的全称是 Distributed Denial of Service,即“分布式拒绝服务”,它的前身是 DoS (Denial of Service,拒绝服务)。DDoS 攻击通常是利用大量计算机同时向服务器发送大量垃圾数据,让服务器因为资源限制而无法处理正常的用户请求。

重放攻击和 DDoS 攻击有个相同点:都是利用大量请求快速占用系统资源。有时候,当重放攻击的请求量达到一定量级(如单节点达到 1000 次),就发生质的转变,可视为 DDoS 攻击。目前各大云产商基本都有预防 DDoS 攻击的能力。

如何预防重放攻击?

通常有三种方法预防范重放攻击:加随机数、加时间戳、加序列号。

加随机数,我们可以让后端生成一个随机数给前端,并将该随机数记录为未使用,同时设置有效期。在后续前后端交互中,后端会接收前端请求,并校验是否有该随机数,是否未使用,是否未过期。如果请求中没有该参数,或者该随机数已使用或者已过期,则拒绝请求。

这个方法的优点是不容易伪造,但它需要额外保存使用过的随机数,若记录的时间段较长,则保存和查询的开销较大。

加时间戳, 我们可以在前端生成一个时间戳,然后把它作为参数发给后端,后端校验该时间戳与当前时间是否相差在一个允许的范围内(比如 10 秒),如果超过了,则拒绝请求。

该方法优点是不用像加随机数那样额外保存其他信息,但前端和后端认证双方需要准确的时间同步,同步越好,受攻击的可能性就越小。所以,当系统变得庞大时,跨越的区域较广,要做到精确的时间同步就变得不容易了。

加序列号, 每次请求或返回的时候,我们都可以在上一次请求或返回的序号上加 1 ,并将当前序号记录下来。如果收到不连续的序号,则认为是非法数据。

该方法优点是不需要时间同步,保存的信息量比随机数方式小。缺点是一旦攻击者对报文解密成功,就可以获得流水号,从而每次将流水号递增欺骗认证端。

我们都知道,一个正常用户在发起抢购商品的请求前,需要先进入秒杀活动页或者商品详情页,而这两个页面都会请求后端接口,以获取活动信息和商品信息。基于此,我们就可以利用这两个页面来做文章。

具体来说,就是在这两个页面调用后端接口的时候,由后端给前端下发一个短时间内可以作为唯一标识的东西,比如用随机数和时间戳拼接得到的数字。同时,后端将这个唯一标识记录下来,有效期设置为活动结束时间。

当用户在前端发出请求抢购商品的时候,后端从抢购接口处接收到请求,此时它先判断是否有该唯一标识,如果没有该标识或者该标识已经被用过,则拒绝请求。

由于唯一标识的生成算法是由后端控制,攻击者无法伪造,只能通过接口获取。所以,当攻击者通过接口获取唯一标识的时候,我们可以对攻击者进行常规限流,如限制每个 IP 每秒请求数。

需要注意的是,秒杀系统最好开启代理层的粘性会话(Sticky Session)功能,以便将同一个用户的请求转发到同一个服务节点。这样就可以利用服务节点的本地内存缓存,来快速校验唯一标识,以及针对 IP 限流。

如何防 XSS ?

前面提到了“注入”的风险,比如在提交表单的时候,攻击者可能在表单某个字段里填上一段可被系统执行的脚本代码。如果系统没有安全校验并执行了该代码的话,可能就会遭到攻击者入侵,导致用户数据泄漏。

举个例子,某系统查询账号信息的 SQL 可能是这样拼接而成的:

"SELECT * FROM accounts WHERE custID='" + ID + "'"

如果攻击者将参数 ID 填写为:

or '1'='1'

这将会导致所有账号信息被泄漏。

XSS 产生的本质原因和这个类似,也是通过前端传入非法脚本代码到后端。只不过与 SQL 注入不同的是,XSS 是在前端获取用户信息并发起非法请求,而 SQL 注入是在后端获取用户信息。

我们要怎么防范 XSS 风险呢?

首先,我们要对前端输入的数据进行校验,过滤掉非法关键字。比如,我们可以让前端对商品图片 URL 输入框的数据进行校验,过滤掉诸如“javascript:”这样的前端关键字。

其次,后端对前端输入的数据进行转码后,再存入数据库,如进行 URLEncode 和 Escape 。在拼接 SQL 语句的时候,不要用简单的字符串拼接,而是利用占位符和参数通过 SQL 语句生成函数的来生成,如下所示:

sql := mysql.Raw("SELECT * FROM accounts WHERE custID='?'", ID)

这样可以利用 SQL 语句生成函数来校验参数合法性。
最后,前端在页面渲染的时候,也需要注意对一些关键字段进行字符转义,比如对字符串中的单引号(')、双引号(")、尖括号(<>)进行转义,就能防止前端利用这些数据渲染页面的时候执行攻击者注入的 javascript 代码。

引自:拉勾打造千万级流量系统

Logo

开源、云原生的融合云平台

更多推荐