简介
SSH(Secure Shell)
是一个提供数据通信安全、远程登录、远程指令执行等功能的安全网络协议,由芬兰赫尔辛基大学研究员Tatu Ylönen
,于1995年提出,其目的是用于替代非安全的Telnet、rsh、rexec等远程Shell协议。之后SSH发展了两个大版本SSH-1和SSH-2。
通过使用SSH,你可以把所有传输的数据进行加密,这样"中间人"这种攻击方式就不可能实现了,而且也能够防止 DNS欺骗
和 IP欺骗
。使用SSH,还有一个额外的好处就是传输的数据是经过压缩的,所以可以加快传输的速度。SSH有很多功能,它既可以代替Telnet,又可以为FTP、Pop、甚至为PPP提供一个安全的"通道"。
SSH为一项创建在应用层和传输层基础上的安全协议,为计算机上的 Shell 提供安全的传输和使用环境。
SSH的基本框架
SSH协议框架中最主要的部分是三个协议:
-
传输层协议(The Transport Layer Protocol)
:传输层协议提供服务器认证,数据机密性,信息完整性等的支持。 -
用户认证协议(The User Authentication Protocol)
:用户认证协议为服务器提供客户端的身份鉴别。 -
连接协议(The Connection Protocol)
:连接协议将加密的信息隧道复用成若干个逻辑通道,提供给更高层的应用协议使用。
SSH-AUTH是SSH里面用于验证客户端身份的协议。我们在用ssh命令输入密码的那一步实际上就是在这个阶段。可以看到的是,虽然传输的是用户名和密码,但是由于这个协议建立在SSH-TRANS之上,所以内容都是加密的,可以放心的传输。
而SSH-CONN是真正的应用协议。在这里可以定义各种不同的协议,其中我们经常使用的scp、sftp还有正常的remote shell都是定义在这里的一种协议实现。这里的各种应用协议都要首先经过SSH-AUTH的验证之后才可以使用。
这个三个协议之间的关系可以用下面这幅图来说明:
SSH的加密
SSH从安全和性能两方面综合考虑,结合使用了 Public Key/Private key(公钥/私钥)
和 Secret Key(密钥)
。
-
Public Key/Private key
:非对称加密,安全,但效率低,不适合大规模进行数据的加密和解密操作 -
Secret Key
:对称机密,高效,但安全性相对较低,Key的分发尤其不方便
对密码学基础知识和数字签名了解,可以参考阮一峰的博文
SSH的主要特性
- 加密:避免数据内容泄漏
- 通信的完整性:避免数据被篡改,以及发送或接受地址伪装
(检查数据是否被篡改,数据是否来自发送者而非攻击者)SSH-2
通过MD5
和SHA-1
实现该功能,SSH-1
使用CRC-32
- 认证:识别数据发送者和接收者身份 客户端验证SSH服务端的身份:防止攻击者仿冒SSH服务端身份,避免中介人攻击和重定向请求的攻击;
OpenSSH
通过在know-hosts
中存储主机名和host key
对服务端身份进行认证 服务端验证请求者身份:提供安全性较弱的用户密码方式,和安全性更强的per-user
public-key signatures
;此外SSH还支持与第三方安全服务系统的集成,如Kerberos
等 - 授权:用户访问控制
- Forwarding or tunneling to encrypt other TCP/IP-based sessions 可以通过SSH为Telnet、FTP等提供通信安全保障,支持三种类型的
Forwarding
操作:Port Forwarding
;X Forwarding
;Agent Forwarding
SSH中的Key
SSH结合使用了Public Key/Private key
和Secret Key
:
-
Public Key/Private key(非对称加密)
用于在建立安全通道前在客户端和服务端之间传输Secret Key
和进行身份认证
; -
Secret Key(对称加密)
则用来作为SSH会话的安全保证,对数据进行加密和解密。
SSH可以处理4种密钥:
名称 | 生命周期 | 创建 | 类型 | 描述 |
---|---|---|---|---|
Host Key | 持久化 | 服务端 | Public Key | Host Key是服务器用来证明自己身份的一个永久性的非对称密钥 |
User Key | 持久化 | 用户 | Public Key | User Key 是客户端用来证明用户身份的一个永久性的非对称密钥(一个用户可以有多个密钥/身份标识) |
Server Key | 默认为1小时 | 服务端 | Public Key | Server Key 是SSH-1协议中使用的一个临时的非对称密钥,每隔一定的间隔(默认是一个小时)都会在服务器重新生成。用于对Session Key进行加密(仅SSH-1协议有,SSH-2对其进行了增强,这里Server Key作为一个概念便于在流程中进行描述) |
Session Key | 客户端 | 会话(Session) | Secret Key | Session Key是一个随机生成的对称密钥,用户SSH客户端和服务器之间的通信进行加密,会话结束时,被销毁 |
SSH框架:
安全连接的建立
在进行有意义的会话之前,SSH客户端和服务器必须首先建立一条安全连接。该连接可以允许双方共享密钥、密码,最后可以相互传输任何数据。
现在我们介绍SSH-1协议是如何确保网络连接的安全性的。SSH-1客户端和服务器从阿卡似乎经过很多个步骤,协商使用加密算法,生成并共享一个会话密钥,最终建立一条安全连接:
- 客户端连接到服务器上
- 客户端和服务器交换自己支持的SSH协议版本号
- 客户端和服务器切换到基于报文的协议
- 服务器向客户端提供自己的身份证明和会话参数
- 客户端给服务器发送一个(会话)密钥
- 双方启用加密并完成服务器认证
- 建立安全连接
每个阶段均涉及到客户端与服务端的多次交互,通过这些交互过程完成包括证书传输、算法协商、通道加密等过程。
1 客户端连接到服务器上
这个步骤没什么好说的,就是向服务器的TCP端口(约定是22)发送连接请求。
2 客户端和服务器交换自己支持的协议版本号
这些协议是以 ASCII 字符串表示,例如:SSH-1.5-1.2.27
,其意义为SSH协议,版本号是V1.5,SSH1实现版本为1.2.27。可以使用 Telnet 客户端连接到一个SSH服务器端口是看到这个字符串:
➜ telnet 192.168.1.200 22
Trying 192.168.1.200...
Connected to doc.dinghuo123.com.
Escape character is '^]'.
SSH-2.0-OpenSSH_6.0p1 Debian-4+deb7u6
如果客户端和服务器确定其协议版本号是兼容的,那么连按就继续进行,否则,双方都可能决定中断连接。例如,如果一个只使用 SSH-1 的客户端连接到一个只使用 SSH-2 的服务器上,那么客户端就会断开连接并打印一条错误消息。实际上还可能执行其他操作:例如,只使用SSH-2的服务器可以调用SSH-1服务器来处理这次连接请求。
3 客户端和服务器切换基于报文的协议
协议版本号交换过程一旦完成,客户端和服务器都立即从下层的 TCP 连接切换到基于子报文的协议。每个报文都包含一个32位的字段,1 - 8字节的填充位[ 用来防止已知明文攻击unknown-plaintext attack ],一个1字节的报文类型代码, 报文有效数据和一个4字节的完整性检査字段。
4 服务器向客户提洪自己的身份证明和会话参数
服务器向客户端发送以下信息(现在还沒有加密):
-
主机密钥(Host Key)
,用于后面证明服务器主机的身份 -
服务器密钥(Server Key)
,用来帮助建立安全连接 - 8个随机字节序列,称为
检测字节(check bytes)
。客户端在下一次响应中必须包括这些检测字节,否則服务器就会拒绝接收响应信息,这种方法可以防止某些IP伪装攻击(IP spoofing attack)
。 - 该服务器支持的加密、压缩和认证方法
此时,双方都要计算一个通用的 128 位会话标识符(Session ID)
。它在某些协议中用来惟一标识这个 SSH 会话。该值是 主机密钥(Host Key)
、服务器密钥(Server Key)
和检测字节(check bytes)
一起应用 MD5散列函数
得到的结果。
当客户端接收到 主机密钥(Host Key)
时,它要进行询问:“之前我和这个服务器通信过吗?如果通信过,那么它的主机密钥是什么呢?”要回答这个问题,客户端就要査阅自己的已知名主机数据库。如果新近到达的主机密钥可以和数据库中以前的一个密钥匹,那么就没有问题了。
但是,此时还存在两种可能:已知名主机数据库中没有这个服务器,也可能有这个服务器但是其主机密钥不同。在这两种情况中,客户端要选择是信任这个新近到达的密钥还是拒绝接受该密钥。此时就需要人的指导参与了,例如,客户端用户可能被提示要求确定是接受还是拒绝该密钥。
The authenticity of host 'ssh-server.example.com (12.18.429.21)' can't be established.
RSA key fingerprint is 98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d.
Are you sure you want to continue connecting (yes/no)?
如果客户端拒绝接受这个主机密钥,那么连接就中止了。让我们假设客户端接受该密钥,现在继续介绍。
5 客户端给眼务器发送一个(会话)密钥
现在客户端为双方都支持的 bulk箅法
随机生成一个新密钥,称为 会话密钥(Session Key)
。其目的是对客户端和服务器之间发送的数据进行加密和解密。所需要做的工作是把这个 会话密钥(Session Key)发送给服务器
,双方就可以启用加密并开始安全通信了。
当然,客户端不能简单地把会话密钥(Session Key)
发送给服务器。此时数据还没有进行加密,如果第三方中途截获了这个密钥,那么他就可以解密客户端和服务器之间的消息。此后你就和安全性无缘了。因此客户端必须安全地发送会话密钥(Session Key)
。 这是通过两次加密实现的:一次使用服务器的公共主机密钥(Host Key)
,一次使用服务器密钥(Server Key)
。
这个步骤确保只有服务器可以读取会话密钥(Session Key)
。在会话密钥(Session Key)
经过两次加密之后,客户端就将其发送给服务器,同时还会发送检测字节和所选定的算法(这些算法是从第4步中服务器支持的算法列表中挑选出来的)。
6 双方启用加密并完成服务器认证
在发送会话密钥之后,双方开始使用密钥和所选定的 bulk算法
对会话数据进行加密,但是在开始继续发送其他数据之前,客户端要等待服务器发来一个确认消息,该消息(以及之后的所有数据)都必须使用这个会话密钥(Session Key)
加密。这是最后一歩,它提供了服务器认证:只有目的服务器才可以解密 会话密钥(Session Key)
,因为它是使用前面的 主机密钥(Host Key)
(这个密钥已经对已知名主机列表进行了验证)进行加密的。
如果没有会话密钥(Session Key)
,假冒的服务器就不能解密以后的协议通信,也就不能生成有效的通信,客户端会注意到这一点并中断连接。
注意服务器认址是隐含的;并没有显式交换来验证服务器主机密钥(Host Key)
。因此客户端在继续发送数椐之前,必须等待服务器使用新会话密钥(Session Key)
作出有意义的响应。 从而在处理之前验证服务的身份,虽然 SSH-1 协议在这点上并没有什么特殊 . 但是 SSH-2 需要服务器认证时显示地地交换会话密钥(Session Key)
。
使用服务器密钥(Server Key)
对会话密钥(Session Key)
再进行一次加密就提供了一种称为完美转发安全性的特性。这就是说不存在永久性密钥泄露的可能,因为它不会危害到其他部分和以后SSH会话的安全性。如果我们只使用服务器主机密钥(Host Key)
来保护会话密钥(Session Key)
, 那么主机密钥(Host Key)
的泄露就会危害到以后的通倍,并允许解密原来记录下来的会话。使用服务器密钥(Server Key)
再加密次就消除了这种缺点,因为服务器密钥(Server Key)
是临时的,它不会保存到磁盘上,而且会周期性地更新(缺省情况下,一小时更新一次)。如果一个入侵者已经获取了服务器的私钥,那么他必须还要执行中间人攻击或服务器欺骗攻击才能对会话造成损害。
7 建立安全连接
由于客户端和服务器现在都知道会话密钥(Session Key)
,而其他人都不知道,因此他们就可以相互发送加密消息(使用他们一致同意的 bulk算法
)并对其进行解密了。而且,客户端还可以完成服务器认证。我们现在就已经准备好开始客户端认证了。
客户端认证
SSH提供多种客户端认证方式。
SSH-1:
- Password
- Public Key
- Kerberos
- Rhosts && RhostsRSA
- TIS
SSH-2:
- Password
- Public Key
- hostbased 在SSH-2中考虑 Rhosts 存在安全漏洞,废弃了这种方式。
这里之讨论我们经常使用的的 Password
和 Public Key
方式。
此时安全通道已经及建立,之后的所有内容都通过 Session Key
加密后进行传输。
Password
Password
方式既客户端提供用户和密码,服务端对用户和密码进行匹配,完成认证。类Unix系统中,如 OpenSSH 的框架,一般通过系统的本地接口完成认证。
Password
的优势是简单,无需任何而外的配置就可以使用。缺点密码不便于记忆,过于简单的密码容易被暴力破解。
Public Key
Public Key
认证的基本原理是基于非对称加密方式,分别在服务端对一段数据通过公钥进行加密,如果客户端能够证明其可以使用私钥对这段数据进行解密,则可以说明客户端的身份。因为服务端需要使用客户端生成的密钥对的公钥对数据首先加密,所以需要先将公钥存储到服务端的密钥库(Auhtorized Key)。还记得Github中使用git协议push代码前需要先添加SSH KEY吗?
下面详细介绍一个通过 Public Key
进行客户端认证的过程。
- 客户端发起一个
Public Key
的认证请求,并发送RSA Key
的模数作为标识符。(如果想深入了解RSA Key详细 --> 维基百科) - 服务端检查是否存在请求帐号的公钥(Linux中存储在
~/.ssh/authorized_keys
文件中),以及其拥有的访问权限。如果没有则断开连接 - 服务端使用对应的公钥对一个随机的256位的字符串进行加密,并发送给客户端
- 客户端使用私钥对字符串进行解密,并将其结合
Session ID
生成一个MD5值发送给服务端。 结合Session ID
的目的是为了避免攻击者采用重放攻击(replay attack)
。 - 服务端采用同样的方式生成 MD5值 与客户端返回的 MD5值 进行比较,完成对客户端的认证。