关于后端:SSH中的安全-从SSH协议看身份验证底层原理

40次阅读

共计 6240 个字符,预计需要花费 16 分钟才能阅读完成。

原文链接:SSH 中的平安 | 从 SSH 协定看身份验证底层原理

推广:NextSSH 简洁直观的 SSH 客户端 https://codemutex.com/

前言

前置关键词:SSH 客户端 / 服务器,Linux/Unix 零碎的用户账户,TCP/IP,Socket。

本文撰于 2022 年 9 月,若相干内容有更新请依照援用链接内的内容为准。

本文介绍了 SSH 协定在验证用户身份过程中的实现细节,想帮忙读者更加深刻的理解 SSH 客户端与服务器的工作过程。也因而,本文可能不适用于领导 SSH 服务器或客户端的配置。

本文在撰写中参考了下列内容。

  • SSH 架构 RFC 4251 The Secure Shell (SSH) Protocol Architecture
  • SSH 传输层协定 RFC 4253 The Secure Shell (SSH) Transport Layer Protocol
  • SSH 身份验证协定 RFC 4252 The Secure Shell (SSH) Authentication Protocol
  • SSH 连贯协定 RFC 4254 The Secure Shell (SSH) Connection Protocol
  • SSH 交互式身份验证 RFC 4256 Generic Message Exchange Authentication for the Secure Shell Protocol (SSH)

SSH 协定的构造

建设一个 SSH 连贯,将会通过上面几个过程。

  • (明文通信) 建设 TCP 连贯
  • (明文通信) 协商 SSH 协定版本 (实质上是互相发送蕴含版本号的字符串)

    • 服务器将本人的 SSH 协定版本发送到客户端,格局为:SSH-protoversion(版本号)-softwareversion(自定义) SP(空格一个, 可选) comments(正文, 可选) CR(回车) LF(换行)
    • 客户端将本人的 SSH 协定版本发送到服务器,格局为:SSH-protoversion(版本号)-softwareversion(自定义) SP(空格一个, 可选) comments(正文, 可选) CR(回车符) LF(换行符)
  • (明文通信) 协商密钥

    • 服务器发送公钥
    • 客户端和服务器互相发送反对的相干加密算法列表、MAC 算法列表
    • 应用 D-H 算法 生成尔后通信中应用的对称加密密钥(会话密钥)
  • (密文通信) 身份认证
  • (密文通信) 正式进入利用 (如关上 Shell、SFTP、端口转发)

从下面的流程中能够看出,身份验证过程实际上产生在 SSH 连贯建设之后,尔后客户端与服务器之间传输的明码、密钥、信息都曾经受到 SSH 协定生成的「会话密钥」加密。

对于「平安」

SSH 的全称是「Secure Shell」,平安 Shell。其中的「平安」不是指应用 SSH 就能进入一个相对的平安世界,而是蕴含了传输平安和身份平安。

假如,我正在打电话给身在公司的敌人,询问一些机要信息。此时攻击者就能够剪断我屋外的电话线,别离接上两个电话听筒对听筒放在一起。我和我的敌人都不会留神到问题,而攻击者能够从中得悉咱们之间传输的信息。这是网络中存在的中间人攻打。

SSH 防止了这个问题,客户端与服务器之间的数据传输通过了下面的过程而加密(SSH 传输层协定 RFC 4253)。这个加密是无对于用户的账号密码的,在事先就实现。无论是网络中的交换机还是跳板机都无奈间接获取加密前的明文。SSH 爱护了传输时的平安。

SSH 也同样提供了验证用户身份的形式,客户端能够将用户的密码发送至服务器,以此让服务器确认用户的身份,只容许被受权的拜访连贯到服务器。

SSH 的平安不是相对的。比方攻击者拿起剪断的电话线(中间人攻打),他仍旧能够通过客户端与服务器之间的通信内容揣测出这是 SSH 连贯。SSH 协定并不能保障连贯不被侦测到特色。

身份验证形式

明码(Password)

应用明码登录是罕用且较为便捷的认证形式。在配置文件 /etc/ssh/sshd_config 中增加 PasswordAuthentication yes 以开启明码登录。

通常也认为应用明码登录是一种较为软弱的认证形式。这不是说应用明码会造成传输时的不平安,而是对明码自身保留的担心。个别的用户明码都是十位或数十位字符,可能被记录与纸上或记事本中。即便不是如此,有意义的明码也容易受到社会工程攻打而泄露。

/etc/ssh/sshd_config 文件中有时会设置 PermitRootLogin prohibit-password,要求系统管理用户 root 不得应用明码登录。

依据 RFC 中的形容,用户认证过程是在连贯握手之后的。此时客户端与服务器之间曾经建设起了加密的连贯。单方都会应用握手时替换好的密钥加密所有传输内容。后文中的 SSH 数据款式都是被加密传输的。

登录时,登录申请由客户端发动。一个名称为 SSH_MSG_USERAUTH_REQUEST 的音讯从客户端收回,蕴含了登录的用户名与明码。

C:      byte      SSH_MSG_USERAUTH_REQUEST
C:      string    user name
C:      string    service name
C:      string    "password"
C:      boolean   FALSE
C:      string    plaintext password in ISO-10646 UTF-8 encoding [RFC3629]

S:      byte      SSH_MSG_USERAUTH_SUCCESS

若登录认证失败,服务器将回复 SSH_MSG_USERAUTH_FAILURE

基本上,这样的音讯往返就实现了平时常见的登录过程。

除此之外,在 RFC 规范中还有一个用于服务器响应明码登录申请的音讯。

通常,服务器会胜利或失败地响应此音讯。然而,如果明码已过期,服务器应通过 SSH_MSG_USERAUTH_PASSWD_CHANGEREQ 响应来批示这一点。在任何状况下,服务器都不得容许应用过期明码进行身份验证。

后文介绍的 keyboard-interactive 登录形式也能够做出明码过期提醒。

公钥私钥(Publickey)

在 RFC 规范中,公钥验证形式是惟一必须实现的验证形式(The only REQUIRED authentication)。所有实现都必须(MUST, RFC2119)反对这种办法。在 /etc/ssh/sshd_config 文件中应用 PubkeyAuthentication yes 开启公钥验证形式。

此验证形式须要用户先筹备一个非对称加密的密钥对,将公钥保留至 SSH 服务器的 ~/.ssh/authorized_key 文件中。客户端登录时,在本地用私钥加密某个信息,并将后果发送给服务器,服务器将通过公钥验证收到的密文是否来自指定的用户。

私钥通常以加密的模式存储在客户主机上,用户必须在生成签名之前提供一个口令(passphrase)。即便不是这样,签名操作也波及一些低廉的计算。为了防止不必要的解决和用户互动,提供以下信息来查问应用 “ 公钥 “ 办法的认证是否能够承受。

C:      byte      SSH_MSG_USERAUTH_REQUEST
C:      string    user name in ISO-10646 UTF-8 encoding [RFC3629]
C:      string    service name in US-ASCII
C:      string    "publickey"
C:      boolean   FALSE
C:      string    public key algorithm name
C:      string    public key blob

任何公钥算法都能够被提供给认证应用,如果申请中的算法不被服务器反对,它必须间接回绝该申请。

服务器必须以 SSH_MSG_USERAUTH_FAILURE 或以下形式回应该音讯。

S:      byte      SSH_MSG_USERAUTH_PK_OK
S:      string    public key algorithm name from the request
S:      string    public key blob from the request

之后,客户端会应用私钥加密一个音讯(音讯的形成形式参见 RFC 4252),将后果发送给服务器。上面音讯中的 signature 即为加密运算后的内容。

C:      byte      SSH_MSG_USERAUTH_REQUEST
C:      string    user name
C:      string    service name
C:      string    "publickey"
C:      boolean   TRUE
C:      string    public key algorithm name
C:      string    public key to be used for authentication
C:      string    signature

应用公钥形式登录的长处是,密钥对根本不可能被写在纸上(密钥是很长的随机文本,社会工程攻打中只能通过更艰难的间接形式窃取这么长的内容);在网络上传输、保留的通常是密钥对的公钥文件,而非私钥文件。

更加不言而喻的益处是,私钥文件是保留在客户端的计算机上的。应用 SSH 命令时就无需再重复输出明码。因而网络上很多教程应用此形式作为免明码登录的形式。与此同时,因为须要事后将公钥放在服务器上(通常是通过网络上传),其也的确不便于配置。

交互式(keyboard-interactive)

在 RFC 文档中,这个验证形式被视作是前述计划的一种扩大。容许 SSH 客户端和服务器在获取身份验证信息时进行一些交互。如要启用此形式需在 /etc/ssh/sshd_config 文件中增加 ChallengeResponseAuthentication yes。通常状况下,这个验证模式会与零碎内的 PAM 模块一起启用。以此来反对谷歌验证器(多因素验证),或其余外部身份校验模块。

应用交互式验证能够容许用户输出更多的信息,取得更多的提醒内容。

从 RFC 中来看,此验证模式也是从客户端发动身份验证申请开始。

C:      byte      SSH_MSG_USERAUTH_REQUEST
C:      string    user name (ISO-10646 UTF-8, as defined in [RFC-3629])
C:      string    service name (US-ASCII)
C:      string    "keyboard-interactive" (US-ASCII)
C:      string    language tag (as defined in [RFC-3066])
C:      string    submethods (ISO-10646 UTF-8)

当服务器得悉客户端筹备应用 keyboard-interactive 为验证形式后,服务器会向客户端收回用户信息申请。在这个来自服务器的申请中,服务器将提供提醒文本(instruction, prompt)并且为每一个字段(或者称为询问)标记一个序号(num-prompts)。

S:      byte      SSH_MSG_USERAUTH_INFO_REQUEST
S:      string    name (ISO-10646 UTF-8)
S:      string    instruction (ISO-10646 UTF-8)
S:      string    language tag (as defined in [RFC-3066])
S:      int       num-prompts
S:      string    prompt[1] (ISO-10646 UTF-8)
S:      boolean   echo[1]
S:      ...
S:      string    prompt[num-prompts] (ISO-10646 UTF-8)
S:      boolean   echo[num-prompts]

收到来自 SSH 服务器的申请后,客户端即可开始向用户展现界面获取信息。在应用 ssh 命令时,通常会在终端内期待用户输出,具备 GUI 的软件将会展现提醒界面。

当用户实现输出后,客户端即可发送用户信息响应。

C:      byte      SSH_MSG_USERAUTH_INFO_RESPONSE
C:      int       num-responses
C:      string    response[1] (ISO-10646 UTF-8)
C:      ...
C:      string    response[num-responses] (ISO-10646 UTF-8)

这样的过程(服务器申请 - 用户输出 - 客户端响应)可能会反复屡次。例如用户输出的明码谬误,或者服务器根据状况申请了更多的信息。

上面是来自 RFC 文档中的,客户端和服务器之间的两个替换例子。第一个例子是用需解决的 Token 进行验证的例子。这是一种其余认证办法无奈实现的形式。

C:      byte      SSH_MSG_USERAUTH_REQUEST
C:      string    "user23"
C:      string    "ssh-userauth"
C:      string    "keyboard-interactive"
C:      string    ""C:      string""

S:      byte      SSH_MSG_USERAUTH_INFO_REQUEST
S:      string    "CRYPTOCard Authentication"
S:      string    "The challenge is’14315716’"
S:      string    "en-US"
S:      int       1
S:      string    "Response:"
S:      boolean   TRUE

[Client prompts user for password]

C:      byte      SSH_MSG_USERAUTH_INFO_RESPONSE
C:      int       1
C:      string    "6d757575"
S:      byte      SSH_MSG_USERAUTH_SUCCESS

第二个例子是一个规范的明码认证。但在此例子中,用户的明码曾经过期。

C:      byte      SSH_MSG_USERAUTH_REQUEST
C:      string    "user23"
C:      string    "ssh-userauth"
C:      string    "keyboard-interactive"
C:      string    "en-US"
C:      string    ""

S:      byte      SSH_MSG_USERAUTH_INFO_REQUEST
S:      string    "Password Authentication"
S:      string    ""S:      string"en-US"
S:      int       1
S:      string    "Password:"
S:      boolean   FALSE

[Client prompts user for password]

C:      byte      SSH_MSG_USERAUTH_INFO_RESPONSE
C:      int       1
C:      string    "password"

S:      byte      SSH_MSG_USERAUTH_INFO_REQUEST
S:      string    "Password Expired"
S:      string    "Your password has expired."
S:      string    "en-US"
S:      int       2
S:      string    "Enter new password:"
S:      boolean   FALSE
S:      string    "Enter it again:"
S:      boolean   FALSE

[Client prompts user for new password]

C:      byte      SSH_MSG_USERAUTH_INFO_RESPONSE
C:      int       2
C:      string    "newpass"
C:      string    "newpass"

S:      byte      SSH_MSG_USERAUTH_INFO_REQUEST
S:      string    "Password changed"
S:      string    "Password successfully changed for user23."
S:      string    "en-US"
S:      int       0

[Client displays message to user]

C:      byte      SSH_MSG_USERAUTH_INFO_RESPONSE
C:      int       0

S:      byte      SSH_MSG_USERAUTH_SUCCESS

本文转载不得删改,应全文转载并保留原文出处链接。

正文完
 0