引言
优先级低,随着服务节点增多优先级增高,系统拆分为微服务后,数据分散在各个微服务节点上。从系统连接的角度来说,任意微服务都可以访问所有其他微服务节点;但从业务的角度来说,部分敏感数据或者操作,只能部分微服务可以访问,而不是所有的微服务都可以访问,因此需要设计服务安全机制来保证业务和数据的安全性。
服务安全主要分为三部分:接入安全、数据安全、传输安全。通常情况下,服务安全可以集成到配置中心系统中进行实现,即配置中心配置微服务的接入安全策略和数据安全策略,微服务节点从配置中心获取这些配置信息,然后在处理具体的微服务调用请求时根据安全策略进行处理。由于这些策略是通用的,一般会把策略封装成通用的库提供给各个微服务调用。基本架构如下:
微服务架构下,有以下三种方案
可以选择,用的最多的是OAuth模式。
- 网关鉴权模式(API Gateway)
- 服务自主鉴权模式
- API Token模式(OAuth2.0)
①网关鉴权模式(API Gateway)
通过上图可见,因为在微服务的最前端一般会有一个API网关模块(API Gateway),所有的外部请求访问微服务集群时,都会首先通过这个API Gateway,所以我们可以在这个模块里部署auth逻辑,实现统一集中鉴权,鉴权通过后,再把请求转发给后端各个服务。
这种模式的优点就是,由API Gateway集中处理了鉴权的逻辑,使得后端各微服务节点自身逻辑就简单了,只需要关注业务逻辑,无需关注安全性事宜。
这个模式的问题就是,API Gateway适用于身份验证和简单的路径授权(基于URL的),对于复杂数据/角色的授权访问权限,通过API Gateway很难去灵活的控制,毕竟这些逻辑都是存在后端服务上的,并非存储在API Gateway里。
②服务自主鉴权模式
服务自主鉴权就是指不通过前端的API Gateway来控制,而是由后端的每一个微服务节点自己去鉴权。
它的优点就是可以由更为灵活的访问授权策略,并且相当于微服务节点完全无状态化了。同时还可以避免API Gateway 中 auth 模块的性能瓶颈。
缺点就是由于每一个微服务都自主鉴权,当一个请求要经过多个微服务节点时,会进行重复鉴权,增加了很多额外的性能开销。
③API Token模式(OAuth2.0)
OAuth2.0是一种访问授权协议框架。它是基于Token令牌的授权方式,在不暴露用户密码的情况下,使 应用方 能够获取到用户数据的访问权限。
例如:你开发了一个视频网站,可以采用第三方微信登陆,那么只要用户在微信上对这个网站授权了,那这个网站就可以在无需用户密码的情况下获取用户在微信上的头像。
如图,这是一种采用基于令牌Token的授权方式。在这个模式下,是由授权服务器(图中Authorization Server)、API网关(图中API Gateway)、内部的微服务节点几个模块组成。
流程如下:
第一步:客户端应用首先使用账号密码或者其它身份信息去访问授权服务器(Authorization Server)获取 访问令牌(Access Token)。
第二步:拿到访问令牌(Access Token)后带着它再去访问API网关(图中API Gateway),API Gateway自己是无法判断这个Access Token是否合法的,所以走第三步。
第三步:API Gateway去调用Authorization Server校验一下Access Token的合法性。
第四步:如果验证完Access Token是合法的,那API Gateway就将Access Token换成JWT令牌返回。
(注意:此处也可以不换成JWT而是直接返回原Access Token。但是换成JWT更好,因为Access Token是一串不可读无意义的字符串,每次验证Access Token是否合法都需要去访问Authorization Server才知道。但是JWT令牌是一个包含JOSN对象,有用户信息和其它数据的一个字符串,后面微服务节点拿到JWT之后,自己就可以做校验,减少了交互次数)。
第五步:API Gateway有了JWT之后,就将请求向后端微服务节点进行转发,同时会带上这个JWT。
第六步:微服务节点收到请求后,读取里面的JWT,然后通过加密算法验证这个JWT,验证通过后,就处理请求逻辑。
网站的安全架构——对外安全接口
一、密码学概念
1.明文、密文、密钥、加密、解密
- 明文:指没有经过加密的信息/数据。
- 密文:明文被加密算法加密之后,会变成密文,以确保数据安全。
- 密钥:是一种参数,它是在明文转换为密文或将密文转换为明文的算法中输入的参数。密钥分为对称密钥与非对称密钥。
- 加密:将明文变成密文的过程,确保数据传输过程中的不可读性
- 解密:将密文还原为明文的过程。
2.对称加密、非对称加密
①对称加密
加密和解密使用相同密钥的加密算法。代表:DES算法,RC算法。速度快,系统开销小,适合大量数据加密。缺点在于加解密使用同一个秘钥,远程通信的情况下如何安全的交换秘钥是一个难题。
②非对称加密
非对称加密算法需要两个密钥(公开密钥和私有密钥)。公钥与私钥是成对存在的,如果用公钥对数据进行加密,只有对应的私钥才能解密。
其中一个对外公开,称为公钥,另一个只有所有者知道,称为私钥。用公钥加密的信息只有私钥才能解开,反之,用私钥加密的信息只有公钥才能解开(签名验签)。
需要注意的是公钥和私钥都可以用来加密,也都可以用来解密,并不是说规定死了只能私钥来加密公钥来解密,或者公钥来加密私钥来解密,但这个加解密必须是一对密钥之间的互相加解密,否则不能成功。
代表:RSA算法。速度慢,适合少量数据加密。对称加密算法不能实现签名,因此签名只能非对称算法。
3.单向散列加密
单向散列加密是通过对不同输入长度的信息进行散列计算,得到固定长度的密文输出,单向的(不可逆)。
用户的密码通过此加密将密文存在数据库当中,登录时根据用户输入的密码进行加密后与数据库中的密文进行对比。
这样即使平台数据库被“拖库”,也不会造成用户的密码泄漏。但是虽然不能通过算法将单向散列密文反算得到明文,但是由于人们设置密码具有一定的模式,因此通过彩虹表(人们常用密码和对应的密文关系表)等手段可以进行猜测是破解。为了加强单向散列计算的安全性,还会给三列算法加点盐(salt),salt相当于加密的秘钥,增加破解难度。代表:MD5算法、SHA算法
不能在代码中写死盐,且盐需要有一定的长度(盐写死太简单的话,黑客可能注册几个账号反推出来)
每一个密码都有独立的盐,并且盐要长一点,比如超过 20 位。(盐太短,加上原始密码太短,容易破解)
最好是随机的值,并且是全球唯一的,意味着全球不可能有现成的彩虹表给你用。
Spring Security 已经废弃了 MessageDigestPasswordEncoder,推荐使BCryptPasswordEncoder,也就是BCrypt来进行密码哈希。BCrypt 生而为保存密码设计的算法,相比 MD5 要慢很多。BCrypt比MD5慢几十倍,黑客想暴力破解的话,就需要花费几十倍的代价。因此一般情况,建议使用Bcrypt来存储用户的密码
二、加签验签
加密没有办法验证发送方的身份,所以需要签名
1.加签验签概念
「加签」:用Hash函数把原始报文生成报文摘要,然后用私钥对这个摘要进行加密,就得到这个报文对应的数字签名。通常来说呢,请求方会把「数字签名和报文原文」一并发送给接收方。
「验签」:接收方拿到原始报文和数字签名后,用「同一个Hash函数」从报文中生成摘要A。另外,用对方提供的公钥对数字签名进行解密,得到摘要B,对比A和B是否相同,就可以得知报文有没有被篡改过。
2.为什么需要加签验签
中间人Actor截取了C的公钥,他把自己的公钥发给了A公司,A误以为这就是C公司的公钥。A在发起转账时,用Actor的公钥,对请求报文加密,加密报文到在传输过程,Actor又截取了,这时候,他用自己的私钥解密,然后修改了报文,再用C的公钥加密,发给C公司,C公司收到报文后,继续用自己的私钥解密。
这就需要「加签验签」表明身份和报文真实性。A公司把自己的公钥也发送给C公司,私钥自己保留着。在发起转账时,先用自己的私钥对请求报文加签,于是得到自己的数字签名。再把数字签名和请求报文一起发送给C公司。C公司收到报文后,拿A的公钥进行验签,如果原始报文和数字签名的摘要内容不一致,那就是报文被篡改。
因为消息摘要算法(hash算法)无法逆向解开的,只能起验证的作用,所以截取了报文想要修改其中参数同时修改签名是不可能的。
- 用来互相验证接收方和发送方的身份;
- 在验证身份的基础上再验证一下传递的数据是否被篡改过。
使用数字签名可以用来达到数据的明文传输。
3.先签名后加密(一般只需要签名即可)
先签名后加密是指先对消息进行签名,然后对消息的签名值和消息一起进行加密。如果采用先加密后签名的方式,接收方只能知道该消息是由签名者发送过来的,但并不能确定签名者是否是该消息的创建者。比如在发送一个认证凭据时采用先加密后签名的方式,消息在发送过程中就有可能被第三方截获并将认证凭据密文的签名值修改为自己的签名,然后发送给接收方。第三方就有可能在不需知道认证凭据的情况下通过这种方式来通过认证获取权限。
如果使用“先加密后签名”,则消息发送方Alice和消息接收方Bob的处理过程如下图:
Alice发送的明文消息先经过Alice私钥签名,再将消息明文和消息签名一起使用Bob的公钥加密,密文经过网络传输到Bob。因为Bob使用私钥解密还原出消息明文和签名,而私钥只有Bob持有,这能保证消息明文不会泄露给第三方Eve,然后Bob使用Alice的公钥验证消息明文和签名是否一致,因为Alice的签名只有Alice的公钥才能验证,这能保证消息一定来自Alice,不可抵赖,且没有被第三方Mallory篡改。在拿不到消息明文的情况下,无法计算出消息签名,而还原消息明文又必须使用Bob的私钥,这样就能防止网络传输过程中的中间人进行篡改攻击。
假如“先加密后签名”,会存在什么样的攻击风险呢,先看下“先加密后签名”的处理流程:
如果存在一个中间人Mallory可以拦截Alice和Bob的消息,在不篡改消息明文和密文的情况下,使用Mallory的私钥对消息密文进行签名,并替换Alice原始的签名,最后篡改后的消息传输给接收方Bob,Bob仍然可以成功解密明文,同时用Mallory的公钥成功验证签名,最终这条消息会被Bob认为是Mallory发送的合法消息。
“先加密后签名”似乎一定不安全,是这样吗?中间人Mallory针对“先加密后签名”进行替换签名攻击得手的前提条件:
- 接收方Bob根据签名内容中的证书ID找到对应的Mallory公钥来验证签名。
- 接收方Bob仅使用公钥验签来识别发送方的身份。
只要打破上面的任意一个前提,“先加密后签名”也是安全的:
- Bob使用固定的公钥来验证签名,而不是根据签名内容来找对应公钥,或者不使用多个公钥尝试验签。即Mallory替换签名后,Bob仍然用Alice的公钥验证签名,肯定能发现请求被篡改。
- Bob使用公钥+应用层属性一起识别发送方的身份,假设Alice在消息明文中携带自己的用户ID,Bob验证公钥和用户ID是否匹配,即使Mallory替换签名也无法攻击。
三、常见加密相关算法简介
1.消息摘要算法
- 相同的明文数据经过相同的消息摘要算法会得到相同的密文结果值。
- 数据经过消息摘要算法处理,得到的摘要结果值,是无法还原为处理前的数据的。
- 数据摘要算法也被称为哈希(Hash)算法或散列算法。
- 消息摘要算法一般用于签名验签。
消息摘要算法主要分三类:MD(Message Digest,消息摘要算法)、SHA(Secure Hash Algorithm,安全散列算法)和MAC(Message Authentication Code,消息认证码算法)。
①MD家族
- MD2,MD4,MD5 计算的结果都是是一个128位(即16字节)的散列值,用于确保信息传输完整一致。
- MD2的算法较慢但相对安全,MD4速度很快,但安全性下降,MD5则比MD4更安全、速度更快。
- MD5被广泛应用于数据完整性校验、数据(消息)摘要、数据加密等。
- MD5,可以被破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞攻击,因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。
②ShA家族算法
它是在MD算法基础上实现的,与MD算法区别在于「摘要长度」,SHA 算法的摘要「长度更长,安全性更高」。
- SHA-0发布之后很快就被NSA撤回,因为含有会降低密码安全性的错误,它是SHA-1的前身。
- SHA-1在许多安全协议中广为使用,包括TLS、GnuPG、SSH、S/MIME和IPsec,是MD5的后继者。
- SHA-2包括SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。它的算法跟SHA-1基本上相似,目前还没有出现明显弱点。
- SHA-3是2015年正式发布,由于对「MD5出现成功的破解」,以及对SHA-0和SHA-1出现理论上破解的方法,SHA-3应运而生。它与之前算法不同的是,它是可替换的加密散列算法。
③MAC算法家族
MAC算法 MAC(Message Authentication Code,消息认证码算法),是带密钥的Hash函数。输入密钥和消息,输出一个消息摘要。它集合了MD和SHA两大系列消息摘要算法。
- MD 系列算法: HmacMD2、HmacMD4 和 HmacMD5 ;
- SHA 系列算法:HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384 和 HmacSHA512 。
2.对称加密算法
加密和解密使用「相同密钥」的加密算法就是对称加密算法。
①DES
数据加密标准(Data Encryption Standard,缩写为 DES)是一种对称密钥加密块密码算法。DES算法的入口参数有三个:Key、Data、Mode。
- Key: 7个字节共56位,是DES算法的工作密钥;
- Data: 8个字节64位,是要被加密或被解密的数据;
- Mode: 加密或解密。
②3DES
三重数据加密算法(Triple Data Encryption Algorithm,又称3DES(Triple DES),是一种对称密钥加密块密码,相当于是对每个数据块应用三次数据加密标准(DES)算法。
③AES
AES,高级加密标准(Advanced Encryption Standard),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。
- 采用对称分组密码体制,密钥长度为 128 位、 192 位、256 位,分组长度128位
- 相对于DES ,AES具有更好的 安全性、效率 和 灵活性。
3.非对称加密算法
非对称加密算法需要两个密钥:公钥和私钥。公钥与私钥是成对存在的,如果用公钥对数据进行加密,只有用对应的私钥才能解密。
公钥和私钥只是一个相对概念,不能单纯的去称呼一对密钥中的一个为公钥,另一个为私钥,它们的公私性总是相对于生成者来说的。一对密钥生成后,保存在生成者手里的就是生成者私钥,生成者发布出去的就是生成者公钥。(私钥加密,公钥解密)
①RSA算法
- RSA加密算法是一种非对称加密算法,广泛应用于加密和数字签名
- RSA算法原理:两个大素数的乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
- RSA是被研究得最广泛的公钥算法,从提出到现在,经历了各种攻击的考验,普遍认为是目前最优秀的公钥方案之一。
②DSA
- DSA(Digital Signature Algorithm,数字签名算法),也是一种非对称加密算法。
- DSA和RSA区别在,DSA仅用于数字签名,不能用于数据加密解密。其安全性和RSA相当,但其性能要比RSA好。
③ECC 算法
- ECC(Elliptic Curves Cryptography,椭圆曲线密码编码学),基于椭圆曲线加密。
- Ecc主要优势是,在某些情况下,它比其他的方法使用更小的密钥,比如RSA加密算法,提供相当的或更高等级的安全级别。
- 它的一个缺点是,加密和解密操作的实现比其他机制时间长 (相比RSA算法,该算法对CPU 消耗严重)。
4.国密算法
国密即国家密码局认定的国产密码算法。为了保障商用密码的安全性,国家商用密码管理办公室制定了一系列密码标准,即SM1,SM2,SM3,SM4等国密算法。
①SM1
- SM1,为对称加密算法,加密强度为128位,基于硬件实现。
- SM1的加密强度和性能,与AES相当。
②SM2
- SM2主要包括三部分:签名算法、密钥交换算法、加密算法
- SM2用于替换RSA加密算法,基于ECC,效率较低。
③SM3
- SM3,即国产消息摘要算法。
- 适用于商用密码应用中的数字签名和验证,消息认证码的生成与验证以及随机数的生成。
④SM4
- SM4是一个分组算法,用于无线局域网产品。
- 该算法的分组长度为128比特,密钥长度为128比特。
- 加密算法与密钥扩展算法都采用32轮非线性迭代结构。
- 解密算法与加密算法的结构相同,只是轮密钥的使用顺序相反,解密轮密钥是加密轮密钥的逆序。
- 它的功能类似国际算法的DES。
四、开发接口防止篡改应用
为开发者分配AccessKey(开发者标识,确保唯一)和SecretKey(用于接口加密,确保不易被穷举,生成算法不易被猜测)。
1.参数签名
按照请求参数名的字母升序排列非空请求参数(包含AccessKey),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA;
在stringA最后拼接上Secretkey得到字符串stringSignTemp;
对stringSignTemp进行MD5运算,并将得到的字符串所有字符转换为大写,得到sign值。
请求携带参数AccessKey和Sign,只有拥有合法的身份AccessKey和正确的签名Sign才能放行。
这样就解决了身份验证和参数篡改问题,即使请求参数被劫持,由于获取不到SecretKey和机密算法,无法伪造合法的请求。
2.重放攻击
虽然解决了请求参数被篡改的隐患,但是还存在着重复使用请求参数伪造二次请求的隐患。
①timestamp+nonce方案
nonce指唯一的随机字符串,用来标识每个被签名的请求。通过为每个请求提供一个唯一的标识符,服务器能够防止请求被多次使用(记录所有用过的nonce以阻止它们被二次使用)。
然而,对服务器来说永久存储所有接收到的nonce的代价是非常大的。可以使用timestamp来优化nonce的存储。
假设允许客户端和服务端最多能存在15分钟的时间差,同时追踪记录在服务端的nonce集合。
当有新的请求进入时,首先检查携带的timestamp是否在15分钟内,如超出时间范围,则拒绝,然后查询携带的nonce,如存在已有集合,则拒绝。否则,记录该nonce,并删除集合内时间戳大于15分钟的nonce(可以使用redis的expire,新增nonce的同时设置它的超时失效时间为15分钟)。
②Token&AppKey(APP)
在APP开放API接口的设计中,由于大多数接口涉及到用户的个人信息以及产品的敏感数据,所以要对这些接口进行身份验证,为了安全起见让用户暴露的明文密码次数越少越好,然而客户端与服务器的交互在请求之间是无状态的,也就是说,当涉及到用户状态时,每次请求都要带上身份验证信息。
Token身份验证
用户登录向服务器提供认证信息(如账号和密码),服务器验证成功后返回Token给客户端;
客户端将Token保存在本地,后续发起请求时,携带此Token;
服务器检查Token的有效性,有效则放行,无效(Token错误或过期)则拒绝。
安全隐患:Token被劫持,伪造请求和篡改参数。
Token+AppKey签名验证
与上面开发平台的验证方式类似,为客户端分配AppKey(密钥,用于接口加密,不参与传输),将AppKey和所有请求参数组合成源串,根据签名算法生成签名值,发送请求时将签名值一起发送给服务器验证。
这样,即使Token被劫持,对方不知道AppKey和签名算法,就无法伪造请求和篡改参数。再结合上述的重发攻击解决方案,即使请求参数被劫持也无法伪造二次重复请求。
3.密码如何安全传输
使用https 协议 + 非对称加密算法(如RSA)来传输用户密码,为了更加安全,可以在前端构造一下随机因子。
使用BCrypt + 盐存储用户密码。日志输出脱敏。
在感知到暴力破解危害的时候,「开启短信验证、图形验证码、账号暂时锁定」等防御机制来抵御暴力破解。
数据输出展示:前端展示需要防止水平越权,另外,前端的展示可以埋点和方便数据采集。