写点什么

如何构建安全的 App 网络通信?

作者:ZA技术社区
  • 2023-10-11
    上海
  • 本文字数:4879 字

    阅读完需:约 16 分钟

如何构建安全的App网络通信?

前言


说到安全肯定逃不开数据的加解密,数据本地存储大多用对称加解密来实现,那网络传输数据的时候是不是也用对称加解密来实现?没错,常规网络通信时,大部分网络传输过程中基本也是用对称加解密来实现的,毕竟时间宝贵。如果使用非对称加密方式,需要花 N 多时间等数据加密后传输出去,或者解密出来。只是用对称加解密有个致命问题就是密钥 Key 的交换,毕竟网络是一个不可靠加不安全的通信环境。这个时候就需要启用另外一种加解密方式——非对称加解密。


不过想要做出一个安全的加密通信信道不是简单的用个对称加密和非对称加密可以完成的。在说网络安全之前,我们先讲几个比较基础的概念。
复制代码


1.1 单向函数第一个要说的就是“单向函数”。假设现在存在一个函数,函数式为 f(X)=Y。输入 X,很容易就能得出结果 Y。但是在知道结果 Y,想反向算出输入值 X 时非常困难(就是用现有最快的计算机来计算,也得计算个几百上千年才能得出结果的这种)。这种函数就叫单向函数。其中最简单的就是整数相乘。例如 3*7=21;知道 21,很容易就能猜出来,可能的结果有【1,21】、【3、7】这两组。但是将整数的位数提高到几百位,就很难反推出来。


由此也能很容易的看出来单向函数不能用于数据加密。因为经过单向函数加密的数据谁都不能解开,用它来加密数据没有任何意义。单向函数一般两个用途:1、生成信息摘要,或者数据指纹。比如说我们非常熟悉的 MD5、SHA1 算法就是具有单向函数性质的摘要算法。2、密码保护。一般用户登录时给服务器提交明文密码是非常危险的,非常容易被抓包。就算千幸万苦的提交到服务器端,服务器端保存不当照样会造成用户密码泄漏。此时就需要将用户密码经单向函数计算,然后将计算出的函数值存储在服务器端。用户登录时,客户端只需要提交用户密码计算出的函数值,服务器端用接收到的函数值和本地存储的值进行对比即可。


1.2 单向陷门函数单向限门函数是一类特殊的单向函数。是一类存在“陷门”或者说“后门”的单向函数。还是上文提到的单向函数 f(x)=y,已知函数计算方法和输入值 x 时非常容易计算得到结果 y,但是反向不能计算。此时如果再提供一个参数、或者数据 Z,就可以通过 y 反向计算出输入值 X。这个 Z 就被称为后门或者说陷门。例如下图的利用 RSA 算法对数据进行加解密的过程中,私钥就可以被看做是“后门”。



1.3 前向加密安全这个概念说的是:在一个加密通信信道中,用来产生会话的长期加密密钥泄漏,不会造成之前通讯密钥的泄漏。比如说在这个加密信道中发送了 A、B、C、D、E 五次网络请求,在第五次 E 的时候密钥被破解,第五次请求 E 被破译为明文。此时以后的请求 G、H 很难保证安全,但是之前的 ABCD 四次网络请求仍然无法被破译,还是安全的。


1.4 非对称加密非对称加密算法有两个密钥,因为加密解密用的不同的密钥,所以就叫非对称加密。两个密钥,一个公布出去叫公钥,一个自己保留叫私钥。用公钥加密的数据,只有对应的私钥可以解密。用私钥加密的数据,也只有对应的公钥才可以解密。下图就是一个典型的公钥加密,私钥解密的过程。



非对称加密算法有很多种,除了经常听说的 RSA,还有 D-H,ECC(椭圆曲线加密算法)等等算法。


网络安全通信


2.1 安全通信第一版了解了非对称加密算法,我们很自然地就能设计出一个两端通信的安全加密方案。如下图所示:



上述方案看似安全和完美,但是也存在一些问题和缺陷。比如性能差,会受到中间人攻击。
复制代码


2.2 安全通信升级版针对性能差,我们可以采用对称加密来解决。中间人攻击,这个时候就需要用到“数字证书”。数字证书的构成内容比较多,包含颁发者、使用者(持有者)、有效期、公钥等等。这里只说几个简单的知识点。


2.2.1 摘要将使用者信息(这一栏一般是自身的网站域名和公司名等),自己生成的公钥和其他信息打包一起,经过 hash 算法计算,生成的 hash 值就叫做摘要。


2.2.2 签名 CA 证书机构用自己的私钥对摘要进行加密运算,生成加密后的密文,这个密文就被称为是数字签名。


2.2.3 证书将 2.2.1 中的自身信息和摘要,2.2.2 步骤中的数字签名放在一起,就称之为数字证书。


下图就是一个签名生成数字证书和验证数字证书是否正确的流程图:



2.2.4 经典的 TLS 握手过程有了数字证书,再加上对称加密算法,我们就可以构建出一个安全的加密通信信道。


  1. 首先客户端生成一个随机数 k1,然后和服务器端打招呼并将随机数 K1 发送给服务器端。

  2. 服务器端拿到 K1。自己再生成随机数 K2。并且将自身的证书和 K2 发送给客户端。

  3. 客户端拿到服务器端的随机数 K2 和证书后,校验证书是否正确。如果校验成功,那继续进行握手。如果失败,会直接终止。

  4. 客户端生成随机数 K3,使用从证书中得到的服务器端公钥对 K3 进行加密。

  5. 服务器端接收数据后,用自己的私钥对其进行解密,从而得到随机数 K3。

  6. 两端用三个相同的随机数 K1+K2+K3 合并生成一个对称加密算法的 Key。后续就用这个 key 来加密两端通信的数据。



网络通信安全在众安 APP 中的落地


3.1 我们面对的严苛问题看完上面安全知识点介绍和两种网络通信安全方案,我们就明白想构建一个安全的网络通信环境是多么的困难。尤其是我们做的金融 App,那相对于普通 APP 安全等级更是严苛。


首先,金融 APP 不能只是简单的走 Https 协议而不做证书绑定校验(不做证书检验就会导致”中间人“攻击);


第二,App 的网络请求需要配合服务器端做”防重放“处理,防止被人”重放攻击”;


第三,网络请求必须做签名处理,并且签名算法要保密,防止网络请求被篡改;


第四,网络请求数据必须做明文隐藏,防止网络请求被抓包的时候数据被攻击方直接获取;


第五,也是大家比较容易忽视的一点,就是多平台覆盖。很多 App 都做了网络请求安全防护,协议设计上大多只能兼容 ios 和 android 两端,web 端就被忽视掉了;


第六,不能只顾安全不顾性能,App 的网络通信性能必须兼顾到。


3.2 众安 APP 中的落地所以我们需要一个全面、健壮、能兼顾到上面几个安全问题的方案。


参考 TSL 协议,相当于在 Https 请求的基础上再加一道防线,我们设计了众安的网络请求安全协议。


现详细说明一下该方案中的核心四步:


• 第一步自然就是采用 https 协议,挂上证书启用证书校验。此外由于 SSL、TSL 各个版本的缺陷,我们在服务器端写死了 TSL 版本为 1.2+。即发起请求的客户端 TSL 版本必须是 1.2 或者以上版本,否则在 https 的握手阶段网络连接就会失败。这一步旨在于有效的防止“中间人”攻击。


• 第二步,在每个网络请求的 body 中都放入一个随机数,后台服务器会在内存中记录这个随机数,并且加一个有效时间。这一步可以有效的防止”重放攻击“。


• 第三步,生成一对 RSA 的公私钥对。公钥放客户端,私钥放服务器端。每个网络请求使用随机算法生成一个随机数 Key 作为 AES 算法的 key。用 AES 算法对网络请求的 body 数据整体进行加密。用公钥对 key 进行加密,加密后的值放到网络请求的 head 中。这一步是为了防止 body 明文泄密。


• 第四步,再次加固网络请求,给请求加签名——防篡改。使用 header 中的字段进行拼接,加入 time 这类随机数生成签名 sign。这一步可以有效的防止网络请求被抓包后篡改。只要我们的签名算法没有泄漏或者被破解,那攻击者就很难篡改我们的网络请求。


下图就是一个完整的网络请求客户端发送,服务器端接收的过程。



其他的 App 网络通信方案介绍


再简单介绍两种密码登录交互方案供大家参考:


4.1 用户登录密码交互方案大部分 app 都存在使用账号密码登录的情况。在这个时候为了保护用户的明文密码不泄漏,很多 app 就会采用下面这种或者类似方案来设计用户登录交互。



  1. 首先在客户端对用户的明文密码附加盐值然后用 hash 算法计算生成密码。(这里之所以附加盐值后再 hash 是为了防止用户密码被人用彩虹表直接破译出来)

  2. 服务器端准备两张表,当用户注册的时候,一张表用来存储用户提交的 hash 值密码,一个用来存储给用户生成的 UUID 随机码。(这里不存储用户明文密码就是为了防止服务器被脱库后,用户明文密码泄漏)

  3. 用户登录的时候服务器端收到用户发来的 hash 密码,这里称为 HashPW1,然后根据用户名查询到对应的 UUID。服务器在自己的数据库中根据用户名查询出 hash 密码,称为 HashPW2。接着查询到对应的 UUID。

  4. 然后在内存中用(HashPW1+UUID)生成密码 1,用(HashPW2+UUID)生成密码 2。两个密码相等即登录成功,不相等登录失败。(这里之所以在内存中生成真正的登录密码是为了防止将密码存储在数据库中,有权限的人可以直接查询到。经过这样复杂的操作有查询数据库权限的人只能看见两个 hash 值,不知道真正的密码如何生成。写代码的人知道密码生成算法,但是没有对应的随机值,都没有能力拿到用户的登录密码)


方案点评:


• 优点:该方案的最大优点在于隐藏客户的明文密码。整个系统的开发链上的几个关键角色:客户端开发、运维、后端开发都无法真正知晓客户的明文密码。在客户端,可以有效隐藏明文密码;在数据库中,存储的是客户的两个 Hash 值,运维无法掌握客户密码;在后端,虽然写后端代码的开发知晓真正的密码如何生成,但是却没有密码生成的两个关键 hash 值,仍然无法知晓客户的明文密码。


• 缺点:该方案缺点也是非常明显,客户端生成客户密码的 hash 值是固定的。如果被截取到该 hash 值就相当于被拦截了客户密码。必须在此基础上再加上网络通信安全策略,例如防抓包等策略,进一步加强防护。


4.2 用户交易密码交互方案除了登录过程以外还有一种密码使用情况就是交易密码使用场景。理论上 6 位数字的交易密码也可以采用上面的交互方案,但是由于很多系统都对接了第三方系统,第三方系统只认用户的 6 位数字密码。也就是说我们还是需要将用户交易密码明文提交到服务器端。这时一般是这样操作的:



  1. 第一步客户端通过 API1 发起请求到服务器。服务器端会有一个令牌池,令牌池中存储 RSA 公私钥对和一个随机码。收到用户请求后从令牌池捞出一对 RSA 公私钥对和随机码,和用户的 token 或者 userId 做绑定。

  2. 第二步将绑定后的公私钥对中的公钥和随机码发送给客户端

  3. 第三步客户端收到随机码和公钥后,将 6 位交易密码拼接随机码,整体用公钥加密生成密文串。客户端再通过 API2 提交密文串到服务器端。

  4. 第四步,服务器端接收到密文串后,根据用户 token 或者 userId 查询到对应的私钥,用私钥解密得到对应的随机码和 6 位数字密码。随机码校验 ok 后即可使用交易密码;如果校验失败才用户本次无法使用交易密码。


方案点评:


• 优点:算是比较完美的方案,可以用于生产环境


• 缺点:系统的后端开发可以拿到用户的明文密码,如果操作不当,有密码泄露的风险。


总结


对于一个网络 App 来说,在一个不安全的网络环境中构建一条安全、稳定的网络通信信道非常重要,尤其对于金融 App 来说更是重中之重。简单地使用 https 协议并不能解决各种安全漏洞和网络攻击。综上所述,众安在构建自己的网络通信安全信道时进行了全方位的考虑:


• 在使用 https 协议的基础上,进行二次加密安全。即使 https 协议被破解,攻击者也只能获取到一堆加密过的密文信息。


• 网络请求不仅实现了“防篡改”和“防重放”,还采用了“前向加密安全”。每个请求的加密密钥都是独立且动态生成的。即使某一条请求被抓包和破解,也不会影响其他请求的安全性。


• 考虑多平台,该安全协议适用于 iOS、Android 和 Web 端。


• 在设计通信安全的同时,也考虑了与性能的平衡。不仅注重安全性,还优化了通信的性能,确保用户在使用 ZA App 时能够享受到快速、高效的网络通信体验。


附录 D-H 算法


D-H 算法全称 Diffie-Hellman 算法。是一种密钥协商算法,用来在不安全的网络环境中协商出一个统一的加密密钥。但是该方法不能防止中间人攻击。


具体的密钥协商过程可以用下面这个交互过程简单理解一下。


• 首先 A 端和 B 端公开两个数字 P 和 Q,其中 P=5,Q=8。A 端和 B 端各自生成一个随机数 Ra=2、Rb=3。并且保证各自会保存好各自的随机码不泄漏。


• 接着 A 端采用公式 Ra*P 计算结果 X(X=10),B 端采用公式 Rb * Q 计算结果 Y(Y=24)


• 双方互换 X 和 Y 值。这样 A 端拿到有 Ra,P,Q,Y。B 端持有 Rb,P,Q,X。


• A 端采用公式 Ra * P * Y 计算密钥,结果等于 240。B 端采用公式 Rb * Q * X = 240。双方通过协商生成了统一的密钥 240。



上面这个过程只是用乘法简单的模拟一下这个过程,具体的 D-H 算法要复杂的多,而且是采用离散对数来实现的。具体数学原理如下图所示:



本文作者: 谢鹏飞 来自于众安国际 ZA Frontend 团队

用户头像

还未添加个人签名 2023-09-13 加入

还未添加个人简介

评论

发布
暂无评论
如何构建安全的App网络通信?_数据安全_ZA技术社区_InfoQ写作社区