密码验证涉及安全和信任方面的许多假设。经过加密的SSH隧道和公开密钥验证则是确保密码在传输过程中不受到危及的两种常用方法。不过要是你目前所使用的这台计算机不可信任,那该如何是好?
这绝非小心谨慎的人担心的一种易受攻击的场景。在许多平常情况和常见地方,你可能不该使用系统密码,哪怕是通过一条安全隧道。例子包括如下:
◆酒店、图书馆或网吧里面的公共计算机;
◆同事被感染了病毒的计算机;
◆结对编程时所使用的共享工作站;
◆别人有可能看到你键入密码整个过程的任何地方。
上述这些例子有何共同之处呢?共同之处就是,实际上,你试图从一个不可信任的来源连接至可信任的目的地。这完全有悖于设计大多数验证系统的初衷。
就拿公开密钥验证来说。SSH公开密钥验证无疑绕过了远程主机上的密码提示,但是它仍需要你把你的私有密钥密码交给本地机器。此外,一旦密钥用你的密码来解密,本地系统就可以完全访问里面的敏感密钥内容。
幸好,已经有一种办法可以解决这个经常被人忽视的问题:那就是一次性密码。
SSH和一次性密码这对组合具有强大的功能:
◆SSH协议提供了跨整个网络对登录顺序进行加密的机制。
◆良好的SSH客户端让你可以在输入登录资料之前,先检查远程主机的公开密钥指纹。这可以防止未授权主机收集你的一次性密码。
◆一次性密码体系确保了密码无法被重复使用。所以,就算某个密码在传输过程中被获取,一旦你用该密码登录上去,这个密码对攻击者来说也是毫无价值的。
类似UNIX的系统有许多一次性密码解决方案。两种最有名的解决方案是S/KEY和OPIE(每件事情的一次性密码)。
由于OPIE最近从Debian和Ubuntu储存库中移除后,由德国计算机科学家Markus Kuhn开发的OTPW一次性密码体系提供了一种切实可行的替代方案。虽然OTPW并非直接替代OPIE,但它提供了同类功能,同时提供了S/KEY或OPIE所没有的一些令人关注的特性。#p#
从Debian和Ubuntu储存库移除OPIE
Debian在2011年年初开始移除与OPIE有关的程序包,起因是二进制程序的安全性、许可问题和缺少上游活动等方面引起了一番讨论。
如果你有兴趣想了解具体细节,可以参阅下列相关的Debian错误报告:
◆http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=511582
◆http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=622220
◆http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=622221
◆http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=622246
虽然截至截稿时,OPIE程序包仍然在目前的Debian稳定版本(代号为"Squeeze")中,Debian端口(debports)储存库里面也有一些非正式的平台端口,但OPIE并未出现在测试版或非稳定版中,它也似乎不太可能添加到下一个隐私版本中。
尤其是,OTPW提供了下列功能:
◆双因子验证,由"前缀密码"和一组自动生成的、一次性的后缀组成。就算后缀列表落到了坏人的手里,要是没有前缀密码,那也少不了蛮力攻击。
◆通过使用密码锁和三重质询(triplet challenge),防范某些竞争条件和中间人攻击。
◆支持共享式文件系统。由于OTPW对照存储在用户主目录里面的散列值列表来检查密码,所以一次性密码列表适用于挂载同一$HOME目录的所有系统。
接下来将介绍OTPW的安装和使用,并特别着重介绍与OpenSSH的集成。
程序包安装
想使用OTPW,你就需要两个二进制程序:otpw-bin和libpam-otpw。就Debian和Ubuntu而言,安装很容易,只要执行这个命令:
sudo apt-get install otpw-bin libpam-otpw
如果你的发行版并不提供OTPW,可以直接从开发者的主页下载源代码。源打包文件并不使用GNU自动配置(autoconf)工具,所以你需要按照开发者的操作说明,手动编译和安装二进制程序。
配置可插拔验证模块(PAM)
让系统为OTPW作好准备的下一步是,配置libpam-otpw。全面处理PAM不在本文的探讨范围之内,但我还是会在这里介绍几种最常见的使用场合。
改动PAM配置会你让无法使用你的工作站或服务器,所以一个好主意就是,让你现有的终端保持开放状态,直到你确信一切都正常工作。如果你可以访问控制台,手边要备一份可启动的发行版或挽救盘。想了解通过SSH测试PAM方面的更多信息,请参阅《借助SSH,测试一次性密码验证》这篇附文。#p#
借助SSH,测试一次性密码验证
如果你在为OTPW配置一个远程系统,应该在不关闭目前SSH连接的情况下测试PAM堆栈。切记:如果你在PAM配置上出现了错误,即便有权访问控制台,也可能无法验证--所以手边要备一份可启动的发行版,比如Knoppix、SystemRescueCD或Finnix,以防万一。与此同时,现有的登录仍然不受到影响,因为它们已经通过了验证。
为了正确测试PAM堆栈,你不能重新使用现有的SSH连接。大多数最新的发行版在默认情况下都支持SSH多路复用和持久性连接,所以要明确禁用这些选项以便测试。
此外,SSH在默认情况下偏爱公开密钥验证。所以,为了测试OTPW验证,公开密钥验证同样需要临时禁用。
下面这个调用能够正确测试SSH PAM堆栈,不需要对系统做任何改动:
read -p 'Hostname: ' REMOTE_HOST &&
SSH_AGENT_PID= SSH_AUTH_SOCK= \
ssh \
-o PreferredAuthentications=keyboard-interactive \
-o ControlPersist=no \
-o ControlPath=none \
"$REMOTE_HOST"
一旦你确信OTPW在正常工作,还应该验证你的其他验证机制(即SSH公开密钥和平常的系统密码)继续正常工作。
启用OTPW的最容易办法就是把它直接放在common-auth配置文件中pam_unix的前面:
# /etc/pam.d/common-auth
auth sufficient pam_otpw.so
session optional pam_otpw.so
auth sufficient pam_unix.so nullok_secure
auth required pam_deny.so
PAM库的顺序非常重要。如果把OTPW放在第一位,拥有~/.otpw文件的用户会首先得到一次性密码的提示,要是OTPW登录失败,允许退回到标准的系统密码。没有~/.otpw文件的用户只会看到标准的密码提示。
如果你更喜欢顺序倒过来,在退回到一次性密码之前提示系统密码,只要确保pam_deny放在最后一位:
# /etc/pam.d/common-auth
auth sufficient pam_unix.so nullok_secure
auth sufficient pam_otpw.so
session optional pam_otpw.so
auth required pam_deny.so
如果你忍不住想完全移除标准的系统密码,尤其是控制台登录时所用的系统密码,千万别这么做。在一些系统上,尤其是在拥有ecryptfs-encrypted主目录的Ubuntu系统上,要是没有标准的系统密码,想从OTPW故障恢复过来极其困难。
改动common-auth通常是在无外设服务器或只有控制台的系统上要做的正确操作。不过,提供X Window System的工作站或服务器给一次性密码体系带来了特殊的问题。
一些工具或应用程序无法与OTPW顺利协同运行,原因是它们无法把质询显示给用户。典型的症状通常是,出现了永远无法完成或似乎忽视用户输入的密码对话。在过去,gksu和GNOME显示管理器(GDM)结合OPIE使用时有这个问题。这种情况下,解决办法就是从common-auth移除OTPW,只把它添加在特定的服务中。
比如说,你可以把OTPW验证添加到SSH连接,同时针对控制台或GUI登录就使用标准的密码提示。只要三个简单步骤,就能做到这一点:
1. 从common-auth删除引用pam_otpw.so的任何行:
# /etc/pam.d/common-auth on Debian Squeeze
auth sufficient pam_unix.so nullok_secure
auth required pam_deny.so
2. 为PAM创建一个新的OTPW包含文件:
# /etc/pam.d/otpw
auth sufficient pam_otpw.so
session optional pam_otpw.so
3. 在/etc/pam.d/sshd中的common-auth前面添加OTPW:
# 其他内容……
# 启用OTPW验证。
@include otpw
# 标准的Un*x验证。
@include common-auth
# 更多内容……
SSH配置
除了配置PAM库外,OTPW在SSH守护程序的配置文件中还需要下列三个设置:
# /etc/ssh/sshd_config
UsePrivilegeSeparation yes
UsePAM yes
ChallengeResponseAuthentication yes
这些设置通常就在那里,但是可能被注释掉了或者被设置成"no",所以要作相应改动。下一步,改动配置文件后,重新装入SSH守护程序:
# 普通Linux
sudo /etc/init.d/ssh reload
# Debian 6.0.4+
sudo service ssh reload
# Ubuntu 11.04+
sudo reload ssh
#p#
生成OTPW密码
一旦OTPW PAM模块已被正确配置,只有拥有~/.otpw文件的用户在登录过程中会遭到一次性密码对话的质询。除了只与质询的有效应答匹配的单向散列列表外,该文件里面还含有关于其内容的一些元数据。
想创建该文件,或者重新为它装填新密码,可使用otpw-gen实用工具。默认情况下,它会创建280个密码后缀,格式经编排后可以排列到单面信函纸(8.5" x 11")上。由于~/.otpw文件里面只存储了单向散列,而不是密码本身,密码生成时,你必须捕获或打印该命令的标准输出。你无法事后获取密码列表,而是需要生成新的密码。
下面是你首次运行该命令,将输出发送到默认打印机所看到的样子:
$ otpw-gen | lpr
Generating random seed ...
If your paper password list is stolen, the thief
should not gain access to your account with this
information alone. Therefore, you need to memorize
and enter below a prefix password. You will have to
enter that each time directly before entering the
one-time password (on the same line).
When you log in, a 3-digit password number will be
displayed. It identifies the one-time password on
your list that you have to append to the prefix
password. If another login to your account is in
progress at the same time, several password numbers
may be shown and all corresponding passwords have to
be appended after the prefix password. Best generate
a new password list when you have used up half of
the old one.
Enter new prefix password:
Reenter prefix password:
Creating '~/.otpw'.
Generating new one-time passwords ...
生成新密码列表时,标准错误上出现的提示略有不同:
Overwrite existing password list '~/.otpw' (Y/n)?
Enter new prefix password:
Reenter prefix password:
Creating '~/.otpw'.
Generating new one-time passwords ...
第一个提示确保你没有不小心覆盖现有的密码列表;第二个提示要求你输入一个新密码。没有什么可以阻止你在每次调用时重复使用同一个前缀密码--随机种子使得重复散列不可能出现,但是最好的做法还是每当你重新生成密码列表时,使用一个新的前缀。
如果你想在远程主机上生成密码列表,但是打印到本地输出设备上,你可以通过SSH连接来做这一步,只要你信任你的本地主机:
read -p 'Hostname: ' &
注意使用stty以确保前缀密码没有回送到屏幕上。只要你的前缀密码仍然是安全的,使用不可信任的输出设备跟密码列表落到坏人手里一样没有关系。对经常出差的人来说,这常常是个实用的安全折中方案。
最后,想禁止对某个用户实行OTPW质询,只要从该用户的主目录删除.otpw文件即可。#p#
使用OTPW登录
一旦你手里有了密码列表,就可以准备为你的SSH连接使用一次性密码验证了。假设你没有将任何身份装入到SSH代理里面,对话应该类似这样:
$ ssh localhost
Password 015:
带数字的提示是OTPW质询。为了应答,请在你之前打印的密码清单上找到匹配的质询ID。接下来,输入前缀密码,后面跟着质询ID之后的字符串。
使用"foo"作为前缀密码,生成下列后缀列表。就算你使用同一个前缀密码,你的列表和后缀也会不一样。
OTPW list generated 2012-05-06 13:40 on localhost
000 SWvv JGk5 004 =qfF q2Mv 008 sb5P h94r 012 o5aH +/GD 016 8eLV VxuA
001 xPZR :ceV 005 B=bq =mHN 009 WBSR smty 013 QMZ% +bm8 017 vjFL K4VU
002 Sj%n 9xD3 006 RrNx sJXC 010 Xr6J F+Wv 014 j=LO CMmx 018 Km8c 8Q3K
003 s7g8 NE%v 007 sd=E MTqW 011 fNKT vo84 015 fWI% MB9e 019 z8ui %eQ3
!!! 切记:先输入前缀密码!!!
为了成功应答这个质询,在提示符处输入:
foo fWI% MB9e
空格是可选的;如果有空格,OTPW会忽视。
如果你正确应答了质询,登录就会继续下去。否则,你会得到使用标准系统登录的提示。这时候,你可以输入标准的系统密码,或者按返回键,再试一下OTPW。在系统指定的密码尝试次数(通常是三次)之后,登录失败后,你就会回到命令提示:
$ ssh localhost
Password 013:
Password:
Password 013:
Password:
Password 013:
Password:
Permission denied (publickey,password,keyboard-interactive).
为了防止同时登录,或者当SSH在OTPW验证过程中被中断,OTPW可能会锁定密码。密码被锁定后,你下一次登录尝试就会实行三重质询,要求一个前缀和三个后缀来应答:
$ ssh localhost
Password 004/011/005:
鉴于与前面一样的密码列表,将三重应答作为一行来输入,有无空格都没关系。下面显示了如何进行应答(注意:下面第一行只是一种信息帮助;你只要输入下面第二行,不带管道符):
prefix | suffix 004 | suffix 011 | suffix 005
foo | =qfF q2Mv | fNKT vo84 | B=bq =mHN
一旦你成功应答了三重质询,登录就会继续下去,~/.otpw.lock符号链接应该会被删除,你的下一个质询将再次是单一质询ID编号。
在一些情况下,无法正确清除密码锁。如果你继续得到三重质询的提示,可以手动删除锁定文件:
rm ~/.otpw.lock
如果用户拥有的经过加密的主目录在登录之前没有挂载上去,就需要采取另外几个步骤。#p#
OTPW和经过加密的主目录
ecryptfs文件系统给SSH和OTPW带来了特殊的问题。默认情况下,Ubuntu等发行版借助用户的系统密码,打开挂载经过加密的主目录所需的特殊口令短语(passphrase)。
这由pam_ecryptfs.so模块来处理,可以通过/etc/pam.d/common-auth及其他来添加该模块。如果你使用系统密码之外的任何密码来进行验证,该模块会提示你输入系统登录密码,以便挂载经过加密的主目录。
实际上,这意味着挂载远程主目录时,你的系统密码暴露在不可信任的终端上。这显然不是很理想。
避免这个问题的最佳办法就是,让控制台会话始终运行着。比如说,使用系统密码登录到控制台,然后锁定屏幕。只要你的控制台会话仍处于活动状态,主目录就仍然处于挂载状态。因而,你可以使用OTPW验证,无须对系统进行进一步的改动,也不会在登录或挂载过程中泄露系统密码。
然而,当控制台会话未运行时,如果你仍想能够使用OTPW用于SSH登录,又明白这么做对安全有何影响,下面教你如何来实现。
首先,你需要创建调用otpw-gen的包装脚本:
#!/bin/bash
set -e
otpw-gen "$@"
mv ~/.otpw /usr/local/lib/otpw/$LOGNAME/
ln -s /usr/local/lib/otpw/$LOGNAME/.otpw ~/
包装脚本应该放在你的路径中,并确保可以执行。
下一步,把otpw4ecryptfs.sh(下面所列)放在~/bin或/usr/local/sbin中:
#!/bin/bash
# 用途:
# 为拥有ecryptfs-mounted主目录的系统上所有用户启用OTPW。
set -e
# 暴露可能被ecryptfs隐藏的底层目录。
sudo mkdir -p /mnt/real_home
sudo mount -o bind /home /mnt/real_home
# 收集所有非系统用户。
users=$(
awk -F: '$1 != "nobody" \
&& $3 >= 1000 \
&& $3 < 65534 \
{print $1}' /etc/passwd
)
# 为每个非系统用户启用OTPW。
for user in $users; do
sudo mkdir -p /usr/local/lib/otpw/$user
sudo touch /usr/local/lib/otpw/$user/.otpw
sudo chown -R $user: /usr/local/lib/otpw/$user
sudo chmod 755 /mnt/real_home/$user
ln -sf /usr/local/lib/otpw/$user/.otpw \
/mnt/real_home/$user/
ln -sf /usr/local/lib/otpw/$user/.otpw \
/home/$user/
done < /etc/passwd
sudo umount /mnt/real_home
你运行该脚本后,它创建了可以由pam_otpw.so读取的 OTPW文件,即使用户的主目录卸载后也能读取。
请注意:该脚本为所有用户的主目录赋予了读取和执行权限,那样pam_otpw.so就能读取OTPW密码文件。这本身不是一种风险,但是依赖限制更大的目录权限的用户可能想之后立即加强其主目录中文件和文件夹的许可权限。
最后,所有用户都应该运行otpw-gen-wrapper.sh,装填和维护OTPW密码列表。始终使用包装脚本,而不是直接调用otpw-gen,否则密码生成会破坏正确操作所需的符合链接。#p#
检查剩余的密码
如果你的密码列表已用完,就再也无法使用OTPW来登录,除非生成新的列表。同样,如果你的密码列表不含有至少三个未使用的应答,~/.otpw.lock存在时,就无法使用OTPW来登录,因为没有足够的质询ID来发出三重质询。
此外,OTPW的安全性一方面来自剩余质询具有的随机性。使用三重质询特别会迅速用完你未使用的密码,所以一旦数量降至最小数以下,重新生成密码列表不失为是个好主意。
OTPW开发者建议:当仍未使用的原始密码不足一半时,应该重新生成密码列表,但不要定义质询的足够随机性所需要的最小密码数量。少量未使用的密码让你更容易遭到蛮力攻击,因为可供使用的质询比较少。
当未使用的密码降至所生成密码数量的一半时,pam_otpw.so PAM模块本该通知用户。不过,PAM会话功能在Debian或Ubuntu上似乎无法正常使用。此外,就算能使用,该模块也不会设立最小数,以确保质询有足够的随机性。
代码片段1所示的otwp-stats.sh脚本提供了这项缺失的功能。它还让你可以定义合理的未使用密码最小数,只要调整脚本顶部的MIN_PASSWORDS变量。
代码片段1:otwp-stats.sh
#!/bin/bash
# 30个未使用的密码似乎是个合理的最小数,可确保随机性,并防止出现三重质询用完。
# 这个数字可随意调整,以适合你的需要。
MIN_PASSWORDS=30
OTPW_LIST="$HOME/.otpw"
# 如果没有为该用户设置OTPW,就停止处理。
[ -f "$OTPW_LIST" ] || exit
# OTPW文件的最上面两行是元数据。
TOTAL_PASSWORDS=$((`wc -l < "$OTPW_LIST"` - 2))
# 带横杠的行表示已使用的密码。
USED_PASSWORDS=$(egrep '^-' "$OTPW_LIST" | wc -l)
# 剩余密码的数量是个估算值。
PASSWORDS_LEFT=$((TOTAL_PASSWORDS - USED_PASSWORDS))
cat << EOF
OTPW Password Statistics
------------------------
Passwords used: ${USED_PASSWORDS:=0}
Passwords left: $PASSWORDS_LEFT
EOF
if [ $PASSWORDS_LEFT -le $((TOTAL_PASSWORDS / 2)) ]
then
echo "It's time to generate new OTPW passwords."
elif [ $PASSWORDS_LEFT -le $MIN_PASSWORDS ]; then
echo "Remaining passwords at critical levels."
echo "It's time to generate new OTPW passwords."
fi
把otwp-stats.sh添加到你的~/.profile(或其他外壳启动脚本),以便在登录时提供反馈:
# 通过SSH登录时才运行脚本。
[ -n "$SSH_CONNECTION" ] && ~/bin/otpw-stats.sh
结论
OTPW提供了实现一次性密码的机制,与OPIE和S/KEY相比毫不逊色。它很容易与大多数Linux系统上的SSH集成,而且仍可以在带经过加密的主目录的Ubuntu系统上使用。
原文地址:http://www.linuxjournal.com/content/configuring-one-time-password-authentication-otpw?page=0,0