⽹络协议-应⽤层协议-远程登陆协议
远程登录协议
通过 TELNET 模拟实现 HTTP 和 SMTP 客户端
所谓远程登录指的是从本地计算机登录到⽹络另⼀端的计算机(通常是服务器或者云主机实例),远程登录成功后,就可以直接使⽤这些主机上的应⽤,还可以对这些计算机进⾏参数配置。
适⽤于远程登录的协议主要有两种:TELNET 和 SSH(Secure SHell)。我们⾸先来介绍 TELNET。
TELNET 基于 TCP 连接将向主机发送⽂字命令并在主机上执⾏,常⽤于登录路由器或⾼性能交换机等⽹络设备进⾏相应的设置。
TELNET 客户端是指利⽤ TELNET 协议实现远程登录的客户端程序,很多情况下,它的程序名就是 telnet 命令。TELNET 客户端通常与⽬标主机的23 号端⼝连接,并与监听这个端⼝的服务端程序 telnetd 进⾏交互。
注:在 Windows 系统中,要启⽤ TELNET 客户端,需要在「控制⾯板->程序和功能->启⽤或关闭 Windows 功能」中勾选「TELNET客户端」并确定⽅能在命令⾏中使⽤。
telnet命令格式如下:
telnet 主机名(或IP) TCP端⼝号
端⼝号为 80 时连接 HTTP、为 25 时连接 SMTP、为 21 时连接到 FTP。⽐如我们可以通过telnet连接到 www.baidu 的 80 端⼝:
这种⽅法还可⽤于测试服务器指定端⼝是否可⽤。我们还可以通过客户端telnet命令模拟 HTTP 请求,⽐如我们现在通过GET⽅法访问百度
⾸页(输⼊请求头后两次回车才会返回响应):
还可以直接构建查询请求:
理论上⽀持所有类型的 HTTP 请求,只要你构造的 HTTP 请求头和请求实体符合 HTTP 协议规范(关于 HTTP 协议我们后⾯会详细介绍)。
当然,TELNET 不仅构建限于 HTTP 请求,我们还可以通过它来模拟邮件发送,以 163 邮箱为例,在开始之前,我们先把⽤户名和密码通过 Base64编码进⾏加密处理,然后通过客户端telnet命令构建 SMTP 请求发送邮件:
~ telnet smtp.163 25    // 通过 25 号端⼝登录 163 SMTP 服务器
Trying 220.181.
Connected to smtp.163.
Escape character is '^]'.
220 163 Anti-spam GT for Coremail System (163com[20141201])
HELO localhost  // 通过 HELO 指令向服务器打招呼,并告知客户端机器的名字,可以随便取
250 OK
AUTH LOGIN      // 通过认证指令发起认证请求
334 dXNlcm5hbWU6
// 这⾥输⼊Base64编码后的⽤户名,注意⽤户名不要带 @163 后缀
334 UGFzc3dvcmQ6
/
/ 这⾥输⼊Base64编码后的密码
235 Authentication successful
MAIL FROM:<yaojinbu@163>  // 通过 MAIL FROM 指令指定发件箱
250 Mail OK
RCPT TO:<yaojinbu@outlook>  // 通过 RCPT TO 指令指定收件箱
250 Mail OK
DATA            // 通过 DATA 指令声明开始编写邮件正⽂,最后⼀⾏必须是 .
354 End data with <CR><LF>.<CR><LF>
From:yaojinbu@163
To:yaojinbu@outlook
Subject:TELNET MAIL
This is a mail from TELNET CLIENT!
.
250 Mail OK queued as smtp8,DMCowACXJ5aCjplcp0_eMg--.60339S2 1553567438
QUIT            // 邮件发送成功,断开连接
221 Bye
Connection closed by foreign host.
这个时候,到收件箱就能看到刚刚通过 TELNET 客户端发送出去的邮件了,关于上述命令及返回的 SMTP 响应码后⾯介绍 SMTP 协议的时候还会详
细介绍,这⾥我们主要是为了熟悉 TELNET 客户端的使⽤。
此外,TELNET 客户端作为仿真客户端,理论上还可以⽤于模拟所有应⽤层协议通信,⽐如基于 FTP 进⾏⽂件传输等,这⾥我们可以看到,通过遵循相应的应⽤层协议规范,我们完全可以⾃⼰实现简单的 HTTP、电⼦邮件和⽂件传输客户端,⽇常在操作系统上所使⽤的浏览器、Outlook、邮箱⼤师、FileZill
a 等客户端软件不过是在此基础上提供了更加丰富的功能和更好的⽤户体验⽽已,了解这些底层的协议规范并模拟其实现,可以帮助我们更好的理解⽇常使⽤软件、⼯具的底层原理,也能帮助我们阅读很多框架和⼯具的底层源码,最终编写更加健壮的代码。
TELNET 虽然很简单,功能也⽐较齐全,但是在传输过程中没有对数据进⾏加密,有被窃听的风险,所以我们来介绍⼀种更加安全的远程登录协议—— SSH。
SSH 的基本使⽤与底层原理探究
基本使⽤
TELNET ⽆需任何认证即可发送内容,容易造成通信窃听和⾮法侵⼊的危险。SSH 是英⽂ Secure Shell 的简写,顾名思义,可以加密通信内容,是加密的远程登录协议,通过在⽹络中建⽴安全隧道来实现 SSH 客户端与服务器之间的连接,可以在不安全的⽹络中为⽹络服务提供安全的传输环境。
SSH 本⾝是⼀种协议,有多种实现,最常见的就是开源的,我们在⽇常使⽤过程中最多的场景就是通过它来实现远程登录,既可以在终端直接通过ssh 命令登录远程主机:
ssh <⽤户名>@<;主机名或IP地址>
⽐如,要以 root ⾝份登录主机名为 homestead 的机器,可以通过 ssh root@homestead 进⾏连接。如果需要密码验证的话,下⼀步会提⽰你输⼊密码。
也可以通过客户端⼯具⽐如、、等进⾏远程操作:
服务器上有对应的后台守护进程sshd响应客户端 SSH 远程连接,默认监听端⼝号是 22,当然,你也可以⾃定义这个端⼝号然后在连接时通过-p选项指定:
ssh -p 2222 root@homestead  // 远程 sshd 端⼝号是 2222
关于 SSH 的基本使⽤就是这样,⽐较简单,登录成功后,即可通过命令⾏在远程主机上进⾏操作和配置。下⾯我们来看看 SSH 协议的原理,它是如何确保传输通道的安全的。
底层原理
SSH 之所以能够保证安全,原因在于它采⽤了公钥加密。
整个过程是这样的:
远程主机收到⽤户的登录请求,把⾃⼰的公钥发给⽤户;
⽤户使⽤这个公钥,将登录密码加密后,发送回来;
远程主机⽤⾃⼰的私钥,解密登录密码,如果密码正确,就同意⽤户登录。
ssh命令指定端口这个过程本⾝是安全的,但是实施的时候存在⼀个风险:如果有⼈截获了登录请求,然后冒充远程主机,将伪造的公钥发给⽤户,那么⽤户很难辨别真伪。因为不像 HTTPS 协议,SSH 协议的公钥是没有认证中⼼(CA)公证的,也就是说,都是⾃⼰签发的。
可以设想,如果攻击者插在⽤户与远程主机之间,⽤伪造的公钥,获取⽤户的登录密码,再⽤这个密码登录远程主机,那么 SSH 的安全机制就荡然⽆存了。这种风险就是著名的「中间⼈攻击」。
SSH 协议是如何应对这种攻击的呢?实际上,在通过 SSH ⾸次进⾏远程登录的时候,系统会要求你⽐对返回的公钥是否与⽬标服务器上的公钥信息⼀致。
如果你是第⼀次登录远程主机,系统会出现下⾯的提⽰:
思是⽆法确认远程主机的真实性,只知道它的公钥指纹,问你是否还想继续连接。之所以提⽰ ECDSA 类型的公钥指纹,是因为指纹⽐公钥的长度要短,所以更容易⽐较。
那怎么知道远程主机的公钥指纹应该是多少?这个可以通过ssh-keyscan命令结合ssh-keygen得到。ssh-keyscan命令可获取服务器公钥:ssh-keyscan -t ECDSA -p
⽽ssh-keygen命令可以计算公钥的指纹:
ssh-keygen -E sha256 -lf ~/.ssh/known_hosts
只要计算⼀下服务器上的相应公钥的指纹,并与客户端获取的指纹进⾏⽐对⼀致,就能确定连接的是否是公钥对应的服务器。
注:某些 SSH 终端默认提供的是 MD5 格式的公钥指纹,此时,只需将 -E 指定的参数设置为 md5 即可。
如果输⼊yes确认,则系统会提⽰远程主机已经得到认可:
Warning: Permanently added ',114.215.241.29' (ECDSA) to the list of known hosts.
同时服务器 SSH 公钥会添加到本地的~/.ssh/known_hosts⽂件⾥⾯。然后终端会提⽰你输⼊密码登录远程主机:
's password:
如果密码正确,就可以登录了。下次再连接这台主机,由于~/.ssh/known_hosts⽂件⾥已经包含了对应的公钥信息,就会跳过警告部分,直接提⽰输⼊密码。
⼀般⽽⾔,系统中每个⽤户都有⾃⼰的known_hosts⽂件,此外还有⼀个系统级的/etc/ssh/ssh_known_hosts⽂件,⽤于保存对所有⽤户都可信赖的远程主机的公钥。
使⽤公钥登录
每次都要输⼊密码登录,有些繁琐,你还可以配置每次直接使⽤公钥进⾏登录,所谓「公钥登录」,原理很简单,就是⽤户将⾃⼰的公钥储存在远程主机上。登录的时候,远程主机会向⽤户发送⼀段随机字符串,⽤户⽤⾃⼰的私钥加密后,再发回来。远程主机⽤事先储存的公钥进⾏解密,如果成功,就证明⽤户是可信的,直接允许登录,不再要求密码。
⽣成私钥公钥⽂件
如果你的本地主机不包含~/.ssh/id_rsa⽂件,可以通过运⾏如下命令⽣成:
ssh-keygen -t rsa
每次执⾏上述命令后产⽣的私钥⽂件都不同,如果~/.ssh/id_rsa⽂件已经存在,会提⽰是否覆盖,选择 n 不覆盖,如果该⽂件不存在则会⽣成该⽂件(提⽰需要输⼊的地⽅都可以留空)。
运⾏该命令会⽣成~/.ssh/id_rsa和~/.ssh/id_rsa.pub两个⽂件,分别存储私钥和公钥,接下来我们通过scp命令将公钥⽂件拷贝到远程服务器的.ssh⽬录下(如果服务器上没有该⽬录,先创建这个⽬录)
scp ~/.ssh/id_rsa.pub root@your-server-ip:~/.ssh
将公钥添加到授权KEY
登录到远程服务器,运⾏如下命令将公钥⽂件内容追加到~/.ssh/authorized_keys⽂件:
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
如果服务器上没有~/.ssh/authorized_keys这个⽂件,可以运⾏如下命令将~/.ssh/id_rsa.pub⽂件拷贝过来:
cp id_rsa.pub authorized_keys
接下来,我们在本地主机测试通过 ssh 登录远程服务器:
注意将上述命令中的 IP 地址替换成⾃⼰的服务器公⽹ IP,这样⼀来,就可以⽆需输⼊密码即可登录远程服务器了,⾮常⽅便。