ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Kerberos认证协议详解:票据机制、KDC与实战避坑指南

Kerberos认证协议详解:票据机制、KDC与实战避坑指南 做后端开发或者运维的朋友迟早会遇到一个叫 Kerberos 的东西。它不是某个框架的名字而是一整套在不可信网络中解决“如何安全证明我是我”的认证机制。Windows 域账号登录、大数据集群里 Hadoop/Spark 的认证、NFS 文件系统的安全挂载背后都是它在默默工作。这篇我就把 Kerberos 从里到外拆开讲清楚它的工作流程、核心设计思路以及在实操中你会踩到的那些坑。1. 先搞清楚 Kerberos 到底解决什么问题1.1 为什么不能靠“用户名密码到处传”在继续往下讲之前我们需要先建立一个共识传统的账号密码认证方式在分布式环境下有严重的缺陷。想象一个场景你在办公电脑上要访问一台文件服务器。如果认证方式是“把用户名和密码发给文件服务器”那意味着密码需要在网络上传输一次存在被截获的风险。文件服务器需要保存你的密码或者密码的散列值一旦服务器被攻破所有用户的凭证全部泄露。如果你要访问 10 个服务就得把密码发给 10 台服务器每台服务器都变成你的密码保管者暴露面被无限放大。这个问题在早期的 NFS、HTTP Basic Auth 里都真实存在过。而 Kerberos 的设计目标非常明确密码只提交一次而且永远不出现在网络上。它把“证明你是谁”这件事变成了一张“票据”的流转过程。1.2 Kerberos 的核心思想凭据代替密码Kerberos 的核心可以总结成一句话你想访问某个服务不需要把你的秘密告诉那个服务而是由一个大家都信任的第三方KDC给你开一张“介绍信”。这个介绍信就是票据Ticket。目标服务只认票据不认密码。这个思路在现实生活里非常普遍。你出差住酒店前台不会让你把身份证原件留在酒店而是核对身份后给你一张房卡。房卡本身不代表你的身份但它能打开你的房门。门锁只认房卡不认你的身份证。Kerberos 里的 KDCKey Distribution Center密钥分发中心就是那个前台Ticket 就是那张房卡而目标服务就是那个只认房卡的门锁。这种方式有几个立竿见影的好处你的密码永远不会在网络中明文传输目标服务不需要保存任何用户凭证被攻破的价值大大降低即使某张票据泄露了由于票据有有效期攻击者也只能在有限时间窗口内滥用。2. 核心概念与角色拆解2.1 三个主角客户端、KDC、目标服务Kerberos 的流程里一共有三方参与者角色英文缩写职责客户端Client发起访问请求的用户/服务进程认证服务器ASAuthentication ServerKDC 的一部分负责验证用户身份并发放入场券TGT票据授予服务器TGSTicket-Granting ServerKDC 的另一部分负责为特定服务签发服务票据目标服务Server实际提供资源的服务如文件服务、打印服务、应用服务这里有个容易混淆的点KDC 并不单指一台服务器。在微软的 Active Directory 里域控DC集成了 AS 和 TGS 两个角色。在 MIT Kerberos 或 Heimdal 实现里KDC 进程同时监听 88 端口处理两类请求。从协议层面看你可以把 AS 和 TGS 看成 KDC 的两个逻辑子模块。AS 和 TGS 为什么必须分开这是 Kerberos 设计里最精妙的地方。如果只有一个认证服务直接给用户签发访问各种服务的票据那这个服务就需要知道所有用户的密钥还要管理所有服务间的权限关系耦合度太高。分拆之后AS 只负责一件事核验用户身份并给一个“万能入场券”TGT后续访问任何服务都拿 TGT 去找 TGS 换具体服务的门票。这样 AS 的压力被大幅分流而且 TGT 可以缓存复用避免用户每次访问服务都要重新输密码。2.2 两类关键票据TGT 与 Service Ticket整个 Kerberos 流程中会出现两种票据它们经常把人绕晕先在这里做一个明确的对比TGTTicket-Granting Ticket票据授予票据你通过身份验证后从 AS 拿到的“万能入场券”。它本身不代表任何具体服务的访问权限只代表“你确实是这个用户”。TGT 的有效期通常是 8 到 10 小时相当于你每天早上登录域时刷脸换来的工作证。在后续会话中你访问任何域内资源都靠它去换取具体门票。ST/Service Ticket服务票据你用 TGT 向 TGS 申请、针对某个具体服务签发的“门票”。票据里写明了目标服务的标识SPN、你的身份信息、时间戳和有效期。目标服务只认这个票据验证通过后允许你访问。用生活化的类比TGT 是你在公司楼下的门禁卡证明你是公司员工Service Ticket 是进入某个特定会议室的门票。门禁卡能让你在公司内部自由走动但每个会议室还需要单独刷门票。TGT 的价值在于一次认证多次申请服务票据的价值在于“一票一服务”互不通用。2.3 密钥体系所有安全性都建立在对称加密之上Kerberos 的安全性基石是对称密钥体系。这里有几个关键的“密钥”需要区分清楚用户长期密钥由用户密码通过特定算法通常是 AES-256-CTS-HMAC-SHA1-96 或 RC4-HMAC派生而来。KDC 的数据库中保存着一份密钥副本客户端在登录时输入密码后本地计算出同样的密钥。密码永远不会在网络中传输。KDC 长期密钥KDC 自身的密钥由 krbtgt 账户持有。TGT 用这个密钥加密从而保证客户端无法伪造或篡改 TGTKDC 自己也不会把 TGT 内容放在客户端内存里让用户随意修改。会话密钥Session Key每次认证过程中临时生成的对称密钥只在一次会话内有效。这个密钥会通过用户的长期密钥加密后发给客户端同时被嵌入票据中。目标服务端和客户端之间后续的通信加密就用这个会话密钥。可以这样理解这三层密钥用户长期密钥是你的身份证KDC 用它确认你的身份KDC 长期密钥是 KDC 的私章TGT 上盖了这枚章别人就改不了会话密钥是你这次出差临时用的对讲频道事情办完就作废。正是这套密钥体系保证了即使网络中的某段通信被截获攻击者也无法从中还原出你的密码。3. 认证流程逐步拆解六次交互搞定一次访问Kerberos 的一次完整认证流程从用户登录到访问目标服务一共经历六次网络交互。这里我用时序的方式逐步拆解每一步都告诉你“发什么、收到什么、为什么这么设计”。3.1 第一步客户端向 AS 请求入场券AS-REQ当你在终端输入kinit并输入密码或者 Windows 域用户登录系统时客户端软件已经在本地计算出了你的长期密钥。随后客户端向 AS 发送一个AS-REQ请求。这个请求包含你的用户名Principal 名称。需要获取的 TGT 的服务标识通常是krbtgt/域名。一个客户端生成的随机数Nonce。客户端支持哪些加密算法Encryption Types。请求发出后AS 先在数据库中查找这个用户是否存在。如果存在取出该用户的长期密钥副本。此时 AS 还不能马上发 TGT。它要做一件非常重要的事验证请求方的确是密码持有者。这就涉及到下一节要讲的“预认证机制”。简单来说客户端会在请求里附加一个用自己长期密钥加密的时间戳AS 能用数据库中的密钥副本解出来就说明对方确实知道正确的密码。3.2 第二步AS 签发 TGT 和会话密钥AS-REP验证通过后AS 返回AS-REP这个返回包里有两块内容TGT 本体包含用户的身份信息、域名、时间戳、TGT 有效期允许转发的标志Forwardable等用 KDC 的长期密钥加密。因为加密密钥是 KDC 的客户端无法查看或篡改 TGT 的内容它只能原样保存当作一个“黑盒凭证”。TGT 相关的会话密钥这个密钥是 AS 临时生成的用来保护客户端和 KDCTGS之间的后续通信。它会用用户的长期密钥加密后发给客户端。客户端拿到后用自己的长期密钥解密得到会话密钥 Sk1。这一步是整个 Kerberos 流程里唯一一次需要验证用户密码的地方。做完这一步客户端就有了一个“可以进出 KDC 的入场券”TGT以及一把“和 KDC 私聊用的密钥”Sk1。用户的密码使命至此结束后续所有通信都只用 TGT 和会话密钥。3.3 第三步客户端向 TGS 申请服务票据TGS-REQ现在客户端想要访问一个具体的服务比如文件服务器cifs/fileserver.example.com。它需要向 TGS 发送TGS-REQ请求。请求内容包括目标服务的 SPNService Principal Name服务主体名称例如cifs/fileserver.example.com。第一步拿到的 TGT。一个用 Sk1 加密的Authenticator认证子里面包含客户端用户名和时间戳。Authenticator 是一个非常关键的防伪造组件。它和 TGT 的区别在于TGT 是通用凭证可以被重复使用Authenticator 是一次性的证明证明“我现在正拿着这张 TGT 的人是我”。TGT 本身没有绑定某个具体客户端 IP现代实现还有相关限制任何人窃取到 TGT 后理论上都可以冒充。Authenticator 用会话密钥加密而会话密钥只有真正的客户端和 KDC 知道所以攻击者即使拿到了 TGT也无法构造出合法的 Authenticator。3.4 第四步TGS 返回服务票据TGS-REPTGS 收到请求后先做这么几件事用 KDC 的长期密钥解密 TGT。检查 TGT 是否过期、是否在黑名单里。用 TGT 里保存的 Sk1 会话密钥解密 Authenticator核验时间戳是否在允许的时间偏差窗口内默认 5 分钟。确认无误后为客户端生成一张新的服务票据 ST。这个 ST 里包含用户的身份信息、目标服务名、时间戳、有效期等用目标服务的长期密钥加密。同时生成一个新的会话密钥 Sk2用于客户端和目标服务之间后续通信。Sk2 会被打包进 ST 中用服务密钥加密同时再复制一份用 Sk1 加密后放进响应里。TGS 返回的TGS-REP因此也包含两块用目标服务密钥加密的 ST以及用 Sk1 加密的 Sk2。客户端收到后用 Sk1 解密得到 Sk2但无法解开 ST因为 ST 是用服务端密钥加密的。ST 就像一个密码信封只有目标服务能打开。3.5 第五步客户端向目标服务提交票据AP-REQ客户端现在同时持有两样东西ST 和 Sk2。它向目标服务发送AP-REQ请求包含ST 本身。一个新的 Authenticator这次不是用 Sk1 加密了而是用 Sk2 加密。这个 Authenticator 包含客户端名称和时间戳用来向目标服务证明“我就是票据中写的那个人”。注意此时客户端再次构造了 Authenticator而不是简单地把票据一扔。票据可能被重放攻击——攻击者窃听了 AP-REQ把同一个 ST 再发给服务端一次就完成了冒充。Authenticator 因为带有时间戳且用 Sk2 加密让服务端能确认请求是实时的、来自真正持有 Sk2 的人。3.6 第六步目标服务验证票据并建立会话AP-REP目标服务收到 AP-REQ 后用自己的长期密钥解密 ST。从 ST 中取出 Sk2 会话密钥。用 Sk2 解密 Authenticator验证用户名和时间戳。检查 ST 是否过期、服务名是否匹配自己。验证都通过后服务端原则上可以开始提供数据了。如果是双向认证服务端还可以用 Sk2 加密一个时间戳返回给客户端AP-REP证明自己确实是持有该服务密钥的那个服务防止中间人伪装服务。到这里整个认证流程完成。客户端和服务端之间后续的数据传输可以使用 Sk2 或者在此基础上衍生出的加密密钥进行保护。整个流程看似复杂但实际上客户端和用户是感知不到的——你只是登录了一次剩下的全部自动完成。4. 容易被忽略的增强机制与关键细节4.1 预认证Pre-authentication堵住离线字典攻击在 Kerberos 早期的实现里AS-REQ 发送后AS 直接返回用用户长期密钥加密的会话密钥。这就带来一个漏洞攻击者不需要在真实用户登录时截获请求只需要抓到一个 AS-REP就可以在本地暴力破解用户密码。因为 AS-REP 是用用户密码派生的密钥加密的攻击者可以无限尝试密码组合离线比对解密结果。现代 Kerberos 强制启用预认证机制。客户端在发送 AS-REQ 时必须额外发送一个用用户长期密钥加密的时间戳即 PA-ENC-TIMESTAMP。AS 解密成功才认定客户端确实知道密码随后才会返回 TGT。如果客户端提交了错误密码AS 直接拒绝请求根本不会给攻击者留下任何可解密的密文。这样字典攻击就只能在线进行受限于 KDC 的速率和策略难度大增。这里面有个实操知识点如果账号开了“支持使用 DES 加密”之类的弱加密选项预认证时间戳就可能用弱算法加密给攻击者提供可乘之机。在配置 Kerberos 时建议关闭 DES、RC4 等弱加密算法只保留 AES 系。4.2 时间同步Kerberos 最容易出幺蛾子的地方整个 Kerberos 机制极度依赖时间戳。Authenticator 和票据的有效期都建立在“客户端时间、KDC 时间、服务端时间尽量一致”的基础上。默认时间偏差上限一般是 5 分钟超过这个范围票据就会被直接判为无效。在实际运维中最常见的报错就是Clock skew too great。这个问题产生的根源通常是客户端机器没有配置 NTP 时间同步或者配置的 NTP 源不可达。Windows 域环境还好域控会自动强制时间同步但在混合环境或者 Linux Kerberos 的独立部署里时间问题几乎人人都会遇到。我的经验是在部署 Kerberos 的任何环境里先把所有参与方的时间同步到同一台 NTP 服务器再谈其他。否则排查问题时会极其痛苦——明明票据都正常签发访问却始终报认证失败最后发现只是时钟偏了 6 分钟。另外要注意容器化环境里如果宿主机时间不准确容器内的时间也会跟着偏。用 Docker 跑 Kerberos 客户端时一定要检查容器和宿主机、以及 KDC 之间的时间差。4.3 票据生命周期为什么 8 小时后会突然失联默认 TGT 有效期是 10 小时服务票据的有效期通常是 10 小时Windows 域里两者都是 10 小时。这个值可以调整但不宜太长。票据的有效期设计是一把双刃剑太长泄露后攻击窗口大太短用户一天要多次输密码体验差。Kerberos 提供了一个折中方案票据续期Renew。TGT 可以被续期默认上限是 7 天。你在第 8 小时快到时客户端可以用现有 TGT 向 KDC 申请“续命”KDC 会签发一个带新有效期的新 TGT不需要重新输密码。这种机制保证了在一天内频繁使用资源的场景下用户不会反复被要求认证。在实际运行中很多人碰到“上午还能正常访问下午突然全部认证失败”的问题十有八九就是 TGT 过期了而客户端又没有按预期执行续期。排查时可以用klist查看本地缓存票据的状态。5. 实际应用场景与配置实操5.1 Windows Active Directory你每天都在用 Kerberos在 Windows 域环境中Kerberos 是默认认证协议。你登录域账号、访问共享文件夹、打开 Outlook 连接 Exchange底层走的就是 Kerberos。AD 中的域控就是 KDCkrbtgt账号就是 KDC 的长期密钥。Windows 客户端拿到 TGT 后会缓存到内存lsass 进程中。你可以用协议栈命令查看klist在 Windows 下klist不是默认系统命令需要从 “VS2015 的 Developer Command Prompt” 或 Microsoft 提供的方法中运行。不过 Windows Server 系统自带klist的所有功能。常见场景双跳问题Double Hop在大数据或虚拟化环境中用户从 Win 电脑 A 远程桌面登录到服务器 BB 上运行的程序再去访问 C 服务。Kerberos 默认不允许你的票据被转发到下一台机器这就导致 B 上的应用无法使用你的身份访问 C。解决方案是在 Kerberos 中启用委派Delegation允许 B 代表你向 C 发起请求。在 AD 里这被称为“约束委派Constrained Delegation”它限定了 B 只能代表用户访问列出的特定服务避免委派权限过大。5.2 Linux 侧配置要点krb5.conf 与 keytabLinux 环境下的 Kerberos 客户端配置主要在一个文件里/etc/krb5.conf。一个最小示例[libdefaults] default_realm EXAMPLE.COM dns_lookup_realm false dns_lookup_kdc true ticket_lifetime 24h renew_lifetime 7d forwardable true rdns false [realms] EXAMPLE.COM { kdc kdc1.example.com kdc kdc2.example.com admin_server kdc1.example.com } [domain_realm] .example.com EXAMPLE.COM example.com EXAMPLE.COM日常操作常用命令# 登录并获取 TGT kinit userEXAMPLE.COM # 查看当前票据信息 klist # 销毁票据 kdestroy # 修改自己的密码会更新 KDC 中的密钥 kpasswd如果需要让某个服务进程无交互地使用 Kerberos 凭证就得使用keytab 文件。keytab 是一个包含服务长期密钥的文件服务启动时通过它读取密钥相当于把密钥固化在文件里。生成 keytab 通常在 KDC 侧操作# 在 KDC 上为服务主体生成 keytab ktutil addent -password -p HTTP/webserver.example.comEXAMPLE.COM -k 1 -e aes256-cts-hmac-sha1-96 wkt /etc/webserver.keytab quit注意 keytab 文件的安全性。它以明文保存密钥材料如果泄露等于任何能读取该文件的人都能伪装成相应服务。建议设置文件权限为 600并限制系统账号读取。5.3 跨域认证与信任关系大型企业往往不止一个域。两个域之间实现用户互访就需要建立域信任Domain Trust。Kerberos 的跨域认证流程是A 域用户访问 B 域服务时A 域的 KDC 发现目标服务的 SPN 不在本域就会向 B 域的 KDC 申请一张“针对 B 域 TGT”的票据。这张票据是 A 域 KDC 用自己的跨域密钥签发的B 域 KDC 信任它。基于这个信任链用户最终能拿到 B 域的服务票据。这个机制设计的巧妙之处在于A 域的 KDC 不会直接信任来自 B 域的用户而是通过“参考票据”建立起信任链条。实际配置跨域时两端都要设置信任关系信任类型分为单向和双向以及可传递与不可传递。在微软 AD 环境里这些设置都在 “Active Directory 域和信任” 管理工具中完成在 MIT Kerberos 体系中则需要配置capaths来明确信任路径。6. 常见问题与排查实践6.1 典型报错与原因速查表报错信息常见原因排查方向KDC_ERR_PREAUTH_FAILED密码错误或预认证数据构造失败确认密码、检查加密算法是否匹配KDC_ERR_C_PRINCIPAL_UNKNOWN客户端主体在 KDC 中不存在检查用户名/Realm 拼写、是否导入主体KDC_ERR_S_PRINCIPAL_UNKNOWNSPN 未注册服务主体不存在在 AD 里用setspn检查服务主体名KRB_AP_ERR_TKT_EXPIRED票据过期klist查看票据有效期重新 kinitKRB_AP_ERR_MODIFIED请求被篡改或加密类型不匹配检查客户端与服务端支持的加密算法Clock skew too great时间偏差超过上限同步所有节点时间检查 NTPCannot find KDC for realm找不到 KDC 地址检查 krb5.conf 配置、DNS 解析6.2 手工模拟 Kerberos 流程的排查思路遇到认证问题我习惯在客户端机器上先用命令手工走一遍流程观察每一步的结果。# 开启调试模式会打印详细交互日志 kinit -V -d userEXAMPLE.COM调试模式下输出中会显示每一步的请求类型、加密算法、KDC 是否响应等信息。以下是我常用的排查顺序先用ping kdc.example.com确认 KDC 可达。用nc -vz kdc.example.com 88确认 UDP/TCP 88 端口开放。Kerberos 默认用 UDP 88但大尺寸响应会回退到 TCP 88。防火墙别只放行 UDP。执行kinit -V -d看完整输出。klist确认票据是否拿到、有效期是否合理。如果票据正常但访问特定服务失败去服务端看应用日志多半是 SPN 不匹配。之前排查过一个 Hadoop 集群的认证问题客户端 kinit 完全正常但访问 NameNode 总是提示GSSException: No valid credentials provided。最后用klist -e一查发现拿到的服务票据是针对host/namenode.example.com而 Hadoop 服务端注册的 SPN 是nn/namenode.example.com。两端 SPN 不一致票据自然对不上。这类问题在组件化架构里特别常见因为每个组件都有自己的 SPN 命名规则。6.3 抓包验证眼见为实排查 Kerberos 问题最核心的手段就是抓包。Wireshark 对 Kerberos 协议有详细解析我们可以在看到底层包结构的同时快速定位是哪一步失败。抓取 Kerberos 报文tshark -i eth0 -Y kerberos -T fields -e kerberos.msg_type -e kerberos.error_codekerberos.msg_type对应 AS-REQ、AS-REP、TGS-REQ、TGS-REP、AP-REQ、AP-REP 等消息类型。如果报错时能看到某个包中携带具体的错误码结合错误码速查表就能快速定位是 KDC 拒绝、票据过期、还是 SPN 找不到。此方法适用于 Windows、Linux 和各类应用场景。Wireshark 里还有一个非常实用的功能Edit - Preferences - Protocols - Kerberos你可以填入 krbtgt 的密码或 keytab 文件Wireshark 会尝试解密部分加密内容直观展示票据中包含的身份信息和有效时间。当然使用这个功能仅限于你有权限访问密钥的环境平时调试时也可以用客户端本地导出的票据缓存来辅助验证。7. 安全实践与个人经验总结从实际维护经验出发有几个容易被忽略的安全点想重点提一下。Kerberos 确实解决了密码明文传输和存储的问题但它自己也不是没有薄弱环节。第一TGT 本身可以被窃取。如果攻击者控制了你的机器内存直接读走 TGT他就能在票据有效期内冒充你。这就是“Pass-the-Ticket”攻击的原理。Kerberos 很难防御这类攻击因为票据本身是合法的。因此守住主机安全比守护协议本身更重要包括加固操作系统、限制管理员权限、部署防病毒和 EDR 产品。第二弱加密算法一定要禁用。RC4 在过去被广泛支持但它存在多个已知安全缺陷。Windows 环境中可以通过组策略“网络安全配置 Kerberos 允许的加密类型”来设置Linux 下则通过[libdefaults]里的permitted_enctypes指定。我的建议是只保留aes256-cts-hmac-sha1-96和aes128-cts-hmac-sha1-96新环境还可以考虑aes256-cts-hmac-sha384-192AES-SHA2 系列。第三委派机制要谨慎配置。无约束委派Unconstrained Delegation意味着服务端可以模拟任何用户访问任意服务这是极大的安全隐患。如果只是想让某个服务代表用户访问另一个服务务必使用约束委派并限定具体的 SPN 列表。这个列表定得越窄越好不要图省事直接勾选“信任此计算机以委派给任何服务”。第四监视 KDC 的审计日志。在 AD 环境里事件 ID 4768TGT 请求、4769服务票据请求和 4771预认证失败分别对应认证流程的关键环节。定期检查这些日志注意是否存在大量异常时间点的 TGT 请求、频繁的失败尝试这些往往比任何安全告警系统都更早发现问题。根据我个人这几年的实操经验Kerberos 最大的特点就是“协议本身很优雅但落地环境永远不配合”。它强依赖时间同步、DNS 解析、SPN 命名规范、多端加密算法一致性这四个里任意一个出问题你都会看到千奇百怪的报错。所以调试 Kerberos 时不要慌按照“时间-DNS-票据-加密算法”的顺序排查大多数问题能在十分钟内定位。这套流程我每次都用几乎没失手过。
返回列表