浅析 SSH 协议远程免密登录

序言

  我们经常会通过SSH命令登录远程服务器,那么:

  • SSH是什么?
  • SSH做了什么事情?
  • 为什么有的人不需要输入密码就能直接登录?这个过程又发生了什么?

  跟随本文来研究下这些问题吧!

什么是 SSH ?

  Secure Shell(安全外壳协议,简称 SSH)是一种加密的网络传输协议,可在不安全的网络中为网络服务提供安全的传输环境[1]。SSH 通过在网络中创建安全隧道来实现 SSH 客户端与服务器之间的连接[2]。虽然任何网络服务都可以通过 SSH 实现安全传输,SSH 最常见的用途是远程登录系统,人们通常利用 SSH 来传输命令行界面和远程执行命令。使用频率最高的场合类Unix系统,但是Windows操作系统也能有限度地使用SSH。2015年,微软宣布将在未来的操作系统中提供原生 SSH 协议支持[3]

  在设计上,SSH 是 Telnet 和非安全 shell 的替代品。Telnet 和 Berkeley rloginrshrexec等协议采用明文传输,使用不可靠的密码,容易遭到监听、嗅探中间人攻击[4]。SSH 旨在保证非安全网络环境(例如互联网)中信息加密完整可靠。

  以上来源于维基百科。

  简单来说,SSH 就是一种安全协议,通常用于登陆远程服务器的加密。
  举个栗子:若一个用户从本地计算机使用 SSH 协议远程登录另一台计算机,我们就可以认为这种登录是安全的,即使被中途截获,密码也不会泄露。

  若不使用 SSH 远程登陆服务器,密码在传输过程中,可能被第三方(如黑客)所劫取,如下图:

密码被黑客劫取

  那么,SSH 协议为什么能保证安全呢?
  原因在于,SSH 协议的加密使用了以下 2 种算法:

  • 对称加密算法
  • 非对称加密算法

对称加密算法

  对称密钥算法(英语:Symmetric-key algorithm)又称为对称加密私钥加密共享密钥加密,是密码学中的一类加密算法。这类算法在加密和解密时使用相同的密钥,或是使用两个可以简单地相互推算的密钥。事实上,这组密钥成为在两个或多个成员间的共同秘密,以便维持专属的通信联系[1]。与公开密钥加密相比,要求双方获取相同的密钥是对称密钥加密的主要缺点之一[2]

种类

  常见的对称加密算法有 DES3DESAESBlowfishIDEARC5RC6

特性

  对称加密的速度比公钥加密快很多,在很多场合都需要对称加密。[3]

业务场景

img

  如图所示,客户端要给服务器发送一段信息(此处信息用Hello!表示)。
  众所周知,只要是数据包,就能被他人劫取到,为了防止被别人劫取到这些信息,就需要使用秘钥将其进行加密。相应的,服务器接收到加密后的数据后需要解密,那么也就需要使用相同的秘钥进行解密。

缺陷

  可是这样的加密方式带来了一个问题
  需要产生一个最初的秘钥,然后将其复制后发送给服务器,这样双方才能加密解密数据
  那么,复制后的秘钥应该如何发送给服务器呢?
  加密后传输给服务器没有对应的秘钥就无法解密,不加密传输又可能被他人截取破解。
  为此,我们想到了一个办法——非对称加密

非对称加密算法

  公开密钥密码学(英语:Public-key cryptography),也称为非对称式密码学(英语:asymmetric cryptography),是密码学的一种算法,它需要两个密钥,一个是公开密钥,另一个是私有密钥;一个用作加密,另一个则用作解密。使用其中一个密钥把明文加密后所得的密文,只能用相对应的另一个密钥才能解密得到原本的明文;甚至连最初用来加密的密钥也不能用作解密。由于加密和解密需要两个不同的密钥,故被称为非对称加密;不同于加密和解密都使用同一个密钥的对称加密。虽然两个密钥在数学上相关,但若知道了其中一个,并不能凭此计算出另外一个[来源请求];因此其中一个可以公开,称为公钥,任意向外发布;不公开的密钥为私钥,必须由用户自行严格秘密保管,绝不透过任何途径向任何人提供,也不会透露给被信任的要通信的另一方。

img

  如上图所示,服务器拥有公钥和私钥,它们都能加密数据,公钥加密的数据只有私钥才能解密(公钥自己无法解密),私钥加密的数据只有公钥才能解密(私钥自己无法解密)。
  所谓公钥,即公开的秘钥,任何人都能够获取,这里客户端获取了一把,若黑客想要,也可以给他。
  客户端利用公钥加密数据后给服务器,服务器可以通过私钥解密数据,但服务器通过公钥加密的数据,客户端无法解密(没有私钥)。
  若服务器通过私钥加密数据,那么客户端可以解密,但问题是:既然任何人都可以获取公钥,那么私钥加密的数据任何人都能解密,这么做的意义有什么意义呢?

  因此肯定不能使用私钥加密数据!!!
  那么服务器到底该如何给客户端发加密的数据呢?
  对于这个问题,可基于前面的对称加密来解决。

业务场景

  在客户端通过算法基于一定规则生成第三种钥匙(图中紫色钥匙,对称加密所用),当客户端获取了公钥后,通过公钥将紫钥加密传送给服务器的私钥解密,这样服务器也得到了紫钥。那么便解决了对称加密的问题,以后我们就能通过对称加密的方式来传输数据

  换而言之,非对称加密的公钥私钥主要用途并不是传输数据(虽然小数据会用到,但对大的数据,它们加密解密非常耗费时间,远远没有对称加密传输数据快),而是用来将紫钥从客户端复制到服务器,之后就可通过这对紫钥进行双向加密/解密数据通信

缺陷

  但是,需要注意的是,非对称加密方式还是存在一些问题的。
  那就是无法证明公开密钥本身就是货真价实的公开密钥
  比如,正准备和某台服务器建立公开密钥加密方式下的通信时,如何证明收到的公开密钥就是原本预想的那台服务器发行的公开密钥。或许在公开密钥传输途中,真正的公开密钥已经被攻击者替换掉了。
  为了解决上述问题,可以使用由数字证书认证机构(CA,Certificate Authority)和其相关机关颁发的公开密钥证书,此文不做进一步阐述。

SSH 的密码登陆流程

  当我们使用密码登陆远程服务器,它的整个流程是这样的:

  • ① 当远程服务器收到某个客户端的登录请求,会准备把自己的公钥发送给客户端
  • ② 客户端接收保存这个公钥后,对输入的登录密码将使用公钥加密,之后再发送给远程服务器
  • ③ 远程服务器用自己的私钥,解密公钥加密过的登录密码,若密码正确,就同意用户登录

  具体的命令步骤如下:(这里假设远程主机 IP 为192.168.2.128,正常情况肯定不是这个数字):

1
2
3
4
$ ssh root@192.168.2.128
The authenticity of host '192.168.2.128 (192.168.2.128)' can't be established.
ECDSA key fingerprint is SHA256:ogcmsTImWXFIWNYpAusKfqV3GX6j/ruJy6TQ3DVoSwI.
Are you sure you want to continue connecting (yes/no)?

  这段话的意思简单来说就是:你想要接收远程服务器192.168.2.128的公钥吗?
  你肯定要确定接收呀,那么输入yes回车。

  系统之后会出现一句提示,表示 host 主机已经得到认可,并将公钥保存在文件$HOME/.ssh/known_hosts之中。:

1
Warning: Permanently added '192.168.2.128' (ECDSA) to the list of known hosts.

  然后,会要求你输入密码。

1
root@192.168.2.128's password:

  若输入的密码正确,就可以登录了。

  当远程主机的公钥被接受以后,它就会被保存在文件$HOME/.ssh/known_hosts之中。
  下次再连接到这台主机,系统就会认出它的公钥已经保存在本地了,从而跳过警告部分,直接提示输入密码。

举个例子加深理解
  假设你遇到了多年不见的好友,而你的容貌有了很大的变化,好友认不出来。

1
好友:这SB谁啊???

  于是好友想出了一个办法来证明确实认识你

1
2
首先,好友给你一个打开了的保险箱(公钥),保险箱的密码(私钥)只有好友知道。
要求你拿张纸条写几个小时候的事(登录密码)放进保险箱加完锁(用公钥加密)再还给好友。

  当好友确认后你们就能相认了。。。
  可能有人问为什么要用保险箱?
  emmm,你的好友可能脸盲,遇到冒充你的人想和他相认,也会给其一个相同的保险箱(公钥)。
  若不用保险箱,你写的纸条被别人看到了咋办?
  这样做的目的其实是为了防止别人冒充你(从服务器层面来讲,就是为了防止别人窃取登录密码)。

SSH 远程免密登录工作流程

  平常我们远程登陆服务器,不仅每次都需要输入密码,非常麻烦,而且密码可能被黑客暴力试错破解。
  因此,我们应该禁止服务器通过密码登录。
  不过在此之前,我们需要先实现下免密登录再去那样做,不然你把密码登录关了还怎么登呀!
  下面为 SSH 实现远程免密登录的具体步骤:
  ① 首先在客户端通过特定算法生成一对秘钥(公钥私钥)

1
2
3
4
5
6
ssh-keygen -t rsa  -C "666666@gmail.com"

参数说明:
-t 加密算法类型,这里是使用 rsa 算法,若没有指定则默认生成用于 SSH-2 的 RSA 密钥。
-C 指定注释,可以方便用户标识这个密钥,指出密钥的用途或其他有用的信息。
所以在这里输入自己的邮箱或者其他信息都行

  当然,若不想要这些可以直接输入下面命令(一般也是这么做的):

1
ssh-keygen

  执行命令后,会在用户家目录~下的.ssh文件夹中生成私钥id_rsa和公钥id_rsa.pub文件。
  本地主机的.ssh的文件夹将存在以下几个文件:

  • id_rsa: 执行命令后生成的私钥文件
  • id_rsa.pub: 执行命令后生成的公钥文件
  • know_hosts: 已知的主机公钥清单(SSH 命令远程连接不同服务器时可以选择接受到不同的公钥,会将这些主机的公钥都保存在这里)

注意哦:执行上面命令后,它要求你输入加密的一些附加参数,不用管,一般默认就好,一直回车即可生成秘钥。

  ② 将客户端的公钥~/.ssh/id_rsa.pub通过ssh-copy-id -i命令拷贝到服务器

1
2
3
4
$ ssh-copy-id -i ~/.ssh/id_rsa.pub user@xxx.xxx.xxx.xxx

# user 代表 Linux 用户,xxx.xxx.xxx.xxx 代表远程主机地址,下面为例子:
$ ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.2.128

注意哦:此时需要登录到user@xxx.xxx.xxx.xxx服务器的密码(没错,这里又用到了前面 SSH 密码登录里讲过的步骤),输入正确后即可在服务器生成公钥,该公钥将保存在服务器家目录的.ssh的authorized_keys文件中(作用:存放远程免密登录的公钥,通过该文件可记录多台机器的公钥),然后退出即可。

  ③ 当客户端以后再向服务器发送连接请求,将携带自身的公钥:

1
ssh root@xxx.xxx.xxx.xxx

  ④ 服务器得到客户端的请求后,就会到authorized_keys中查找,比对该公钥是否存在,若存在,就会随机生成一个字符串;
  ⑤ 服务器将使用客户端拷贝过来的公钥将随机生成的字符串进行加密,然后发送给客户端;
  ⑥ 客户端得到服务器发送来的消息后,会使用私钥进行解密,然后将解密后的字符串发送给服务器;
  ⑦ 服务器接收到客户端发送来的字符串后,跟之前的字符串进行对比,若一致,就允许免密码登录。

扩展——禁用密码登陆

  既然开启了 SSH 免密登陆,就可以把密码登陆关闭了。这样既可以快速连接远程服务器,也可以防止黑客攻击服务器,美滋滋啊!
  具体操作步骤如下:

  • 首先修改/etc/ssh/sshd_config文件:
1
vim /etc/ssh/sshd_config
  • 对以下配置进行修改,以使用密钥登陆服务器:
1
2
3
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
  • 对以下配置进行修改,以禁用密码登陆:
1
PasswordAuthentication no
  • 最后重启sshd服务使操作生效:
1
systemctl restart sshd.service

:本地密钥请保存好,远程服务器authorized_keys中公钥也别乱修改。

参考

0%