https握手过程
(1). 客户端向服务器发起ClientHello
请求.发送的消息内容包括
- 支持的协议版本,比如TLS 1.0版
- 一个客户端生成的随机数,稍后用于生成”对话密钥”
- 支持的加密方法,比如RSA公钥加密、支持的压缩方法等。
(2). 服务器响应ServerHello
。响应内容包括
- 确认使用的加密通信协议版本,比如TLS 1.0版本
- 一个服务器生成的随机数,稍后用于生成”对话密钥”
- 确认使用的加密方法,比如RSA公钥加密、服务器证书。
(3). 客户端再次响应。客户端收到服务器回应以后,首先验证服务器证书。如果证书不是可信机构颁布、或者证书中的域名与实际域名不一致、或者证书已经过期,就会向访问者显示一个警告,由其选择是否还要继续通信。如果证书没有问题,客户端就会从证书中取出服务器的公钥。然后进行响应,包括
- 一个随机数。该随机数用服务器公钥加密,防止被窃听、编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送
- 客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供服务器校验。
(4). 服务器的最后回应。服务器收到客户端的第三个随机数pre-master key之后,计算生成本次会话所用的”会话密钥”。然后,向客户端最后发送下面信息。
- 编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。
- 服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供客户端校验。
至此,整个握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的HTTP协议,只不过用”会话密钥”加密内容。
还有几个问题
(1).对称加密和非对称加密(公钥私钥)
假设有A, B 两方,双方都有一对密钥(公钥和私钥)。公钥是对外开放的,任何人都可以得到;私钥是自己的,别人是获取不到的。公钥和私钥是相对应的,利用公钥进行加密,只能用私钥解密,使用公钥无法解密;利用私钥加密,则只能用公钥解密,使用私钥也无法解密。 利用A的公钥对数据进行加密,则只有A的私钥可以解密,任何第三方都不可以解密数据。这样可以在不安全的通道上进行数据传输,保证只有A可以解密数据,任何第三方只能窃听到已加密的数据,即便拥有公钥也无法进行解密操作。 利用A的私钥对约定好的数据加密,发送给B。B可以使用公钥进行解密,从而验证A的身份。任何第三方都无法模拟这样加密后的数据。这种形式广泛用于电子签名等。
(2).为什么不是使用公钥加密而是又生成了一个新的密钥呢?
由于”会话密钥”是对称加密,所以运算速度非常快,而服务器公钥只用于加密”对话密钥”本身,这样就减少了加密运算的消耗时间。
(3).怎么验证网站证书
根证书属于证书颁发结构, 根证书的公钥默认内嵌在我们的系统中。 网站证书是带有根证书的私钥签名,在https握手开始阶段由网站服务器发送给客户端。客户端收到网站证书后,立即校验证书的有效性。校验方法是:取根证书的私钥签名,利用客户端系统内嵌的根证书公钥解密。解密成功,则证书有效。 聊到这里, 我们可以看出若想伪造网站证书进行https代理,必须导入自己的根证书到系统中,才能使客户端认为伪造的网站证书是有效的。
怎么识别请求伪造和请求重放?
这个问题其实总结一下就是怎么识别非自己的客户端调用api。 原问题在http://www.zhihu.com/question/28213465/answer/40043402
我的答案是现在基本无解,但是可以无限的增大难度。 注意这个问题是架构为cs的,也就是客户端和服务器的,客户端一旦发出去是不受你的控制的,别人可以随便的分析和逆向。如果api调用是在服务器和服务器之间的,你的服务器代码别人拿不到,这样下面的方法是很有效的。
- 每个app都有一个app_id和app_secret,app_id可以公开,app_secret必须保密,但是也必须放在app的代码里面。app_secret的保密性就是最关键的环节。
- 每次app发出一个请求的时候,都需要对数据进行签名。大致就是综合数据、app_id、app_secret和时间戳进行hash运算。比如签名规则要求是按照字段的字母升序排列字段的值,直接拼接字符串,然后拼接app_secret,然后是time_stamp。这样的话,需要发送的数据如果是
{'username": "root", "password": "123456"}
,app_id="123098"
,app_secret="2348fhs823r4usfhhkfsdh"
,拼接后的字符串就是"123456root2348fhs823r4usfhhkfsdh1424406627411"
,然后对这个数据进行HMAC-SHA1或者SHA256运算。最终得到另一个字符串"204893FOIU2340SOI8R32R3OIUER"
,然后将这个值、app_id、时间戳和数据一起发送,放在url参数里面或者http头里面都可以的。 - 服务器收到请求之后,首先看对比时间戳和当前时间,如果超过某个允许的延时时间间隔,比如说20秒钟,认为这是一个”旧的”请求,就拒绝接受。可能是请求被黑客截获了,过了一段时间又重放出去的。如果时间戳校验没问题,接下来就是按照相同的规则进行签名,看两个签名是否的是一致的。如果是一致的就说明数据没有被篡改或者数据不是没伪造的。因为黑客不掌握app_secret的情况下,是不可能伪造签名的。
- 有时候为了更加的安全,会将url也加入hash中,这样可以防止数据的目的地被篡改。还有一个是在数据中再加入一个nonce字段,每次都是随机生成,认为不会重复,这样在服务器允许的延时时间间隔中也只能使用一次了,没有nonce的时候,服务器允许时间间隔内数据也是可以使用多次的。
上面的防范还是说在app_secret保密的情况下,如果app被反汇编获取到了app_secret,那就无能为力了。当然这个一个对抗的过程,我们是可以无限的增大破解者的难度的。
- 限制每个app_id的api调用频率,超过频率禁用掉或者需要验证码
- 使用私有通信协议
- 对app代码进行混淆,封装成.so机器码等
- 使用ssl以及对数据进行非对称加密
- 对识别出来的非法调用,延时返回假数据