关于加密
在解释SSH、SSL与HTTPS协议之前我先介绍一下非对称加密协议。在1976年以前,所有的加密都采用对称加密,既A使用某种加密规则对信息加密,B收到信息后逆向加密规则解密数据。这通信方式产生了一个难以解决的问题:A如何安全的把加密规则通知B?
在1976年有两位数学家提出了一个崭新的非对加密的概念:
1.A生成一对两把密钥(公钥和私钥)。公钥是公开的,任何人都可以获得,私钥则是保密的。
2.B获取A生成的公钥,然后用它对信息加密。
3.A得到加密后的信息,用私钥解密。
受这个思路的启发,三位数学家Rivest、Shamir 和 Adleman 设计了一种具体实现上面描述的非对称加密的算法,以他们三个人的名字命名,就是目前在计算机领域应用非常广泛的非对称加密算法RSA加密算法。这样网络上传输的数据都经过公钥加密,然后用私钥解密,就算被第三方截获也无法解密出原始数据。想深入理解非对称加密解密的原理可以看这里。
虽然非对称加密很安全很强大,但是它也有缺点,相对于对称加密它计算量更大,计算时间更长。所以在大规模数据的安全通信场景中,普遍采用非对称加密技术来交换对称加密密钥,之后的通信都采用对称加密技术加密。
SSH
维基百科中对SSH协议的定义如下
Secure Shell(缩写为SSH),由IETF的网络工作小组(Network Working Group)所制定;SSH为一项创建在应用层和传输层基础上的安全协议,为计算机上的Shell(壳层)提供安全的传输和使用环境。
传统的网络服务程序,如rsh、FTP、POP和Telnet其本质上都是不安全的;因为它们在网络上用明文传送数据、用户帐号和用户口令,很容易受到中间人(man-in-the-middle)攻击方式的攻击。就是存在另一个人或者一台机器冒充真正的服务器接收用户传给服务器的数据,然后再冒充用户把数据传给真正的服务器。
而SSH是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议。利用SSH协议可以有效防止远程管理过程中的信息泄露问题。通过SSH可以对所有传输的数据进行加密,也能够防止DNS欺骗和IP欺骗。
SSH之另一项优点为其传输的数据可以是经过压缩的,所以可以加快传输的速度。SSH有很多功能,它既可以代替Telnet,又可以为FTP、POP、甚至为PPP提供一个安全的“通道”。
相信大家看了上面的段定义也是云里雾里。用最通俗的方式来解释一下SSH。有这么一个场景:有一个服务器在互联网的另一头,你需要远程登录到服务器来执行一些命令配置这台服务器。这个时候你和服务器之间就需要有数据通信。假设客户端叫A,服务器叫B。
要实现通信,最简单的想法就是,客户端服务器之间建立一个TCP连接,这样你的指令就可以通过TCP连接从A传输到B。再仔细想想,不对,A与B之间需要通信的数据中间经过了C、D、E、F等网络路由器设备,这其中任何一个路由器被黑客攻破,你们之间的通信数据就毫无保留的被别人看到。
所以,你需要用一个密钥把数据加密吧。这样A和B之间的数据就算被第三方看到,他们也不知道内容是什么。可是这里又存在一个问题,A如何通知B(或者B如何通知A)它的密钥是什么呢?要知道只要数据是在互联网上传输,就有被截获的可能。密钥被截获了,你就没有秘密可言了。
现在我们需要考虑一个安全的方式来传输密钥!让我想想——非对称加密!
- A、B之间建立TCP连接
- B生成一对公私密钥
- B把公钥发送给A
- A生成一个用于加密数据的密钥K(既我们想通知给客户端的密钥,之后的数据通信都使用这个密钥加密,这个密钥不可让第三方知道)
- A把K用公钥加密发送给B,B解密后,从此A、B之间的通信数据都用K密钥进行加密和解密。
这样假如中间的C、D、E截获了A发给B的K密钥内容的数据包,可是这个密钥是用B服务器生成的公钥加密过的,只有B服务器知道如何解密。于是我们的数据很安全了。。。
看起来很简单不是吗?一切大功告成。等等...如果黑客H埋伏在了A和B之间的某一个路由器上,他假冒B生成一对公私密钥,然后把公钥发送给A,这样A与H之间就建成了一个加密通道,A把所有信息发送给H,H截获A的信息,在假冒A与B通信。如此一来,A、B之间的通信就完全暴漏给了H,而A、B却完全不知道,这就是有名的“中间人”攻击。
为解决这个问题,SSH协议采用由人工判断公钥的fingerprint是否可信的方式。当使用ssh命令连接服务器时,命令行会提示如下信息:
The authenticity of host '168.30.9.213 (<no hostip for proxy command>)' can't be established.
RSA key fingerprint is 23:42:c1:e4:3f:d2:cc:37:1d:89:cb:e7:5d:be:5d:53.
Are you sure you want to continue connecting (yes/no)?
输入yes之后才会连接到远程服务器,同时这个信息会存储到用户的.ssh/known_hosts文件中,下次再登录的时候,会检查known_host文件,如果存在相同的公钥信息,就不在提示用户确认了。
这种认证方式假设想登陆服务器的用户已经知道服务器公钥(作为服务器的用户他自然有渠道得知服务器公钥)。fingerprint其实就代表公钥,可以看成是公钥的一个压缩版。有了这个步骤,如果有中间人想冒充服务器B发送公钥给A,它不可能生成一对和B生成的一样的公私密钥,他发送给A的公钥必然与B服务器的不同,所以用户就可以根据printfinger判断所连接的服务器是否可信,有没有被中间人冒充。
SSH的实现细节
上面只是粗略讲解SSH的安全协议的设计思路,实际上SSH通信协议在安全通信过程中分了几个阶段,每个阶段又细分了几个步骤,具体的可参看SSH原理简介。几个主要阶段如下:
- 协议协商阶段
- 服务端认证阶段
- 客户端认证阶段
- 数据传输阶段
客户端认证
这里我想单独谈谈客户端认证,因为对于使用linux远程登录功能的系统管理员来说能有直观感觉的也就是是这个环节了。一般我们能接触到的的认证方式有两种:
- 密码认证
- Public Key认证
密码认证很好理解,就是我们在登录远程linux服务器的时候提供用户名和密码?这里面稍微提示一下,在你的用户名和密码通过网络传输给服务器之前,已经经过了服务器认证协商阶段,这是一个安全的加密信道已经建立,所以你的用户名和密码都是加密后传输给服务器的,保证不会被第三方截获。
每次登录都要输入密码很麻烦,且密码如果简单的话可能还会被暴力破解。Public Key认证提供了一种更安全便捷的认证客户端的方式。这个技术也用到了非对称加密技术,由客户端生成公私密钥对,然后将公钥保存在服务器上。认证的过程大体如下:
- 客户端发起一个Public Key的认证请求,并发送RSA Key的模数作为标识符。(如果想深入了解RSA Key详细 -->维基百科)
- 服务端检查是否存在请求帐号的公钥(Linux中存储在~/.ssh/authorized_keys文件中),以及其拥有的访问权限。如果没有则断开连接
- 服务端使用对应的公钥对一个随机的256位的字符串进行加密,并发送给客户端
- 客户端使用私钥对字符串进行解密,并将其结合session id生成一个MD5值发送给服务端。*结合session id的目的是为了避免攻击者采用重放攻击(replay attack)。
- 服务端采用同样的方式生成MD5值与客户端返回的MD5值进行比较,完成对客户端的认证。
为更广大群众设计的SSL与TLS
上面wiki上也有写,SSH其实是专门为shell设计的一种通信协议,它垮了两个网络层(传输层和应用层)。通俗点讲就是只有SSH客户端,和SSH服务器端之间的通信才能使用这个协议,其他软件服务无法使用它。但是其实我们非常需要一个通用的,建立在应用层之下的一个传输层安全协议,它的目标是建立一种对上层应用协议透明的,不管是HTTP、FTP、还是电子邮件协议或其他任何应用层协议都可以依赖的底层的可安全通信的传输层协议。
网景公司于1994年为解决上面的问题,设计了SSL(Secure Sockets Layer)协议的1.0版本,但并未发布,直到1996年发布SSL3.0之后,开始大规模应用于互联网服务。可能很多人听所过TLS(Transport Layer Security)。它相当于是SSL协议的一个后续版本,他是SSL经过IETF标准化之后的产物(详细参考传输层安全协议,下文中所说的SSL协议也包括TSL)。
跟SSH相比SSL所面临的问题要更复杂一些,上面我们提到,SSH协议通过人工鉴别Public Key的printfinger来判断与之通信的服务器是否可信(不是伪装的中间人)。可是SSL是为了整个互联网上的所有客户端与服务器之间通信而设计的,他们彼此之间不可能自己判断通信的对方是否可信。那么如何解决这个问题呢?
在构思解决方案之前我先讲一个概念数字签名。。。。
想了一下,还是不写了。。。 阮老师的这篇blog对数字签名解释的非常到位,良心推荐,如果有不了解数字签名概念的读者,建议先看看阮老师的文章。
回到上面我们所要解决的问题,以浏览器和网站服务器之前的安全通信举例,首先浏览器要求和某WEB服务器建立安全的SSL连接通道,这时需要服务器的公钥用来加密浏览器生成的通信密钥,发给WEB服务器,以确定通信密钥。假如浏览器接受到一个公钥,它如何知道这个公钥确实是来自那个WEB服务器,而不是中间的某个攻击者截获了它的请求,假扮WEB服务器给它的假密钥?浏览器总不能记录所有可信任站点的公钥吧。
解决问题的思路是这样的,在SSL协议中引入了一种类似公共机关(类似于我国的国家公证处?)的概念,就是我们熟知的CA(数字证书认证机构)。它为浏览器发行一个叫数字证书的东西。这个东西大体上如下图所示,其中比较重要的信息有:
- 对象的公开密钥
- 数字签名
有了数字证书,浏览器在建立SSL连接之前,并不只是简单获取服务器的公钥,而从服务器获取数字证书。数字证书里有服务器的公钥,并且有CA给这个证书签发的签名。这个签名其实是证书内容的摘要经过CA私钥加密生成的。这样浏览器得证书内容和摘要,并用CA的公钥(每个浏览器都存储着一些权威CA的公钥)对数字签名解密,也得到证书的摘要,比对两个摘要如果相同,说明证书是真的,且未经过修改。信任问题就这么解决了,如果上面这段话不好理解的话,再次建议读一读阮老师的数字签名是什么?。
其他SSL的握手,密钥交换,加密的细节这里就不介绍了,普通开发者这要懂上面我介绍的这些基本概念就可以了。
HTTPS
读完上面内容,理解HTTPS就简单了,它的全称是 Hypertext Transfer Protocol Secure,也称为HTTP over TLS, HTTP over SSL,其实就客户端与服务系之间的HTTP通信基于TLS或则SSL协议。对于HTTP协议和SSL/TLS协议本身没有任何特殊定制,因为SSL/STL本身对HTTP协议就是透明的,HTTP在SSL/TLS上运作也不需要任何特殊处理。
做为网站管理员,可能会遇到申请数字证书的任务,理解了上面的概念,申请数字证书就不那么一头雾水了,首先你要为服务器生成一对公司密钥,然后把你网站的信息连同你的公钥一起发送给某个权威的CA,CA会通过某种方式认证申请人是否真的是网站的所有人,比如让你在网站的指定路径上传他指定的特殊蚊子序列。验证通过就会得到证书了。
思考题
通过代理访问HTTPS协议的网站,你的通信是否安全?
强烈推荐《HTTP权威指南》这本书,你会惊奇的发现15年前写的书,里面的内容今天来读不但一点都不过是,且会收获很多。