linux 基于SPNEGO的SSL身份验证:是否应该为每个请求使用`gss_init_sec_context`创建新的安全上下文?

nle07wnf  于 5个月前  发布在  Linux
关注(0)|答案(1)|浏览(51)

我正在为一个Linux客户端应用程序实现基于SPNEGO的身份验证,以验证对Windows IIS服务器的请求。
我读过RFC 4559,它描述了应该如何执行身份验证:
https://datatracker.ietf.org/doc/html/rfc4559
我的问题如下:
1.我应该为每个HTTP请求使用gss_init_sec_context创建一个新的安全上下文吗?这样做成本高吗?Curl为每个请求创建一个安全上下文,但它只执行一个请求:https://github.com/curl/curl/blob/c44671ed431aa3d40819e59f63c471be158480a7/lib/krb5.c#L348
1.如何在PUT/POST请求中使用这种形式的身份验证?我不知道gss_init_sec_context的结果是否会返回HTTP 200或HTTP 401 Negotiate(续)。因此,我不知道何时发送PUT/POST主体?
当使用SPNEGO HTTP身份验证工具处理客户端提供的数据(如PUT和POST)时,在发送用户数据之前,客户端和服务器之间的身份验证应该完成。gss_init_security_context的返回状态将指示安全上下文已完成。此时,可以将数据发送到服务器。
1.为每个请求执行协商(认证)似乎是无效的。为什么客户端不进行一次认证,并为后续请求接受生成的令牌,就像OAuth2.0的情况一样?

额外
Windows

securityInterface->InitializeSecurityContext(
    &credentials, hasContext ? &securityContext : NULL, const_cast<TCHAR*>(servicePrincipalName.c_str()),
    ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_DELEGATE, 0,
    SECURITY_NATIVE_DREP, (inboundData != NULL) ? &inboundBufferDesc : NULL, 0, &securityContext,
    &outboundBufferDesc, &contextAttr, &expiration);

字符串

OSX

OM_uint32 return_flags{0};
OM_uint32 request_flags{GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG};
OM_uint32 minor_status{0};
OM_uint32 major_status = gss_init_sec_context(
    &minor_status, GSS_C_NO_CREDENTIAL, (gss_ctx_id_t*)context, (gss_name_t)name, GSS_KRB5_MECHANISM,
    request_flags, 0, GSS_C_NO_CHANNEL_BINDINGS, (gss_buffer_desc*)input_token_x, nullptr,
    (gss_buffer_desc*)output_token, &return_flags, nullptr);

sulc1iza

sulc1iza1#

我是否应该为每个HTTP请求使用gss_init_sec_context创建一个新的安全上下文,
是的,您应该为每个请求创建一个新的上下文。
GSSAPI是有状态的-每个机制都是在“步骤”中完成的,API上下文是保持这种状态的东西;碰巧的是,HTTP机制通常在一个步骤中完成。但通常,第一步是输出HTTP头中使用的令牌,并且不能永远重复它;第二步将完全执行其他操作(对于HTTP,它将获取服务器的回复并执行相互认证),而第三步根本无法执行。
如果您使用的是一个有状态协议,其中票据可以发送一次,而其他消息仅通过MAC发送艾德,那么您将为wrap/unwrap或seal/unseal函数重用相同的上下文;但HTTP不保留这种状态。
这样做是否昂贵?
是的,这是对系统的GSSAPI或SSPI库的额外调用,但在您获得一次票证后,对该服务主体的所有进一步请求将完全脱机处理-您不会每次都调用KDC。
在发现它太昂贵的情况下,一个常见的模式是只保护一个/login端点,其中您的应用可以将/login令牌交换为普通的HTTP cookie或OAuth2令牌。
如何在PUT/POST请求中使用这种形式的身份验证?我不知道gss_init_sec_context的结果是否会返回HTTP 200或HTTP 401 Negotiate(continuation)。因此,我不知道何时发送PUT/POST主体?
如果您没有使用GSSAPI中的相互身份验证选项,则验证机制总是在一个步骤中完成,并且不会继续。(底层的Krb 5调用始终是单步的,即使GSSAPI将其 Package 在有状态框架中。)我相信SPNEGO在使用其默认机制时也是如此(如果不是100%,则为99%),因为初始SPNEGO令牌将只是一个令牌,实际上不会进行任何协商。
因此,如果您知道需要进行身份验证,只需主动获取令牌并将其包含在标准请求中。
另外,如果我没记错的话,你可以在你的PUT请求中包含一个Expect: 100-continue,要求服务器在头之后返回一个HTTP 100 Continue,允许主体被延迟。
为每个请求执行协商(认证)似乎是无效的。为什么客户端不进行一次认证,并为后续请求接受生成的令牌,就像OAuth2.0的情况一样?
不,它已经非常像OAuth2.0了。身份提供者只对客户端进行一次身份验证;在你收到一张票(有效期约10小时)之后,同一张票将被重复用于你发出的每个HTTP请求。因此,在第一次请求之后,创建GSSAPI上下文的过程应该非常便宜。
回想一下,SSL是在SSL/TLS出现之前近十年开发的(并且比普遍存在的TLS早得多);它的原始设计是用于完全明文传输-甚至可以 * 提供 * 整个安全通道(您可以看到今天正在使用NFS或LDAP)。
因此,在设计上最大的区别在于,安全令牌包括一个一次性签名,以防止重放攻击。与承载令牌不同,安全令牌不是安全敏感的-它带有一个提供安全保证的“会话密钥”;该密钥从不通过网络发送,但用于生成一次性令牌,该令牌不能像OAuth2承载令牌那样被盗和重放。
(And这实际上与最近的OAuth1.x没有太大区别,后者使用的是每个请求的签名; OAuth2中切换到原始承载令牌是一个重大的偏离。
你写了“是的,你应该为每个请求创建一个新的上下文。",你如何从RFC中看到这一点?
根据RFC 1964第1.1节,从客户端到服务器的初始输出令牌包含一个GSSAP-Token消息(其中包含一个票证和一个令牌)。根据RFC 1510第3.2.2节,身份验证器不能被重用;服务器将有一个“重放缓存”来检测令牌重用,从而检测GSSAPI令牌重用。
当你提到一张机票时,你指的是一张机票?
不,我指的是服务票。
通常通过使用SSL TGT获得的SSL TGS与基于SPNEGO的SSL身份验证有何关系?
TGS不是“获取”的东西。TGS(Ticket-Granting Service)是您从获取票证的地方-它是由KDC执行的两个功能之一。
(You从AS获得初始TGT,然后您交易TGT以从TGS获得服务票-或者,可选地,以相同的方式获得更新的TGT。)
比特币真的是比特币吗?

如果您所说的“TGS”是指“服务票证”-某种意义上,但不完全是。GSSAPI SPNEGO令牌包含一个SOAP消息1,该消息包含一个适用于该服务的SOAP票证以及一个用于证明票证所有权的MAC(一次性MAC)。
1(在GSSAPI和SPNEGO之前,它就有自己的身份验证令牌格式;这仍然可以通过直接libkrb 5使用,并且可以在一些旧的非GSSAPI软件中找到。
我在这里纠正你:如果您没有在GSSAPI中使用相互身份验证选项,则验证机制始终在一个步骤中完成,并且不会有任何继续。RFC规定可能会发生任意数量的步骤,并且客户端应该能够处理这种情况
GSSAPI框架可以包含任意数量的步骤,但每个特定机制的步骤数量通常是固定的。如果您使用GSSAPI和GSSAPI作为机制,RFC 1964(第1.1.2节,第一段)指出,当未启用相互验证时,仅向一个方向发送一条消息,并且您通常可以依赖该行为。
“如果你使用的是一个有状态协议,其中票据可以发送一次,并且进一步的消息仅仅是MAC'ed艾德,那么你将为wrap/unwrap或seal/unseal函数重复使用相同的上下文;但是HTTP不保持这种状态。”-你会在这里详细说明一个有状态协议的例子和wrap/unwrap或seal/unseal函数的名称吗?
LDAP就是一个广泛使用的例子它通过另一个框架(SASL)集成了GSSAPI,但这两个框架共享概念。LDAP中的身份验证始终是“持久的”(每连接,而不是每请求)-客户端和服务器交换SASL/GSSAPI消息,直到身份验证完成(并且两者都有一个“完整的”GSSAPI上下文),然后它们使用GSS_Wrap()和GSS_Unwrap()函数来加密连接的其余部分(或者可能只提供完整性,在微软术语中也称为“签名”)。这些消息实际上具有GSS_C_SEQUENCE_FLAG引用的序列号。
例如Cyrus SASL库,它被libldap在非Windows平台上使用。(注意这里的“token”指的是一大块签名/密封的数据,TLS可能称之为“record”-不再是身份验证token。)另请参阅SSPI互操作性文档,了解除了Wrap/Unwrap之外的其他一些功能。
虽然许多协议都使用SASL(和SASL GSSAPI/GSS-SPNEGO),但除了LDAP之外,几乎没有一个协议使用SASL的签名/盖章功能,而更喜欢TLS。但特别是在MS Active Directory环境中,TLS需要手动设置,但TLS保证始终可用,它仍然每天使用。
在GSSAPI之前,通过krb_priv_priv()和类似的函数,在“raw”中可以使用相同的功能。
当通过加密的SSL/TLS通道进行通信时,双向身份验证的目的是什么?
我已经为我的问题添加了一些代码,适用于Windows和OSX。在OSX的情况下,我可以省略GSS_C_MUTUAL_FLAG标志,服务器不会响应HTTP 200和附加的延续数据(相互身份验证)来完成安全上下文。在Windows的情况下,我不能禁用相互身份验证,因为没有实现这一点的标志?
这似乎是ISC_MUTUAL_AUTH?
因此,它总是以延续数据进行响应。在这种情况下,忽略该数据并删除安全上下文是否安全?
是的,如果你没有进行相互认证,你可以忽略这些数据;但是如果我正确理解了GSSAPI认证机制,这是服务器关心的最后一步,所以即使有了 * 相互认证,流程也不会改变-你仍然可以将响应馈送到init_context并在本地检查返回代码,而不必发送任何东西。
当对Windows IIS使用SSL时,我观察到Persistent-Auth:true被返回给客户端。这导致所有请求在Windows上自动进行身份验证,但在OSX上不会。您对此有任何经验吗?
否;乍一看,这感觉很像是从NTLM遗留下来的(这是多步骤的,因此微软决定在集成到HTTP时使其连接绑定)。尽管微软的own docs关于这个头说它是“性能优化的一部分,并不保证任何东西”。

相关问题