SSH(Secure Shell,安全外壳协议),是用于计算机之间的加密登录的一种安全协议标准。其最大的特点在于加密,这也正是它相比TelnetFTP等其他协议的优势。如果一个用户从本地计算机,使用SSH协议登录另一台远程计算机,我们就可以认为,这种登录是安全的,即使被中途截获,密码也不会泄露。

使用SSH登录只需要一行命令:

1
2
3
ssh 用户名@服务器IP          # 默认状态
ssh 服务器IP                # 当本机的用户名和服务器用户名相同时
ssh -p 8888 用户名@服务器IP  # 自定义访问端口

SSH的加密方式

加密的方式主要有两种:对称加密非对称加密。对称加密指的是加密与解密使用同一套密钥。这种加密方式在不知道密钥的情况下极其难被破解,但是在SSH这种具有多个客户端的情况下,如果大家共用一套密钥,那么只要有一个客户端的密钥发生了泄露,所有的连接都会变得不安全,所以对称加密在SSH上是不可行的,SSH使用的是非对称加密

所谓非对称加密,就是使用公钥和私钥来对数据进行加解密。经过公钥加密的数据,只能通过私钥来解密,而通过公钥来推出私钥的可能性几乎没有。其工作流程主要分为以下几步:

  1. 客户端向服务器发送连接请求
  2. 服务器返回公钥文件,客户端使用该公钥对密码进行加密后发送给服务器
  3. 服务器使用私钥解密,并与正确密码进行比对,验证客户端的身份
  4. 比对后返回结果给客户端

可以看到,私钥是始终存在于服务器端的,即便拦截到了密文,由于没有私钥也无法进行解密,这也就保证了数据的安全性。

但是,它依然有不完美之处:首先由于SSH的密钥都是自己签发的,并不像HTTPSCA证书,这也就使得SSH密钥的真伪性很难辨别。

如果服务器发送的公钥被拦截,随后拦截者发送另一个伪造的公钥给客户端,那么拦截者接收客户端发送的密文之后,就可以使用自己伪造的私钥来解密,随后得到密码就可以直接攻击目标服务器。这种风险就是著名的"中间人攻击"(Man-in-the-middle attack)。

SSH的认证方式

使用公钥指纹认证

SSH在用户第一次登录服务器时,会发出类似于下面的信息:

1
2
3
The authenticity of host 'example.com (xxx.xxx.xxx.xxx)' can't be established.
RSA key fingerprint is xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx.
Are you sure you want to continue connecting (yes/no)?

它的意思就是:无法确认主机的真实身份,但是知道它的公钥指纹,是否继续连接?

从这里可以看出,SSH的第一种认证方式就是把公钥指纹显示给客户端,让客户端进行辨别。为什么使用公钥指纹呢?因为公钥的长度很长不便于比对(通常有1024位),于是就计算公钥的hash并生成一个128位的指纹,这样就更好比对了。

那么用户怎么知道远程主机的公钥指纹应该是多少?目前并没有很好的办法,所以远程主机必须在自己的网站上贴出公钥指纹,以便用户自行核对。

如果用户确认无误,就可以输入yes,继续连接。接下来服务器会把这个客户端添加至“已信任的主机”列表(known_hosts)里。

1
2
Warning: Permanently added 'example.com (xxx.xxx.xxx.xxx)' (RSA) to the list of known hosts. 
Password: (enter password) 

使用公钥认证

公钥认证的原理如下:

  1. 客户端自己生成一对公钥和私钥,然后将公钥上传至服务器
  2. 当服务器收到该客户端的连接请求时,会将一个随机字符串返回给客户端
  3. 客户端使用自己的私钥来对该字符串进行加密,随后发送给服务器
  4. 服务器使用客户端发送的公钥进行解密,若解密结果和发送的字符串一致,那么认证成功,该客户端是可信的,以后该客户端登录服务器不需要密码

由上面的非对称加密原理,这种认证方式的安全性是毋庸置疑的。由于公钥和私钥都是客户端自己生成的,私钥只有该客户端才有,那么通过公钥加密的数据也只有该客户端能够解密,这也就起到了认证的效果。

客户端可以通过ssh-keygen命令来生成自己的公钥和私钥。在生成过程中,会提示要不要对私钥设置口令(passphrase),如果担心私钥的安全,这里可以设置一个。其他的选项一般来说一路回车保持默认即可。

这些文件默认会保存在用户文件夹的.ssh目录下,其中id_rsa是私钥文件,id_rsa.pub是公钥文件。所以接下来要做的,就是把id_rsa.pub的内容上传给服务器。

若系统为macOS或Linux,可以直接使用ssh-copy-id命令一步到位:

1
ssh-copy-id 用户名@服务器IP

若系统为Windows,则需要手动将公钥文件上传至服务器,然后使用cat命令将内容添加至$HOME/.ssh/authorized_keys文件中。

上传文件,在客户端使用scp命令:

1
2
# 第一个参数表示源文件路径,第二个参数表示目的路径,服务器IP与路径间使用逗号隔开
scp C:/Users/you/.ssh/id_rsa.pub 用户名@服务器IP:~/.ssh 

接下来在服务器端输入以下命令,将公钥内容添加到$HOME/.ssh/authorized_keys文件中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 登录服务器
ssh 用户名@服务器IP

# 检查 .ssh 目录是否存在,若不存在则新建
mkdir -p .ssh

# 创建 authorized_keys 文件,若存在则不做任何更改
touch ~/.ssh/authorized_keys

# 追加公钥内容到 authorized_keys 文件中
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

# 删除公钥文件(强迫症专用)
rm ~/.ssh/id_rsa.pub

接下来断开服务器连接,再次连接时,就不需要输入密码直接登录了: