SSH(Secure Shell) 是较可靠,专为远程登录会话和其他网络服务提供安全性的协议,利用 SSH 协议可以有效防止远程管理过程中的信息泄露问题。
实际应用中,主要用于保证远程登录和远程通信的安全,任何网络服务都可以用这个协议来加密。
1995年,芬兰赫尔辛基工业大学的研究员 Tatu Ylönen 设计了 SSH 协议的第一个版本(现称为 SSH 1),同时写出了第一个实现(称为 SSH1)。
当时,他所在的大学网络一直发生密码嗅探攻击,他不得不为服务器设计一个更安全的登录方式。写完以后,他就把这个工具公开了,允许其他人免费使用。
SSH 可以替换 rlogin、TELNET、FTP 和 rsh 这些不安全的协议,所以大受欢迎,用户快速增长,1995年底已经发展到五十个国家的20,000个用户。SSH 1 协议也变成 IETF 的标准文档。
1995年12月,由于客服需求越来越大,Tatu Ylönen 就成立了一家公司 SCS,专门销售和开发 SSH。这个软件的后续版本,逐渐从免费软件变成了专有的商业软件。
SSH 1 协议存在一些安全漏洞,所以1996年又提出了 SSH 2 协议(或者称为 SSH 2.0)。这个协议与1.0版不兼容,在1997年进行了标准化,1998年推出了软件实现 SSH2。但是,官方的 SSH2 软件是一个专有软件,不能免费使用,而且 SSH1 的有些功能也没有提供。
1999年,OpenBSD 的开发人员决定写一个 SSH 2 协议的开源实现,这就是 OpenSSH 项目。该项目最初是基于 SSH 1.2.12 版本,那是当时 SSH1 最后一个开源版本。但是,OpenSSH 很快就完全摆脱了原始的官方代码,在许多开发者的参与下,按照自己的路线发展。OpenSSH 随 OpenBSD 2.6 版本一起提供,以后又移植到其他操作系统,成为最流行的 SSH 实现。目前,Linux 的所有发行版几乎都自带 OpenSSH。
现在,SSH-2 有多种实现,既有免费的,也有收费的。
SSH 的软件架构是服务器-客户端模式(Server - Client)。在这个架构中,SSH 软件分成两个部分:向服务器发出请求的部分,称为客户端(client),OpenSSH 的实现为 ssh;接收客户端发出的请求的部分,称为服务器(server),OpenSSH 的实现为 sshd。另外,OpenSSH 还提供一些辅助工具软件(比如 ssh-keygen 、ssh-agent)和专门的客户端工具(比如 scp 和 sftp)。
连接建立
SSH依赖端口进行通信。在未建立SSH连接时,SSH服务器会在指定端口侦听连接请求,SSH客户端向SSH服务器该指定端口发起连接请求后,双方建立一个TCP连接,后续会通过该端口通信。
默认情况下,SSH服务器使用端口号22。当SSH应用于NETCONF时,可以指定默认端口号是22或者830。SSH使用的端口号可以被更改为设备其他可用端口,更改后当前所有的连接都会断开,SSH服务器开始侦听新的端口。由于SSH默认端口号22为知名端口,在进行关键安全传输时,建议修改SSH端口号。
版本协商
SSH协议目前存在SSH1.X(SSH2.0之前的版本)和SSH2.0版本。SSH2.0协议相比SSH1.X协议来说,在结构上做了扩展,可以支持更多的认证方法和密钥交换方法,同时提高了服务能力。SSH服务器和客户端通过协商确定最终使用的SSH版本号,过程如下:
算法协商
SSH工作过程中需要使用多种类型的算法,包括用于产生会话密钥的密钥交换算法、用于数据信息加密的对称加密算法、用于进行数字签名和认证的公钥算法和用于数据完整性保护的HMAC算法。SSH服务器和客户端对每种类型中具体算法的支持情况不同,因此双方需要协商确定每种类型中最终使用的算法,过程如下:
密钥交换
SSH服务器和客户端通过密钥交换算法,动态生成共享的会话密钥和会话ID,建立加密通道。会话密钥主要用于后续数据传输的加密,会话ID用于在认证过程中标识该SSH连接。
由于SSH服务器和客户端需要持有相同的会话密钥用于后续的对称加密,为保证密钥交换的安全性,SSH使用一种安全的方式生成会话密钥,由SSH服务器和客户端共同生成会话密钥,利用数学理论巧妙地实现不直接传递密钥的密钥交换,无需通过不安全通道传送该密钥,具体过程如下图所示。
- 1. SSH服务器生成素数G、P、服务器私钥b,并计算得到服务器公钥y=(G^b)%P。
- 2. SSH服务器将素数G、P、服务器公钥y发送给SSH客户端。
- 3. SSH客户端生成客户端私钥a,计算得到客户端公钥x=(G^a)%P。
- 4. SSH客户端将客户端公钥x发送给SSH服务器。
- 5. SSH服务器计算得到对称密钥K=(x^b)%P,SSH客户端计算得到对称密钥K=(y^a)%P,数学定律可以保证SSH服务器和SSH客户端生成的对称密钥相同。
用户认证
SSH客户端向SSH服务器发起认证请求,SSH服务器对SSH客户端进行认证。SSH支持以下几种认证方式:
密码认证
密码认证的基本原理是SSH客户端使用服务器公钥对密码进行加密,SSH服务器使用服务器私钥解密后验证密码的合法性,具体过程如下图所示。
SSH密码认证登录流程
- 1. SSH客户端向SSH服务器发送登录请求。
- 2. SSH服务器将服务器公钥发送给SSH客户端。
- 3. SSH客户端输入密码,使用服务器公钥加密密码后发送给SSH服务器。
- 4. SSH服务器收到密文,使用服务器私钥解密得到密码。验证密码是否正确,如果正确则认证通过。
但是,这种认证方式存在中间人攻击的风险,如果有人截获了SSH客户端的登录请求后,冒充SSH服务器将伪造的公钥发送给SSH客户端,就可以获取到用户的登录密码。所以,在首次登录SSH服务器时,SSH客户端上会提示公钥指纹,并询问用户是否确认登录。用户确认后公钥将被保存并信任,下次访问时,SSH客户端将会核对SSH服务器发来的公钥和本地保存的是否相同。这种方式适用于公布了公钥指纹的SSH服务器以及已登录过正确SSH服务器的SSH客户端。
密钥认证
为避免中间人攻击,可以使用安全性更高的密钥认证。密钥认证的基本原理是SSH服务器使用客户端的公钥对随机内容加密,SSH客户端使用自己的私钥解密并发送给服务器以证实自己的身份,具体的过程如下图所示。
SSH密钥认证登录流程
会话请求
认证通过后,SSH客户端向服务器发送会话请求,请求服务器提供某种类型的服务,即请求与服务器建立相应的会话。服务器根据客户端请求进行回应。
会话交互
会话建立后,SSH服务器端和客户端在该会话上进行数据信息的交互,双方发送的数据均使用会话密钥进行加解密。
这个时候还不能直接启动,直接启动会提示配置错误,因为公钥还没生成:
上面第一条 sshd 命令失败提示“sshd re-exec requires execution with an absolute path”,是为了防止有人出于各种目的,放置同名软件在$PATH变量指向的目录中,代替真正的 sshd,此时需要使用绝对路径来启动。也就是下一条的 /usr/sbin/sshd
此时启动仍然失败因为还没有公钥文件,执行命令:
ssh-keygen -A
图上左侧执行命令的窗口,右侧是查看配置文件夹的窗口,右侧上是执行前的配置文件,下是执行后的配置文件,可以看到生成了 ssh_host_dsa_key 与 ssh_host_dsa_key.pub 这两个文件,其他的一些文件是:
sshd_config.d 是一个目录,用于存放 SSH 服务器(sshd)的配置文件。这些配置文件通常以 .conf 结尾,并使用不同的前缀命名,如 sshd_config、sshd_default_config 等。这些配置文件允许管理员对 SSH 服务器进行详细的设置,例如端口号、认证方式、日志记录等。
然后进入ssh_config 文件编辑参数,可以设置的参数如下:
参数 | 默认值 | 参数说明 |
---|---|---|
Port | 22 | sshd服务默认的端口22,为了安全考虑建议修改成其它端口;配置文件可以使用多个Port命令,同时监听多个端口 |
AddressFamily | any | 设置协议簇,默认支持IPV4和IPV6 |
ListenAddress | 0.0.0.0 | 默认监听网卡所有的IP地址 |
PermitRootLogin | yes | 是否允许root登陆,默认是允许的,建议设置成no |
StrictModes | yes | 当使用者的host key改变之后,server就不接受其联机 |
MaxAuthTries | 6 | 最多root尝试6次连接 |
MaxSessions | 10 | 最大允许保持多少个未认证的连接。默认值是 10 到达限制后,将不再接受新连接,除非先前的连接认证成功或超出 LoginGraceTime 的限制。 |
PrintMotd | yes | 登陆后是否显示一些默认信息 |
PrintLastLog | yes | 显示上次登录的信息 |
TCPKeepAlive | yes | ssh server会传keepalive信息给client以此确保两者的联机正常,任何一端死后,马上断开 |
PasswordAuthentication | yes | 是否允许使用基于密码的认证。默认为”yes”。 |
PermitEmptyPasswords | no | 是否允许密码为空的用户远程登录。默认为”no”。 |
还有很多参数就不全列出来了,有兴趣可以自己查询; https://man.openbsd.org/sshd_config
此时配置就完成了,再次使用 /usr/sbin/sshd 启动服务,然后查看一下服务是否存在:
我用的是win10系统,win10默认自带SSH客户端,因此不需要安装
看到图上我先是使用默认端口连接失败,然后指定端口在连接,会先问你是否确定XXX,输入yes就行,然后输入要登录的账号密码,就连接成功了;
实际开发一般都是办公提供一台windows机器,然后远程连到开发机器上编码,以VSCODE为例:
安装VSCODE ,然后安装 remote-SSH 插件;
在左下角选择“打开远程窗口”,然后新建ssh连接,然后输入刚刚的ssh指令,
然后后面还需要选连接机器平台(linux/windows/XXX)以及输入密码, 这些都输入后最后连接成功效果如:
左下角连接按钮显示了连接的IP, 打开终端后也是连接的机器了。