1. SSH是什么

简单说,SSH是一种网络协议,用于计算机之间的远程加密登录。

SSH 为 Secure Shell的缩写,由 IETF 的网络小组(Network Working Group)所制定,SSH 为建立在应用层基础上的安全协议。SSH 是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议。利用 SSH 协议可以有效防止远程管理过程中的信息泄露问题。SSH最初是UNIX系统上的一个程序,后来又迅速扩展到其他操作平台。SSH安装容易、使用简单,而且比较常见,一般的Unix系统、Linux系统、FreeBSD系统都附带有支持SSH的应用程序包。Windows如果需要使用SSH,可以安装PuTTY或者Cygwin。最初的SSH是由芬兰的一家公司开发的。但是因为受版权和加密算法的限制,现在很多人都转而使用OpenSSH。OpenSSH是SSH的替代软件包,而且是免费的,可以预计将来会有越来越多的人使用它而不是SSH。

2. SSH原理

非对称加密技术

SSH 服务基于非对称加密(public-key cryptography,也称公开密钥加密)技术实现数据加密传输。该技术会生成一对数学相关的密钥,其中一个(公钥)用于对数据进行加密,而且只能用于加密,而另一个(私钥)只能用于解密。使用加密密钥加密后的数据,只能用对应的解密密钥才能解密。而且,只知道其中一个密钥,无法计算出另一个。因此,如果公开了一对密钥中的一个,并不会危害到另一个的秘密性质。通常把公开的密钥称为公钥(public key),而不公开的密钥称为私钥(private key)。

一般来说,非对称加密的应用场景有两个:

  • 公钥是用来解密的。比如A给B发送消息,B使用A的公钥验证A发布的数据或文件的完整性、准确性,以防止数据篡改。相应的密钥称为数字签名(数字证书)。
  • 公钥是用来加密的。比如A给B发送消息,A使用B的公钥给消息加密,以防被劫持后泄漏。SSH服务基于该场景实现。

与对称密钥加密相比,非对称加密的优点在于不存在共享的通用密钥。由于解密用的私钥无需发送给任何用户,所以可以避免密钥被劫持或篡改。而加密用的公钥即便被劫持或篡改,如果没有与其匹配的私钥,也无法解密数据。所以,截获的公钥是没有任何用处的。

当前,SSH主要采用 RSA 算法(协议 V2 默认算法)和 DSA 算法(协议 V1 仅支持该算法)来实现非对称加密技术。

3. SSH连接交互过程

ssh 需要sudo_ssh 需要sudo

SSH连接时,整个交互过程如上图。,主要可以分为三个阶段

服务端准备阶段

服务端在每次启动 SSH 服务时,都会自动检查 /etc/ssh/ 目录下相关密钥文件的有效性。如果相关文件检查发现异常,则会导致服务启动失败,并抛出相应错误信息。 如果文件相关不存在,则会自动重新创建。

默认创建的相关文件及用途说明如下:

root@vultr:~# ll /etc/ssh				
-rw-r--r--  1 root root 553122 Feb 10  2018 moduli				# 用于 DH-GEX 算法
-rw-r--r--  1 root root   1580 Feb 10  2018 ssh_config			# SSH 客户端配置文件
-rw-r--r--  1 root root   3249 Jul 19  2018 sshd_config			# SSH 服务配置文件
-rw-------  1 root root    227 Aug  2  2018 ssh_host_ecdsa_key		# DSA 算法私钥
-rw-r--r--  1 root root    172 Aug  2  2018 ssh_host_ecdsa_key.pub	# DSA 算法公钥
-rw-------  1 root root   1675 Aug  2  2018 ssh_host_rsa_key	# SSH V2版RSA算法私钥
-rw-r--r--  1 root root    392 Aug  2  2018 ssh_host_rsa_key.pub # SSH V2版RSA算法公钥
-rw-r--r--  1 root root    338 Jul 19  2018 ssh_import_id
非对称加密协议

当服务器SSH服务启动,客户端也安装了SSH后,就可以进行连接了。

  • 客户端向服务端发送连接请求。
    客户端连接服务器有两种方式:1. 密码登录。2. 密钥登录。
    客户端通过SSH工具连接服务端。相关信息通过明文发送,发送的请求包括IP地址和用户名,服务器识别对应的客户端公钥(保存在$HOME/.ssh/authorized_keys中),找到该公钥后,服务器通过公钥加密一段随机字符串,并使用共享密钥加密后发送给客户端。如果未找到公钥,则需要输入密码。
  • 服务端返回公钥信息
    根据客户端所使用的服务协议版本及算法设置,返回相应公钥信息。比如,默认情况下,客户端通过 SSH V2 版协议,基于 RSA 算法建立连接,则服务端将 ssh_host_rsa_key.pub 文件中的内容返回客户端。相关信息通过明文发送。
  • 客户端对服务端公钥信息进行比对和确认
    客户端接收到服务端公钥信息后,会进行如下比对,并让用户对相关信息进行确认。
  • 如果是首次连接服务端,客户端会收到类似如下信息,让用户确认公钥指纹的有效性:
The authenticity of host '192.168.0.1 (192.168.0.1)' can't be established.
RSA key fingerprint is c2:49:d9:43:74:d5:ed:bc:28:9b:d2:7b:63:94:cf:bc.
Are you sure you want to continue connecting (yes/no)?
  • 如果用户输入 no ,则连接中断并报错(Host key verification failed)。
  • 如果用户输入 yes,则会将相应的公钥信息,保存到当前用户家目录下 .ssh 目录内的 known_hosts 文件中。 比如:cat ~/.ssh/known_hosts
# IP 明文显示:  
192.168.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA3sdlboGEgY9buZpkPuygPw0NxAvmxYd0mc3fo2MgP+JqgFC9/9ZXOgDXKJrjE2HKBieJZSPKGncIh+zLxTvmykeJQBXv7i1GiUjW+H3VY69Ge3AdGfCd+XF+Cvi1e+j18zhHnjSzvIBoNpT5cBWWNbw7mNHCwTb0sHAVUkWR4Ck/LM5/rQ09A+m6BLfZJL8CRNGxKTbyINi6o812S+Cy64WqDs1nTpIXp2Bkcpjclb36bFSs9Z/tWNuJl7A//7HNtxMgFGBnE07Ykvvy8s06DUmkyFy8GcXGBpnfdg9utLodfQLFQnKflCQZ110BpQaCWlWPjU9dc4w3XLJ/XQOP4w==

# IP 加密处理:
3efXAZ4sNHcUcHamBy4gDriblc8=|8idBhLq9aLl2sfh4KswMsk4sPFI= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAwS4DE3hok8RCkxYlTxsexNrNa62e05UGSkoP7ie26DDWjG1Aoc74cCsE4is9p7lEfFUYYlAzeYhPqE/yGf5YxRZUOU2IeFI4cEqo8YZr7edVYpgAq2f2J0zMwk1syenD12lmUPkYA4mMB6it3jxXR5k+H0HZh9YA7mRXkiTjlkAMWirBcnUvtKYRv9LRIr3ikUiPy2gfZO291Ae9zuTsWwEtHQxIpiBgk3vwF2gCUFlX9y//IsMjdQq5prk7x3BjXhUorqgJO1gt1VHW8Xxx9Oe50YF1hi9DuE6VXwyh4xfHTmauRQybwsYafdA3HxrA2od6x9l19D9EH7xHAjDa5w==
  • 如果服务端因重装系统等因素导致公钥指纹出现变化,则会直接导致连接失败(报错Host key verification failed),则需要删除已保存的条目后再重新连接。
  • 如果之前已经成功连接,而且公钥指纹对比一致,则会继续下一步操作。
  • Server端接收到Client的连接请求后,会在authorized_keys中匹配到Client的公钥pubKey,并生成随机数R,用Client的公钥对该随机数进行加密得到pubKey®,然后将加密后信息发送给Client。
  • Client端通过私钥进行解密得到随机数R,然后对随机数R和本次会话的SessionKey利用MD5生成摘要Digest1,发送给Server端。
  • Server端会也会对R和SessionKey利用同样摘要算法生成Digest2。
  • Server端会最后比较Digest1和Digest2是否相同,完成认证过程
后续数据交互过程

后续登录校验及正常的数据传输,都会通过双向加密方式进行。相关交互说明如下:

  • 如果服务端需要发送数据给客户端:
  • 服务端使用所持有的客户端公钥,对需要传输的数据进行加密,再发送给客户端。
  • 客户端收到信息后,使用所持有的自身私钥解密后获取数据。
  • 如果客户端需要发送数据给服务端,也是类似的流程:
  • 客户端使用所持有的服务端公钥,对需要传输的数据进行加密,再发送给服务端。
  • 服务端收到信息后,使用所持有的自身私钥解密后获取数据。

4. SSH相关配置文件

从这个连接过程中,可以看出,主要使用到两个文件夹下的内容:

~/.ssh/ 这里主要是客户端连接信息和密钥存储的敌方。这个文件夹下的内容可以使用命令ssh-keygen生成,执行这个命令后,会生成两个文件,id_rsaid_rsa.pub。第一个是私钥文件,第二个是公钥文件。公钥需要上传到服务端 ~/.ssh/authorized_keys中,但是这都是针对当前登录用户的。也就是说,只对当前用户有效。

/etc/ssh/ 这个主要是ssh配置和服务器密钥存储的地方。由SSH服务启动时生成。

在连接中的两个过程:

  • 客户端存储服务器的公钥到本地
    也就是上面的第二步。服务器返回回来的公钥在/etc/ssh下,比如
root@vultr:~# ls /etc/ssh
moduli       ssh_host_ecdsa_key      ssh_host_ed25519_key.pub  ssh_import_id
ssh_config   ssh_host_ecdsa_key.pub  ssh_host_rsa_key
sshd_config  ssh_host_ed25519_key    ssh_host_rsa_key.pub
root@vultr:~# cat /etc/ssh/ssh_host_ecdsa_key.pub
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDlzdrNE506YkXpPznE4DPPBa0GXQ4U2wxkaVxhIiaJHnaHds30okQeG/auZf1mslwZYmMBs7dN4wL/ERjKGNTY= root@guest

之所以有多组密钥,是因为使用了不同的加密算法。
客户端接收到之后,会存储在~/.ssh/known_hosts文件里,查看这个文件,可以看到有一行与服务器ssh_host_ecdsa_key.pub内容一致。

pyjun@DESKTOP-49M11D1:~/.ssh$ cat known_hosts
|1|5HUCN+t1jFIpHxBWvuUcnijkvH8=|GVx2vO71UnftuIF6I7gX3qAo5OY= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDlzdrNE506YkXpPznE4DPPBa0GXQ4U2wxkaVxhIiaJHnaHds30okQeG/auZf1mslwZYmMBs7dN4wL/ERjKGNTY=
  • 客户端将公钥存储到服务器中
    在请求连接过程中,如果使用密钥的方式登录,那么就需要先把客户端的公钥存储到服务器中。
ssh-keygen   				# 生成当前账户的私钥和公钥,一路回车即可
ssh-copy-id user@host		# 将公钥传送到远程主机host上面

其实,它是将客户端的~/.ssh/id_rsa.pub文件内容追加到服务器文件~/.ssh/authorized_keys中。

~/.ssh/authorized_keys里表示本机可以被哪些机器访问
~/.ssh/known_hosts里表示本机访问过哪些机器

5. SSH相关配置

SSH配置文件有两个,一个是ssh_config,一个是sshd_config。前者是客户端配置,后者是服务器配置。也就是说,如果本机是作为客户端,那么就修改第一个配置,如果本机是作为服务器,那么就修改第二个配置,

一般来说,和密钥登录的配置有关的有以下几个,如果密钥配置好后无法登录,尝试配置以下三项。

RSAAuthentication yes
PubkeyAuthentication yes 					# 如果为yes,则无法使用密码登录
AuthorizedKeysFile .ssh/authorized_keys		# 默认服务端公钥文件位置

其他

passwordAuthentication  no  				# 只能通过私钥访问

6. SSH相关命令

  • 常见的登录命令
# 以用户名user,登录远程主机host
$ ssh user@host

# 本地用户和远程用户相同,则用户名可省去
$ ssh host

# SSH默认端口22,可以用参数p修改端口
$ ssh -p 2017 user@host
  • 生成秘钥操作
$ ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
# -t:指定生成密钥类型(rsa、dsa、ecdsa等)
# -P:指定passphrase,用于确保私钥的安全(相当于密码,可以为空)
# -f:指定存放密钥的文件(公钥文件默认和私钥同目录下,不同的是,存放公钥的文件名需要加上后缀.pub)

# 然后我们看一下 ~/.ssh 文件夹下的内容
$ ll ~/.ssh
-rw-r--r-- 1 root root 1199 Jun 27 23:58 authorized_keys	# 保存已授权的客户端公钥
-rw------- 1 root root 1675 Jun 27 16:48 id_rsa				# 保存私钥
-rw-r--r-- 1 root root  398 Jun 27 16:48 id_rsa.pub			# 保存公钥
-rw-r--r-- 1 root root  666 Jun 27 17:57 known_hosts		# 保存已认证的远程主机ID

7. SSH安全

中间人攻击

传统的网络服务程序,如FTP、Pop和Telnet在传输机制和实现原理上是没有考虑安全机制的,其本质上都是不安全的。因为它们在网络上用明文传送数据、用户帐号和用户口令,别有用心的人通过窃听等网络攻击手段非常容易地就可以截获这些数据、用户帐号和用户口令。而且,这些网络服务程序的简单安全验证方式也有其弱点,那就是很容易受到"中间人"(man-in-the-middle)这种攻击方式的攻击。

所谓"中间人"的攻击方式,就是"中间人"冒充真正的服务器接收你的传给服务器的数据,然后再冒充你把数据传给真正的服务器。服务器和你之间的数据传送被"中间人"一转手做了手脚之后,就会出现很严重的问题。中间人能够攻击,主要原因在于他能认识截取的信息,也能发出让接受者认识的信息。

使用SSH,你可以把所有传输的数据进行加密,这样"中间人"这种攻击方式就不可能实现了,而且也能够防止DNS欺骗和IP欺骗。使用SSH,还有一个额外的好处就是传输的数据是经过压缩的,所以可以加快传输的速度。SSH有很多功能,它既可以代替Telnet,又可以为FTP、Pop、甚至为PPP提供一个安全的"通道"。

其实known_hosts这个文件可以通过Client和Server的双向认证,从而避免中间人(man-in-the-middle attack)攻击,每次Client向Server发起连接的时候,不仅仅Server要验证Client的合法性,Client同样也需要验证Server的身份,SSH client就是通过known_hosts中的host key来验证Server的身份的。