# 前言

本文主要讨论telnet,ssh和https用途及原理。

# telnet

它是teletype network的缩写,现在成为一个专有名词,表示远程登录协议和方式,Telnet协议是TCP/IP中的应用层协议。RFC 854定了该协议规范。

它为用户提供了在本地计算机操作远程主机工作的能力,有客户端和服务器端,用户使用客户端输入用户名密码登录服务器端后进行控制。连接客户端和服务器的双方是网络虚拟终端(Network Virtual Terminal,NVT),服务器端和客户端都必须将它们的物理终端和NVT进行相互转换。

NVT是带有输入和输出的字符设备,用户击键产生的数据被发送到服务器进程,服务器端进程回送的响应输出到显示器上,NVT ASCII代表7比特的ASCII字符集,网间协议族都是用NVT ASCII,每个7比特都以8比特发送,最高位为0。

Telnet通信两个方向都是带内信令方式,字节0xff(十进制255)叫做IAC(interpret as command,意思是"作为命令来解释"),该字节后面一个字节才是命令字节。它的命令集如下:

TelnetCommand

# ssh

ssh为Secure Shell的缩写,由IETF的网络小组(Network Working Group)所制定,SSH为建立在应用层基础上的安全协议,专为远程登录会话和其他网络服务提供安全性的协议,利用SSH协议和有效防止远程管理过程中的信息泄露问题。

传统的网络服务程序如ftp,pop和telnet在本质上是不安全的,他们在网络上用明文传送口令和数据,容易被"中间人"(man in the middle)截取,也就是"中间人"冒充真正的服务器接收你传给服务器的数据,然后再冒充你把数据传给真正的服务器,这样服务器和你之间被"中间人"做手脚就会出现严重的问题,通过使用SSH,你可以把传输的数据进行加密,"中间人"的攻击就不可能实现了,而且能防止DNS欺骗和IP欺骗,使用SSH,它传输的数据是经过压缩的,可以加快传输速度,SSH有很功能使它既可以代替Telnet又可以为FTP,PoP甚至为PPP提供安全"通道"。

SSH有两种登陆方式:基于口令登陆和基于公钥登陆,下面来看下这两种方式:

# 基于口令登陆

SSHLogin

上面的过程使用非对称加密,看似安全,但还是有漏洞的,SSH不像https协议那样有证书中心(CA)公证服务器的公钥,如果有人一开始截获了登陆请求,那么他冒充远程主机还是可以使用中间人攻击。如下图:

SSHMiddleAttack

虽然发起这种中间攻击要比之前所说的中间攻击难点,但还是不安全的。避免这种攻击的关键在于如何确定服务器的公钥是服务器的,SSH中通常如果第一次登陆就会出现下面的提示:

The authenticity of host 'ssh-server.example.com (1.1.1.1)' can't be established. RSA key fingerprint is ff:34:78:98:34:34:32:f1:23:b3:34:67:a5:d8:92:3c. Are you sure you want to continue connecting (yes/no)?
1

这是什么意思呢?服务器将自己的公钥指纹发过来,然后客户端查找自己本地的known_hosts文件,如果没有找到就会发出上面的信息,说明该主机是没有连接过的,让SSH客户端自己来决定是否相信远程主机,如果回答yes,那么会提示下面信息:

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

这样便会将该服务器的公钥指纹存入known_hosts,然后下次连接该服务器时,从该文件中查找到服务器的公钥指纹,那么便会跳过警告部分。

为什么这里是用公钥的指纹,而不是公钥呢?应该是因为公钥过长的原因,RSA算法生成的公钥有1024位,但是对公钥hash生成128位的指纹,相比之下指纹更好比较。

由于known_hosts存在,当远程主机重装系统,它会重新生成公钥,再登陆远程主机时,由于known_hosts文件记录了原来的公钥,这就提示指纹验证错误,此时需要删除known_hosts文件。

当写一些自动化脚本自动登陆远程主机时,但是由于known_hosts机智必须要我们手动输入yes才能完成登陆,这会让自动化登陆无法完成,可以通过修改配置文件中的StrictHostKeyChecking ask改为StrictHostKeyChecking no。

# 公钥免密登陆

SSHNoPassLogin

注意上图中的会话密钥协商都是相互通信的,上面为了突出整体流程,放在了服务器端单向给客户端的里面。

# SSH实践操作

  1. 客户端生成密钥对。

    ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa -C "your_email@example.com"
    
    选项说明:
    -t 指定生成密钥类型,可以是rsa,dsa,ecdsa
    -P 指定密码,加密私钥
    -f 指定存放密钥文件位置,公钥文件默认和私钥同目录,公钥的文件以.pub为后缀
    -C 指定注释
    
    
    1
    2
    3
    4
    5
    6
    7
    8
  2. 将本地主机的密钥复制到远程主机的authorized_keys文件里

    ssh-copy-id -i ~/.ssh/id_rsa.pub user@host
    
    参数说明:
    -i 指定要拷贝本地的公钥位置
    
    1
    2
    3
    4

# 管理本地的私钥

ssh-agent是私钥管理工具,在下面情况时我们会考虑使用它:

  1. 使用不同的密钥连接不同的主机时需要手动指定对应密钥,此时ssh代理帮我们选择对应的密钥进行认证,不需手动指定密钥即可进行连接。
  2. 当私钥设置了密码,又虚频繁地使用私钥进行认证时,ssh代理可以帮我们免去重复的密码输入。

# 启动ssh代理

启动ssh代理有两种方式:

ssh-agent $SHELL
eval `ssh-agent`
1
2

第一种方式会在当前shell中启动一个默认shell,作为当前shell的子shell,如果退出当前shell时,会自动关闭ssh-agent。

第二种方式直接启动一个ssh-agent进程,当退出当前的shell时不会自动关闭,所以在退出当前bash之前,手动关闭这个进程,如果是在ssh-agent中,可以使用ssh-agent -k来关闭进程,如果已经退出了,那么只好使用kill命令了。

# 使用ssh代理

通过前面的命令启动ssh代理后,下面就要使用它了,在此之前,先来看下之前不使用代理时是怎样的,我们之前使用ssh命令默认是使用~/.ssh/id_rsa,如果我们生成的密钥不是这个名称,就需要在ssh连接时指定:

ssh -i /private-key-path root@1.1.1.1
1

而使用ssh-agent时:

ssh-agent bash
ssh-add /private/key/path
ssh root@1.1.1.1
1
2
3

这样连接不同的服务器时,或者重复连接同一个服务器时,便不用再根据服务器指定相应的密钥文件,更轻松快捷。

# 管理ssh-agent私钥

下面是ssh-agent的常用命令:

ssh-add -l 查看添加了哪些私钥
ssh-add -L 查看密钥的同时列出相应的公钥
ssh-add /private/key/path 删除指定私钥
ssh-add -D 一次性清空代理中的所有私钥
ssh-add -x 对代理加锁
ssh-add -X 对代理解锁
1
2
3
4
5
6

# 使用ssh-agent代理时的问题

虽然使用代理时一旦用ssh-add添加了密钥便不用再手动使用密钥来连接指定服务器,但是每次重启ssh-agent又需要重新ssh-add密钥,针对这样的情况,有以下几种解决方案:

# 使用SSHConfig

ssh目录下可以存放配置文件,比如~/.ssh/config,它的写法如下:

Host    别名
    HostName        主机名
    User            用户名
    PreferredAuthentications    用什么方式认证,有publickey,password,keyboard-interactive等
    IdentityFile    密钥文件的路径
1
2
3
4
5

这样它会根据配置文件中的别名直接去找相应的密钥文件,要连接的主机名等等。

# 脚本自动化

可以在bashrc中添加如下:

eval "$(ssh-agent -s)"
ssh-add /private/key/path
1
2

这样每次打开shell时便可以直接连接。

# KeePass和KeeAgent

KeePass是一个密码管理工具,它能记录下windows上的密码,邮箱密码,网站密码等等。而KeeAgent是KeePass的一个插件,能在KeePass的数据库中存储SSH密钥,用以被其他程序使用。

# 添加密钥到keychain或者凭据管理器

这个只适用于Mac OS,在Mac上可以使用下面命令将私钥添加到keychain中。

ssh-add -K [path/to/your/ssh-key]
1

# https

HTTPS全称为Hyper Text Transfer Protocol over Secure Socket Layer或Hypertext Transfer Protocol Secure。是安全版的HTTP,在HTTP下面加入SSL层,提供身份验证与加密通讯方法。这样HTTPS中的数据便不会像HTTP协议中那样明文传输,而是通过协商好的密钥,在发送端加密,在接收端解密。

# SSL

网络从底层向上分物理层,数据链路层,网络层,运输层和应用层,我们上面说的SSL(Secure Socket Layer)协议是在运输层和应用层之间,该协议由Netscape在1994年开发,最新版本是1996年的SSL 3.0,后来IETF在SSL基础上设计了运输层安全协议TLS(Transport Layer Security)。下面来看下为什么会需要这个安全协议呢?

# 应用场景

以某用户购物为例,在这个过程中他需要一些安全措施:

  1. 顾客所浏览的服务器是属于真正的厂商,服务器身份必须被鉴别。
  2. 顾客在购物过程中的,购物报文在传输过程中没有被篡改,需要确保报文不被修改,保证完整。
  3. 网上的入侵者不能截获信用卡号等敏感信息,信息传输过程中需要加密。

# SSL工作原理

SSL对客户端和服务器端之间传送的数据进行加密和鉴别,它在双发联络阶段(握手阶段)对将要使用的加密算法和双方共享的会话密钥进行协商,完成服务器和客户端之间的鉴别,在握手阶段结束后,所有传送的数据都使用在握手阶段商定的会话密钥。

SSLFlow

在第三阶段,浏览器收到服务器发来的证书,在自己的可信CA表中查找服务器的发行者CA是否在列表里,如果不在,后面的加密和鉴别连接就不能进行下去,如果在,浏览器就能使用CA相应的公钥对证书解密,得到服务器的公钥。

从上面流程再根据举的实例,我们发现SSL主要提供下面三个功能:

  1. SSL服务器鉴别,客户端从自己的可信CA列表查找是否是我们连接的服务器。
  2. 加密SSL会话,客户端和服务器所有交互数据都在发送端加密,在接收端解密,还提供了一种检测信息是否被攻击者篡改的机制。
  3. SSL客户鉴别,允许服务器证实客户端身份。

# ipsec

这个和https没有关系,只是和ssl有些关联,顺带说一下,前面提到过SSL是运输层和应用层之间的,而ipsec则是网络层上的安全协议,它指的是IP数据报中的所有数据都是加密的,网络层还提供源点鉴别(source authentication),当目的站收到IP数据报时,确信这是从该数据报的源IP地址的主机发出来的。