前言
web与后端,andorid与后端,ios与后端,像这种类型的交互其实就属于典型的前端与后端进行交互。在与B端用户进行交互的过程中,我们通常忽略了其安全性(甚至从未考虑安全性)。比如,请求和响应数据的明文传输,对接口并没有做严格的身份校验。如果我们还是按照这种思路去做C端用户的交互,那么等待着必将是血淋淋的教训。接下来,我带领大家如何在与C端用户安全的进行交互。
保证安全性的几种方式
前后端安全性的交互,大致可以分成如下几类:
1、通信请求使用https
2、对请求参数进行签名,防止数据被踹改
3、对请求参数以及响应进行加密解密处理
4、APP中使用ssl pinning防止抓包操作
使用https
谷歌 Chrome 在18年七月份已经将所有的 HTTP 网站标记为“不安全”。并且已经有越来越多的第三方服务开始推荐甚至是强制要求使用 HTTPS 连接方式,比如现在用得特别多的微信登录、微信支付、短信验证码、地图 API 等等,又比如苹果公司 2016 年在 WWDC 上宣称,公司希望官方应用商店中的所有 iOS App 都使用安全的 HTTPS 链接与服务器进行通信。
那为什么越来越多的 HTTP 都在逐渐 HTTPS 化?HTTP 协议(超文本传输协议)是客户端浏览器或其他程序与 Web 服务器之间的应用层通信协议;HTTPS 协议可以理解为 HTTP+SSL/TLS, 即 HTTP 下加入 SSL 层,HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL,用于安全的 HTTP 数据传输,http与https的区别如下图所示:
不使用SSL/TLS的HTTP通信,就是不加密的通信。所有信息明文传播,带来了三大风险。
- 窃听风险(eavesdropping):第三方可以获知通信内容。
- 篡改风险(tampering):第三方可以修改通信内容。
- 冒充风险(pretending):第三方可以冒充他人身份参与通信。
SSL/TLS协议是为了解决这三大风险而设计的,希望达到:
- 所有信息都是加密传播,第三方无法窃听。
- 具有校验机制,一旦被篡改,通信双方会立刻发现。
- 配备身份证书,防止身份被冒充。
因此强烈建议,为了你的系统安全性,赶快切到https中去吧。
对请求进行签名
我们先来看一个例子,假设用户在下完单之后,可以更改订单的状态,用户对后端发起请求 /user?orderId=123, 假设后端刚好也没有对这笔订单的身份进行验证,那么后果就是,我们根据orderId, 将这笔订单的状态进行了修改:
如果这时候,尝试着将请求中的orderId 换成另外一个orderId, 也会同样对这笔订单做了修改,从安全角度来说这是我们不希望看到的,当然我们也可以加一下身份校验,判断该笔订单是否属于当前的用户;除此之外,我们还应该对请求参数做一次签名处理。
加签和验签就是在请求发送方将请求参数通过加密算法生成一个sign值,放到请求参数里;请求接收方收到请求后,使用同样的方式对请求参数也进行加密得到一个sign值,只要两个sign值相同,就说明参数没有被篡改。
签名参数sign生成的方法
- 将所有以头参数(注意时所有参数),出去sign本身,以及值是空的参数,按参数键字母升序排序。
- 然后把排序后的参数按参数1值1参数2值2......参数n值n(这里的参数和值必须是传输参数的原始值,不能是经过处理的,如不能将"转成"后再拼接)的方式拼接成一个字符串。
- 把分配给接入方的验证密钥key拼接在第2步得到的字符串前面。
- 在上一步得到的字符串前面加上密钥key(这里的密钥key是接口提供方分配给接口接入方的),然后计算md5值,得到32位字符串,然后转成大写,得到的字符串作为sign的值放到请求参数里。
举例
现在假设需要传输的数据:/guest/rechargeNotify?p2=v2&p1=v1&method=cancel&p3=&pn=vn(实际情况最好是通过post方式发送)
- 拼接字符串,首先去除值是空的参数p3,剩下p2=v2&p1=v1&method=cancel&pn=vn,然后按参数名字符升序排序得到字符串:method=cancel&p1=v1&p2=v2&pn=vn。
- 然后做参数名和值的拼接,最后得到methodcancelp1v1p2v2pnvn。
- 在上面拼接得到的字符串前面加上验证密钥key,假设是abc,得到新的字符串abcmethodcancelp1v1p2v2pnvn。
- 将上面得到的字符串进行md5计算,假设得到的是abcdef,然后转为大写,得到ABCDEF这个值即为sign签名值。最终产生的url应该如下:/guest/rechargeNotify?p2=v2&p1=v1&method=cancel&p3=&pn=vn&sign=ABCDEF
- 注意:计算md5之前请确保请求发送方和接收方使用的字符串编码一致,比如统一使用utf-8编码,如果编码方式不一致则计算出来的签名会校验失败。
验签过程
其实就是将请求url按照上述的规则进行同样的操作,计算得到参数的签名值,然后和参数中传递的sign值进行对比,如果一致则校验通过,否则校验不通过。
对请求和响应进行加解密
可能有人会问,都使用了https了,为什么还要对请求和响应再做一次加解密,因为有些第三方抓包工具,例如Charles 通过某些手段是可以抓取https的明文的,因此对一些敏感数据,我们需要进行加密处理,常见的加解密方式有AES 对成加密方式和RSA非对成方式,至于如何运用,可以参考https的原理,有点复杂,不过可以简单分成如下几步:
1.服务器端有一个密钥对,即公钥和私钥,是用来进行非对称加密使用的,服务器端保存着私钥,不能将其泄露,公钥可以发送给任何人。
2.服务器将自己的公钥发送给客户端。
3.客户端收到服务器端的公钥之后,会对公钥进行检查,验证其合法性,如果发现发现公钥有问题,那么HTTPS传输就无法继续。严格的说,这里应该是验证服务器发送的数字证书的合法性,关于客户端如何验证数字证书的合法性,下文会进行说明。如果公钥合格,那么客户端会生成一个随机值,这个随机值就是用于进行对称加密的密钥,我们将该密钥称之为client key,即客户端密钥,这样在概念上和服务器端的密钥容易进行区分。然后用服务器的公钥对客户端密钥进行非对称加密,这样客户端密钥就变成密文了,至此,HTTPS中的第一次HTTP请求结束。
4.客户端会发起HTTPS中的第二个HTTP请求,将加密之后的客户端密钥发送给服务器。
5.服务器接收到客户端发来的密文之后,会用自己的私钥对其进行非对称解密,解密之后的明文就是客户端密钥,然后用客户端密钥对数据进行对称加密,这样数据就变成了密文。
6.然后服务器将加密后的密文发送给客户端。
7.客户端收到服务器发送来的密文,用客户端密钥对其进行对称解密,得到服务器发送的数据。
总结
前后端的交互如果做到以上使用https,对请求加解密以及对请求参数进行验签,基本上能解决大部分问题,但除此之外我们还应该做到对每个接口进行身份校验,确保该接口只能由特定的用户访问,或者该笔数据只能由特定的用户去进行修改。