查看源代码 强化
简介
Erlang/OTP SSH 应用程序旨在用作其他应用程序的库。
使用此库的不同应用程序可能有非常不同的要求。一个应用程序可能运行在高性能服务器上,而另一个应用程序则运行在 CPU 容量非常有限的小型设备上。例如,第一个应用程序可能接受多个用户同时登录,而第二个应用程序则希望将其限制为只有一个。
这个简单的例子表明,不可能在加固选项以及其他满足各种需求的选项上,使用默认值来交付 SSH 应用程序。
本指南的目的是讨论可用的不同强化选项,为读者提供指导。 一般配置在SSH 中的配置章节中描述。
对 DoS 攻击的弹性
以下内容适用于守护进程(服务器)。
DoS(拒绝服务)攻击很难在节点级别进行防御。 这里需要防火墙和其他手段,但这不在本指南的范围内。 但是,可以在 SSH 服务器的配置中采取一些措施来提高弹性。 要使用的选项是
计数器和并行性
max_sessions - 此守护进程在任何时候接受的最大同时会话数。 这包括正在授权的会话。 默认情况下,允许无限数量的同时会话。 如果服务器的容量较低或需要容量余量,则可以很好地设置此项。
max_channels - 每个连接接受的最大通道数。 默认值为无限制。
parallel_login - 如果设置为 false(默认值),则一次只处理一个登录。 如果设置为 true,则同时登录尝试的次数受max_sessions选项的值限制。
超时
hello_timeout - 如果客户端在 TCP 连接建立后,在此时间内(以毫秒为单位)未能发送第一个 ssh 消息,则连接将关闭。 默认值为 30 秒。 这实际上是一个宽裕的时间,因此可以缩短,以使守护进程更不容易受到 DoS 攻击。
negotiation_timeout - 从 TCP 连接建立开始计算的身份验证协商的最大时间(以毫秒为单位)。 如果客户端在此时间内未能登录,则连接将关闭。 默认值为 2 分钟。 这是一个相当长的时间,但如果客户端应该很快(例如,如果它是程序登录),则可以缩短。
idle_time - 设置在关闭最后一个通道后,当没有通道剩余时,连接的超时时间。 它默认为无限。
max_initial_idle_time - 设置在连接上未打开任何通道时将过期的连接超时。 超时在身份验证阶段完成后开始。 它默认为无限。
下图阐明了何时开始超时以及何时触发超时
在 SSH 客户端中验证远程守护进程(服务器)
每个 SSH 服务器都向客户端呈现一个公钥 - 主机密钥 - 同时将相应的私钥保持在相对安全的私密性中。
客户端检查呈现公钥的主机是否也拥有密钥对的私钥。 该检查是 SSH 协议的一部分。
但是,客户端如何知道主机真正是它试图连接的主机,而不是使用自己有效的密钥对冒充预期主机的恶意主机?默认密钥处理插件 ssh_file
提供了两种选择。 这些选择是
预先存储主机密钥 - * 对于默认处理程序 ssh_file,将有效的主机密钥存储在文件
known_hosts
中,并将选项silently_accept_hosts设置为false
。- 或者,使用SSH 客户端密钥 API编写专门的密钥处理程序,以其他方式访问预共享密钥。
预先存储主机密钥的“指纹”(校验和) - * silently_accept_hosts
在守护进程(服务器)中验证远程客户端
密码检查 - 默认密码检查使用 SSH 守护进程中的user_passwords选项中的列表。 可以使用pwdfun插件替换它。 Arity 4 变体 (
pwdfun_4()
) 也可用于在密码检查失败的尝试后引入延迟。 这是一个简单的 pwdfun 示例fun(User, Password, _PeerAddress, State) -> case lists:member({User,Password}, my_user_pwds()) of true -> {true, undefined}; % Reset delay time false when State == undefined -> timer:sleep(1000), {false, 2000}; % Next delay is 2000 ms false when is_integer(State) -> timer:sleep(State), {false, 2*State} % Double the delay for each failure end end.
如果使用公钥登录,通常不会检查用户名。 可以通过将选项
pk_check_user
设置为true
来启用此功能。 在这种情况下,pwdfun 将在密码参数中获取原子pubkey
。
加密领域的强化
算法选择
SSH 安全性的基石之一是密码学。密码分析的发展速度很快,昨天的安全算法在今天可能是不安全的。因此,某些算法默认情况下不再启用,并且该组会随着时间的推移而增长。 有关受支持和禁用的算法列表,请参见SSH (App)。 在《用户指南》中,在 SSH 中配置算法一章描述了启用或禁用算法的选项 - preferred_algorithms 和 modify_algorithms。
重新密钥
在 SSH 连接的设置中,客户端和服务器通过协作生成一个秘密密码密钥。 保守这个密钥的秘密对于保持通信的秘密至关重要。 随着时间的流逝和加密消息的交换,窃听者猜出密钥的可能性越来越大。
因此,SSH 协议定义了一种特殊的操作 - 密钥重新协商 或 重新密钥。 任何一方(客户端或服务器)都可以启动重新密钥,结果是生成新的密码密钥。 结果是,窃听者必须重新开始其邪恶和肮脏的工艺。
有关描述,请参见选项 rekey_limit。
SSH 协议的强化 - 守护进程和客户端
禁用守护进程中的 shell 和 exec
守护进程具有两种服务,用于代表远程客户端评估任务。 exec 服务器端服务接受客户端提供的字符串,对其进行评估并返回结果。 shell 函数使客户端能够在 shell 主机中打开一个 shell。
当不需要这些服务时,可以(并且应该)禁用它们。 选项 exec 和 shell 默认情况下启用,但如果不需要,可以将其设置为 disabled
。 相同的选项还可以为从客户端传递到服务器的字符串安装处理程序。
ID 字符串
减少入侵风险的一种方法是不传递入侵者连接的软件和版本。 这限制了入侵者利用通过阅读公共代码学到的已知缺陷或特性的风险。
每个 SSH 客户端或守护进程都会使用品牌和版本向彼此展示自己。 这可能看起来像
SSH-2.0-Erlang/4.10
或
SSH-2.0-OpenSSH_7.6p1 Ubuntu-4ubuntu0.3
可以使用选项 id_string 更改此品牌和版本。 我们使用该选项启动守护进程
ssh:daemon(1234, [{id_string,"hi there"}, ... ]).
守护进程将把自己展示为
SSH-2.0-hi there
可以将字符串替换为每次连接尝试随机生成的字符串。 有关 id_string 的详细信息,请参阅参考手册。
客户端连接选项
客户端可以使用选项 connect_timeout 限制初始 tcp 连接建立的时间。 时间以毫秒为单位,初始值为无限。
可以使用在建立 SSH 会话的调用中,例如 ssh:connect/3
, 使用参数 NegotiationTimeout
来限制协商(会话建立时间)时间。