前阵子,同事对某App与后台通信的数据进行了抓包分析,由于该App与后台通信使用的是http协议,且未对数据进行安全加密,因此通过抓包分析可直接获取其后台请求的接口及参数,通过构造Http请求,爬取其App内的数据。
平日的工作里,接触的比较多的都是业务逻辑,很少关注接入层这一块,经过上面这件事,激起了我对接入层的一些兴趣,要是哪一天自己做一个App,如何能做到安全接入呢?
总的来说,我们需要一种能够提供下列功能的 HTTP 安全技术:
密码是一套编码方案,加密之前的原始报文通常被称为 明文(plaintext 或 cleartext)。使用了密码之后的编码报文通常被称作 密文(ciphertext)。
在现实中,编/解码算法一般是公开的,我们可以为其设置大量不同的参数值以改变密码的工作方式,这个密码参数被称为 密钥(key)。给定一段明文报文 P、一个编码函数 E 和一个数字编码密钥 e,就可以生成一段经过编码的密文 C。通过解码函数 D 和解码密钥 d,可以将密文 C 解码为原始的明文 P。当然,编/解码函数都是互为反函数的,对 P 的编码进行解码就会回到原始报文 P 上去。
很多数字加密算法都被称为 对称密钥(symmetric-key) 加密技术,这是因为它们在编码时使用的密钥值和解码时一样(e=d)。我们就将其统称为密钥 k。
保持密钥的机密状态是很重要的。在很多情况下,编 / 解码算法都是众所周知的,因此密钥就是唯一保密的东西了。好的加密算法会迫使攻击者试遍每一个可能的密钥,才能破解代码。用暴力去尝试所有的密钥值称为枚举攻击(enumeration attack)。
对称密钥加密技术的缺点之一就是发送者和接收者在互相对话之前,一定要有一个共享的保密密钥。
比如 A、B、C三个人都想与 J 对话,则A、B 和 C 都要建立自己与 J 之间的保密密钥。每对通信实体都需要自己的私有密钥。如果有 N 个节点, 每个节点都要和其他所有 N-1 个节点进行安全对话,总共大概会有 N^2 个保密密钥,这将是一个管理噩梦。
公开密钥加密技术没有为每对主机使用单独的加密/解密密钥,而是使用了两个非对称密钥:一个用来对主机报文编码,另一个用来对主机报文解码。
其中私钥和公钥是一对的,都可以加解密,配对使用。
所有公开密钥非对称加密系统所面临的共同挑战是,要确保即便有人拥有了下面所有的线索,也无法计算出保密的私有密钥:
RSA算法就是一个满足了所有这些条件的流行的公开密钥加密系统,它是在 MIT 发明的,后来由 RSA 数据安全公司将其商业化。即使有了公共密钥、任意一段明文、用公共密钥对明文编码之后得到的相关密文、RSA 算法自身,甚至 RSA 实现的源代码,破解代码找到相应的私有密钥的难度仍相当于对一个极大的数进行质因数分解的困难程度,这种计算被认为是计算机科学中最难的问题之一。
任何人只要知道了公开密钥,就可以向一台公共服务器发送安全报文,所以非对称的公开密钥加密系统是很好用的。两个节点无须为了进行安全的通信而先交换私有密钥。
但是由于公开密钥加密算法的计算可能会很慢,所以实际应用中,一般都是采用对称和非对称加密混合使用策略。
比较常见的做法是,在两节点间通过便捷的公开密钥加密技术建立起安全通信, 然后再用那条安全的通道产生并发送临时的随机对称密钥,通过更快的对称加密技术对其余的数据进行加密。
消息摘要算法(Message Digest Algorithm)是一种能产生特殊输出格式的算法,其原理是根据一定的运算规则对原始数据进行某种形式的信息提取,被提取出的信息就被称作原始数据的消息摘要。
著名的摘要算法有RSA公司的MD5算法和SHA-1算法及其大量的变体。
消息摘要的主要特点有:
假设A写信给B,要确保可靠通信,必须要解决两个问题:
也就说,除了加/解密报文之外,还需要使用加密系统对报文进行签名(sign),以说明是谁编写的报文,同时证明报文未被篡改过,这种技术被称为数字签名(digital signing)。
对于消息的发送者A来说,先要生成一对公私钥对,将公钥给消息的接收者B。
在发送的信息中,除了要包含原始的消息外,还要加上另外一段消息。这段消息通过如下两步生成:
加密之后的信息摘要,就是原始信息的数字签名。
而对于信息的接收者B来说,他所收到的信息,将包含两个部分:
他将通过以下三步来验证消息的真伪:
通过这种所谓的数字签名技术,可以有效解决可靠通信的问题。如果原始消息在传送的过程中被篡改了,那么在消息接收者那里,对被篡改的消息提取的摘要肯定和原始的不一样。并且,由于篡改者没有消息发送方的私钥,即使他可以重新算出被篡改消息的摘要,也不能伪造出数字签名。
所以,综上所述,数字签名其实就是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。
使用数字签名技术,有一个前提,就是消息的接收者必须要事先得到正确的公钥。
如果一开始消息接收者B的公钥就被别人篡改了,那么当坏人假装消息发送者A给你发消息时,那坏人就会被你当成A了,而真正的消息发送者A给你发的消息则会被你视作无效。
那么如何保证公钥的安全可信呢?这就要靠数字证书来解决了。
所谓数字证书,一般包含以下一些内容:
证书的发布机构(Issuer)
证书的有效期(Validity)
消息发送方的公钥
证书所有者(Subject)
数字签名所使用的算法
数字签名
可以看出,数字证书其实也用到了数字签名技术。只不过要签名的内容是消息发送方的公钥,以及一些其它信息。但与普通数字签名不同的是,数字证书中的签名者不是随随便便一个普通的机构,而是要有一定公信力的机构。
因此消息发送者A可以去找"证书中心"(certificate authority,简称CA),为他的公钥做认证。证书中心用自己的私钥,对消息发送方的公钥和一些相关信息进行签名,被签名的信息与签名共同生成"数字证书"(Digital Certificate)。
之后A与B通信时,只需在签名的同时,附上数字证书,B在收到消息后,用CA的公钥对该数字证书进行验证,证明该证书确实是受信任机构颁发的,也就证明了证书中消息发送方的公钥是正确、可信任的。
所以,数字证书可以保证数字证书里的公钥确实是这个证书的所有者的,数字证书主要就是用来解决公钥的安全发放问题。
注意,根据数字证书的生成规则,事实上任何人都可以创建一个数字证书,主要取决于消息接收方是否信任该证书的颁发者。
公钥和私钥用途速记
对于加密解密,是不希望别人知道我的消息,只有我才能解密,所以公钥负责加密,私钥负责解密;
对于签名,是不希望有人冒充我发消息,只有我才能发布这个签名,所以私钥负责签名,公钥负责验证。
重放攻击(Replay Attacks)又称重播攻击、回放攻击,是指攻击者发送一个目的主机已接收过的包,来达到欺骗系统的目的,主要用于身份认证过程,破坏认证的正确性。攻击者可中途截获客户端的请求,并重新发送给认证服务器,从而达到认证通过的目的。
重放攻击的基本原理:把以前窃听到的数据原封不动地重新发送给接收方。
很多时候,网络上传输的数据是加密过的,此时窃听者无法得到数据的准确意义。但如果他知道这些数据的作用,就可以在不知道数据内容的情况下通过再次发送这些数据达到愚弄接收端的目的。
例如,有的系统会将鉴别信息进行简单加密后进行传输,这时攻击者虽然无法窃听密码,但他们却可以首先截取加密后的口令然后将其重放,从而利用这种方式进行有效的攻击。再比如,假设网上存款系统中,一条消息表示用户支取了一笔存款,攻击者完全可以多次发送这条消息而偷窃存款。
防御方案
加随机数:每次请求都需要加上nonce参数,,然后把nonce和其他参数一起进行数字签名。nonce是仅一次有效的随机字符串,要求每次请求时,该参数要保证不同,所以该参数一般与时间戳有关,比如简单的,使用时间戳的16进制,加上客户端的ip地址,mac地址等信息做个哈希之后,作为nonce参数。服务端需要将每次请求的nonce参数存储到一个“集合”中。每次处理请求时,首先判断该请求的nonce参数是否在该“集合”中,如果存在则认为是非法请求。该方法优点是认证双方不需要时间同步,双方记住使用过的随机数,如发现报文中有以前使用过的随机数,就认为是重放攻击。缺点是需要额外保存使用过的随机数,若记录的时间段较长,则保存和查询的开销会很大。
加时间戳:每次请求都需要加上timestamp参数,然后把timestamp和其他参数一起进行数字签名。假设一次正常的请求,从发出到达服务器一般都不会超过x秒,则服务器收到请求之后,首先比较时间戳参数与当前时间是否超过了x秒,如果超过了则认为是非法的请求。一般情况下,黑客从抓包重放请求耗时远远超过了x秒,所以此时请求中的timestamp参数已经失效了。如果黑客修改timestamp参数为当前的时间戳,则signature参数对应的数字签名就会失效,因为黑客不知道签名密钥,没有办法生成新的数字签名。但这种方式的漏洞也是显而易见的,如果在x秒内进行重放攻击,那就没办法保证请求仅一次有效,而且认证双方需要准确的时间同步,同步越好,受攻击的可能性就越小。但当系统很庞大,跨越的区域较广时,要做到精确的时间同步并不是很容易。
加随机数和时间戳:因为timstamp参数对于超过x秒的请求,都认为非法请求,所以我们只需要存储x秒内的nonce参数的“集合”即可。nonce的一次性可以解决timestamp参数x秒的问题,timestamp可以解决nonce参数“集合”越来越大的问题。
加流水号:就是双方在报文中添加一个逐步递增的整数,只要接收到一个不连续的流水号报文(太大或太小),就认定有重放威胁。该方法优点是不需要时间同步,保存的信息量比随机数方式小。缺点是一旦攻击者对报文解密成功,就可以获得流水号,从而每次将流水号递增欺骗认证端。
挑战一应答机制:客户端请求服务器时,服务器会首先生成一个随机数,然后返回给客户端,客户端带上这个随机数,访问服务器,服务器比对客户端的这个参数,若相同,说明正确,不是重放攻击。这种方式下,客户端每次请求时,服务端都会先生成一个挑战码,客户端带上应答码访问,服务端进行比对,若挑战码和应答码不对应,视为重放攻击。
前向安全性指的是长期使用的主密钥泄漏不会导致过去的会话密钥泄漏。前向安全能够保护过去进行的通讯不受密码或密钥在未来暴露的威胁。如果一个系统缺乏前向安全性,则意味着攻击者可以把监听到的加密流量先存起来,后续一旦拿到了密钥,则之前所有流量都可以成功解密,从而危害到会话安全。
HTTPS 是最流行的 HTTP 安全形式。它是由网景公司首创的,所有主要的浏览器和服务器都支持此协议。
使用 HTTPS 时,所有的 HTTP 请求和响应数据在发送到网络之前,都要进行加密。 HTTPS 在 HTTP 下面提供了一个传输级的密码安全层——可以使用 SSL,也可以使用其后继者—— 传输层安全(Transport Layer Security,TLS)。由于 SSL 和 TLS 非常类似,所以我们不太严格地用术语 SSL 来表示 SSL 和 TLS。
不使用SSL/TLS的HTTP通信,就是不加密的通信。所有信息明文传播,带来了三大风险:
现在,安全 HTTP 是可选的。请求一个客户端(比如 Web 浏览器)对某 Web 资源执行某事务时,它会去检查 URL 的方案:
SSL 是个二进制协议,与 HTTP 完全不同,其流量是承载在另一个端口上的(SSL 通常是由端口 443 承载的)。
开始加密通信之前,客户端和服务器首先必须建立连接和交换参数,这个过程叫做握手(handshake)。
假设使用的是基于RSA密钥协商(密钥交换)机制,握手阶段分成五步:
握手阶段有三点需要注意:
密钥协商机制的目的:
即使有攻击者在偷窥客户端与服务端的网络传输,客户端依然可以利用“密钥协商机制”与服务端协商出一个用来加密应用层数据的密钥(也称为“会话密钥”——该密钥仅在当前会话中有效)。
RSA 密钥协商机制有一个很大的问题:没有前向安全性(Forward Secrecy)。
整个握手阶段是不加密(也没法加密),都是明文的。因此,如果有人窃听通信,他可以知道双方选择的加密方法,以及三个随机数中的两个。整个通话的安全,只取决于第三个随机数(Premaster secret)能不能被破解。
而由于用于加密预主密钥的服务器公钥,一般会保持多年不变。任何能够接触到对应私钥的人都可以恢复预主密钥(即第三个随机数),并构建相同的主密钥,从而危害到会话安全性。这意味着攻击者可以把监听到的加密流量先存起来,后续一旦拿到了私钥,之前所有流量都可以成功解密。
基于ECDH的密钥协商机制
ECDH是基于ECC(Elliptic Curve Cryptosystems,椭圆曲线密码体制)的DH( Diffie-Hellman)密钥交换算法。
假设密钥交换双方拥有共享曲线参数(椭圆曲线E、阶N、基点G),密钥交换过程:
客户端、服务端双方即得 Q=bA=b(aG)=(ba)G=(ab)G=a(bG)=aB=Q’ (交换律和结合律),即双方得到一致的密钥Q=Q’ 。
一般来说,上述所说的全世界都知道的值A和B称之为公钥,而客户端和服务端的随机数a和b称之为私钥。
A和B的传输是公开的,想要计算获得共享密钥,第三方要么获取到客户端的随机值然后拿它乘上B,要么获取到服务端的随机值然后拿它乘上A,这样才能获取到客户端和服务端的共享密钥。由于随机数仅在当前会话生效,因此除非第三方能够拿到所有会话的随机数,否则无法解密出历史流量数据。
对于https,每个socket连接都会验证证书,交换密钥。攻击者截获请求,重新发送,因为socket不同,密钥也不同,后台解密后是一堆乱码,所以https本身就是防止重放攻击的,除非能复制socket,或者进行中间人攻击。
SSL 支持双向认证,将服务器证书下发给客户端,再将客户端的证书回送给服务器。 事实上,客户端证书不经常使用,大部分用户甚至都没有自己的客户端证书。
当我们通过 HTTPS 建立了一个安全 Web 事务之后,现代的浏览器都会自动获取所连接服务器的数字证书。如果服务器没有证书,安全连接就会失败。
浏览器收到证书时会对签名颁发机构进行检查。如果这个机构是个很有权威的公共签名机构,浏览器可能已经知道其公开密钥了(浏览器会预先安装很多签名颁发机构的证书)。如果对签名颁发机构一无所知,浏览器就无法确定是否应该信任这个签名颁发机构, 它通常会向用户显示一个对话框,看看他是否相信这个签名发布者。签名发布者可能是本地的 IT 部门或软件厂商。
SSL 是个复杂的二进制协议。除非你是密码专家,否则就不应该直接发送原始的 SSL 流量。幸运的是,借助一些商业或开源的库,编写 SSL 客户端和服务器并不十分困难。
OpenSSL 是 SSL 和 TLS 最常见的开源实现。OpenSSL 项目由一些志愿者合作开发, 目标是开发一个强壮的、具有完备功能的商业级工具集,以实现 SSL 和 TLS 协议以 及一个全功能的通用加密库。可以从 http://www.openssl.org 上获得 OpenSSL 的相 关信息,并下载相应软件。
接入层用来桥接客户端与业务后台,是客户端通往业务后台的大门。
经典的后台三层架构:接入层、逻辑层、存储层。
从客户端的角度来看:
从服务端的角度来看:
通过上面的学习,我们总结下接入层的安全信道体系:
RSA密钥协商实现0 RTT:首次通信或者会话过期时,需要协商密钥,RSA加密后的密钥以及使用该密钥加密的业务数据一起上行,服务端解出密钥之后,即可解出业务数据,进行业务处理并返回。
对于ECDH密钥协商,由于客户端需要收到服务端传递的B之后,才能得到密钥,对数据进行加密,因此业务数据不能随着第一次请求上行,实现不了0 RTT。
实现0 RTT的方案:
第一次请求通过系统A在DNS解析时负责就近接入,提供最优的接入IP供第一次请求访问。当请求到达接入层,接入层使用另一个系统B,将最优IP访问列表下发给终端,后续请求绕过DNS解析,通过IP直连的方式访问。
系统A提供域名解析、就近接入、全局负载均衡,在DNS解析环节,根据用户所在区域、所属运营商,将域名解析至该用户所属地区及运营商访问最优的IDC内的IP。
就近接入:依赖测速系统和IP库。测速系统持续搜集全国各地及各运营商到各IDC的访问速度数据,通过这些数据计算出各地区及各运营商的就近接入点。同时通过IP库,判断用户所属的区域及运营商,结合测速数据和IP信息,可获得就近接入的IP。
全局负载均衡:就近接入使得各省份的流量导向不同的IDC,同时可以通过人工干预的方式,将异常IDC的流量导向其他IDC。