代码改变世界

iOS应用千万级架构:安全与加密

2020-08-26 15:28  jiangys  阅读(959)  评论(0编辑  收藏  举报

前言

不管项目的大小,在项目中,安全和加密都是必须要全面去考虑,在做一个新业务时,我们都需要问一下自己:

  • 这个业务场景需要考虑加密吗?
  • 究竟选用哪些加密算法好呢?
  • 这个加密算法的安全性怎么样呢?
  • 这个加密算法的性能怎么样?
  • 这个加密算法适合做大数据加密吗?

这些问题都需要我们去探讨和对比。不同的场景下,是需要使用不到的加密算法的,有一些底层不需要解密,只需要做相应的验签即可。下面就项目中用到的一些场景和大家进行讨论。

加密算法选用

HTTPS

优点

  1. 使用 HTTPS 协议可认证用户和服务器,确保数据发送到正确的客户机和服务器 
  2. HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,要比 HTTP 协议安全,可防止数据在传输过程中被窃取、改变,确保数据的完整性 
  3. HTTPS 是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本

缺点

  1. 相同网络环境下,HTTPS 协议会使页面的加载时间延长近 50%,增加 10%到 20%的耗电。此外,HTTPS 协议还会影响缓存,增加数据开销和功耗
  2. HTTPS 协议的安全是有范围的,在黑客攻击、拒绝服务攻击和服务器劫持等方面几乎起不到什么作用
  3. 最关键的是,SSL 证书的信用链体系并不安全。特别是在某些国家可以控制 CA 根证书的情况下,中间人攻击一样可行 
  4. 成本增加。部署 HTTPS 后,因为 HTTPS 协议的工作要增加额外的计算资源消耗,例如 SSL 协议加密算法和 SSL 交互次数将占用一定的计算资源和服务器成本。在大规模用户访问应用的场景下,服务器需要频繁地做加密和解密操作,几乎每一个字节都需要做加解密,这就产生了服务器成本。随着云计算技术的发展,数据中心部署的服务器使用成本在规模增加后逐步下降,相对于用户访问的安全提升,其投入成本已经下降到可接受程度 

RSA

由于进行的都是大数计算,使得RSA最快的情况也比DES慢上好几倍,无论是软件还是硬件实现。速度一直是RSA的缺陷。一般来说只用于少量数据加密。RSA的速度比对应同样安全级别的对称密码算法要慢1000倍左右。

公钥加密、私钥解密、私钥签名、公钥验签

AES

高级数据加密标准,能够有效抵御已知的针对DES算法的所有攻击 。密钥建立时间短、灵敏性好、内存需求低、安全性高

3DES

3DES是DES加密算法的一种模式,它使用3条64位的密钥对数据进行三次加密。数据加密标准(DES)是美国的一种由来已久的加密标准,它使用对称密钥加密法。.密钥长度112位或168位,通过增加迭代次数提高安全性 。处理速度慢、密钥计算时间长、加密效率不高

SHA1

主要适用于数字签名

SHA1对任意长度明文的预处理和MD5的过程是一样的,即预处理完后的明文长度是512位的整数倍,但是有一点不同,那就是SHA1的原始报文长度不能超过2的64次方,然后SHA1生成160位的报文摘要。SHA1算法简单而且紧凑,容易在计算机上实现。

●  安全性:SHA1所产生的摘要比MD5长32位。若两种散列函数在结构上没有任何问题的话,SHA1比MD5更安全。

●  速度:两种方法都是主要考虑以32位处理器为基础的系统结构。但SHA1的运算步骤比MD5多了16步,而且SHA1记录单元的长度比MD5多了32位。因此若是以硬件来实现SHA1,其速度大约比MD5慢了25%。

●  简易性:两种方法都是相当的简单,在实现上不需要很复杂的程序或是大量存储空间。然而总体上来讲,SHA1对每一步骤的操作描述比MD5简单。

应用场景

一、请求方式将Get请求改为Post

安全角度解析:

1. 有些IDC只劫持URL,POST数据不在URL   

2.另外POST方便做高强度加密,GET的话做加密容易导致URL过长被截断  

3.部分WEB 攻击只对GET有效

因此,支付业务相关的所有接口,安全这边都要求将所有的Get改为Post。

二、敏感信息加密

涉及到用户的身份证号码、银行卡卡号、信用卡有效期、信用卡 CVV2、等敏感信息时,在用户输入后需要进行加密处理,将密文数据传给服务端。

传输给后台接口的格式为:<加密类型>|<版本号>|<密文数据>

RSA 公钥加密

加密类型:RSA 公钥类型时“加密类型”值为 0

 RSA 公钥加解密流程如下:

  • 服务端生成公私钥对(由于 RSA 1024 已不再安全,因此提供的 RSA 密钥长度为 2048bit),使用 RSA 公钥进行加密,服务端使用该私钥进行解密
  • 将公钥放在页面或者 APP 中
  • 在用户提交数据时,使用 JavaScript 库或者 APP 库传入公钥对敏感数据进行加密,并将密文件转换为 Base64 后作为密文数据
  • 服务端使用对应版本号的 RSA 私钥对密文进行解密获得明文数据

安全控件

安全控件类型时“加密类型”值为 1

  • 直接将安全控件加密后的密文作为密文数据
  • 服务端使用对应版本号的安全控件解密密钥及算法进行解密获得明文数据

风控参数

对一些开户相关的接口,需要额外增加一些用户行为的参数,让风控去处理。比如

是否阅读XXX协议、协议阅读时间、是否粘贴手机号码、填写手机号码所用时间(单位ms)、填写身份证所用时间(单位ms)、身份证是否为粘贴、银行卡是否为粘贴、四要素页面停留总时常(单位ms)、设备号、IP地址等。

四、支付鉴权与支密安全控件

在支付的时候,风控会返回该用户使用的鉴权方式,比如:短密支付、短信支付、指纹支付等。对于高风险的用户,会同时要求多种鉴权组合,比如:短密输入校验通过,还要求输入短信验证码。

短密支付

可以接入安全密码控件,比如使用:微通新成 -- 密码卫士 http://www.microdone.cn/service/

在支付过程中,用户输入密码,短密和长密 都是使用支付密码安全控件完成。

1、先调用接口,拿到该用户的 密码控件公钥、密码控件随机数、盐值、当前时间戳

2、将上面接口拿到的参数,传给安全密码控件,用户输入文本时,就会返回一串RSA加密的字符串。

3、拼接成支付密码密文(<加密类型>|<版本>|<Base64 表示的支付密码密文>)格式,传给后台进行支付

降级方案

这里需要有一套降级方案,毕竟安全密码控件是第三方的。需要备用一套普通密码控件,加密逻辑为:

1、调用接口,拿到该用户的RSA公钥、盐值

2、MD5对密码加密,得到A,MD5(A+盐值),从第8开始取,一共取16位

比如:短密为:123456,盐值为:YneNNN 

A = MD5(123456) 得到 e10adc3949ba59abbe56e057f20f883e

B = MD5(e10adc3949ba59abbe56e057f20f883eYneNNN) 得到 b102f5274bc6bc609c41018367fa3273

C = 第8开始取,一共取16位,得到 4bc6bc609c410183。

最后加密得到的就是 4bc6bc609c410183

3、对第2步拿到的字符串,进行RSA加密,传给后台

五、整包加密

基本概念:涉及到安全要求很高的接口,需要整包加密,比如:指纹开通、手机号注册并绑定、请求设备指纹、短密开通等

保留参数:api_key1,api_key2,api_key3,api_key4,eversion,timestamp六大参数为保留参数,不参与加密。其它参数参与加密,最后和不参数加密的参数拼接成:

api_key1=xxxx&api_key2=xxxx&api_key3=xxxx&api_key4=xxxx&eversion=xxx&timestamp=1595906190&edata=xxxxx

加密计算规则

  • 第一步:根据加密密钥规则,得到加密secret。可以写死在APP里,也可以通过接口拿到
  • 第二步;根据加密数据规则,得到待加密的text,如:name1=value1&name2=value2&name3=value3
  • 第三步:随机产生16个字节的IV(Initialization Vector)
  • 第四步:MD5(secret),得到16个字节的密钥key
  • 第五步:使用AES加密得到加密后的encrypted bytes。key,text,IV都已在前面的步骤产生。mode=CBC,padding=Pkcs7
  • 第六步:字节数组连接,IV bytes + encrypted bytes,得到最终output bytes
  • 第七步:Base64(output bytes),得到加密后的字符串encrypted string
  • 第八步:增加请求参数edata=encrypted string,推荐使用POST发送

六、接口加签

所有请求参数按参数名字母顺序排序,拼接参数名和参数值,即base string=name1=value1&name2=value2&name3=value3

签名计算规则

  • 第一步:SHA1(secret+base string).toHex(),得到hash1
  • 第二步:SHA1(secret+hash1).toHex(),得到最终签名

即hash=SHA1(secret+SHA1(secret+base string).toHex()).toHex()。注意,+表示字符串连接,实际拼接中不需要。

secret :APP里固定死写的一串32位字符串,也可以理解为是盐值。