理解SSH服务的原理

SSH全称Secure Shell,是一种加密的网络传输协议,其目的是在不安全的网络中为网络服务提供安全的传输环境。

相关文件

SSH服务会在系统的用户目录下生成一个.ssh的文件夹,文件夹下一般会有以下几个文件:

  • config : 配置文件,可以为远程主机配置host、登录端口、默认登录用户等,方便用户使用;
  • known_hosts : 记录本机访问过的服务端所提供的host key,下次访问相同主机时会核对host key,如不匹配会发出警告;
  • id_rsa : 保存本机的私钥;
  • id_rsa.pub : 保存本机的公钥;
  • authorized_keys : 本机作为服务端所保存的已授权客户端的公钥;

关于非对称加密

SSH有密码登录和证书登录两种认证方式,都是以非对称加密实现身份验证。不同于对称加密的加密解密使用同一套密钥的方式,非对称加密拥有公钥和私钥两套密钥,其中 公钥负责加密,私钥负责解密 ,公钥加密后的密文只能通过对应的私钥进行解密,而通过公钥几乎不可能推算出私钥。另外提一下,数字签名一类是使用私钥编码、公钥解码的,但是这种方式只能称为编解码而不能称作加密解密,因为公钥是公开的,拥有公钥者都可以通过密文解码出明文,加密的意义就不存在。

密码登录的验证过程

  1. 客户端向远程服务端发出登录请求,服务端把自己的公钥发给客户端;
  2. 客户端本机使用服务端提供的公钥对密码进行加密,并把密文发送至服务端;
  3. 服务端接收密文并使用私钥把密文解密出密码;
  4. 服务端通过密码验证用户身份并返回登录结果;

证书登录的验证过程

  1. 客户端事先生成本机的公钥和私钥,并把公钥追加到服务端主机的authorized_keys文件中;
  2. 客户端发出登录请求,服务端在authorized_keys中匹配该客户端对应的公钥;
  3. 服务端生成随机数,并使用客户端的公钥进行加密,然后把密文发送给客户端;
  4. 客户端通过本机私钥解密密文获得随机数;
  5. 客户端利用随机数和会话密钥session key通过MD5生成摘要Digest,并发送给服务端;
  6. 服务端使用同样的算法生成另一份Digest;
  7. 服务端对比生成的Digest和客户端发送过来的Digest,返回登录结果;

以上两种方式的验证过程是主要的流程而非全部的过程,其他还包括会话密钥session key的生成以及使用会话密钥对会话过程进行加密等等此处略过不提。另外,为了保证访问的远程主机是真正的目标主机,防止IP劫持等中间人攻击(Man-in-the-middle attack)的出现(具体手段为拦截登录请求,向客户端提供伪造的服务端公钥,并在获取密文密码后通过假公钥对应的私钥解密为明文,然后使用明文密码对真正的服务端主机进行攻击。),客户端在初次访问远程主机时会获取远程主机的公钥指纹host key(会提示无法确认主机真实性,询问是否连接)并把其添加到本机.ssh目录下的known_hosts文件中,以后访问远程主机时会先通过对比host key确保是同一主机;

证书登录的配置过程

上面提及到证书登录的前提是把公钥放到目标主机中,下面是详细的过程:

  • 客户端执行ssh-keygen命令生成私钥和公钥,会在用户目录下的.ssh文件夹下生成id_rsaid_rsa.pub两个文件,其中id_rsa为私钥文件,id_rsa.pub为公钥文件;
  • 把公钥文件的内容追加到服务端主机的~/.ssh目录下的authorized_keys中,注意是追加,没有该文件要新建,已存在则不要覆盖原有内容,完成后如果发现不能免密登录应尝试重启服务端的SSH服务(本步骤也可以直接通过在客户端执行ssh-copy-id命令完成);
  • 当然,对于安全性的更多考虑,可以增加禁止root用户登录、禁用密码登录等设置。
  • 此外,当第一次访问某台服务器的时候会因为检查到HostKey不存在而提示添加,这是因为配置项StrictHostKeyChecking的默认值为ask,即会进行对话提示确认,但某些情况下(比如执行脚本)我们并不希望该交互的出现,因此可以添加参数-o StrictHostKeyChecking=no或者在~/.ssh/config配置文件中添加配置StrictHostKeyChecking no来跳过该对话,不过该方法相当于降低了安全性,需要自行斟酌使用。