SAML2断言含OneTimeUse条件验证失败(IDX13510)求助
问题背景
验证带有OneTimeUse条件的SAML2断言时触发以下异常,尝试通过选项类、扩展处理器等方式修改验证行为均未成功:
Microsoft.IdentityModel.Tokens.SecurityTokenValidationException: IDX13510: The Saml2SecurityToken cannot be validated because the Assertion specifies a OneTimeUse condition.Enforcement of the OneTimeUse condition is not supported by default.To customize the enforcement of OneTimeUse condition, extend Saml2SecurityTokenHandler and override ValidateOneTimeUseCondition.
at Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateOneTimeUseCondition(Saml2SecurityToken securityToken, TokenValidationParameters validationParameters)
at Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateConditions(Saml2SecurityToken samlToken, TokenValidationParameters validationParameters)
at ITfoxtec.Identity.Saml2.Tokens.Saml2ResponseSecurityTokenHandler.ValidateToken(SecurityToken token, String tokenString, Saml2Response saml2Response, Boolean detectReplayedTokens)
at ITfoxtec.Identity.Saml2.Saml2AuthnResponse.ReadClaimsIdentity(String tokenString, Boolean detectReplayedTokens)
at ITfoxtec.Identity.Saml2.Saml2AuthnResponse.Read(String xml, Boolean validateXmlSignature, Boolean detectReplayedTokens)
at ITfoxtec.Identity.Saml2.Saml2PostBinding.Read(HttpRequest request, Saml2Request saml2RequestResponse, String messageName, Boolean validateXmlSignature, Boolean detectReplayedTokens)
at ITfoxtec.Identity.Saml2.Saml2Binding`1.ReadSamlResponse(HttpRequest request, Saml2Response saml2Response)
对应的SAML响应示例:
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_381479691072dae3061c8eb476d8f638726d827456" IssueInstant="2023-01-04T19:59:55.431Z" Destination="https://p-dv-vm-tee8won:443/Relativity/Identity/98E1EA02C6824DB779469702E3C3A50DF55188D3"><saml:Issuer>https://capriza.github.io/samling/samling.html</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_b4062517bccdf96dd61be6ad16374ba6aac7bccca6" IssueInstant="2023-01-04T19:59:55.383Z"><saml:Issuer>https://capriza.github.io/samling/samling.html</saml:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#_b4062517bccdf96dd61be6ad16374ba6aac7bccca6"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>czTc3X2L2jmH8hZmbw8CnVbLGWIOCgWpQhLb6cMIRF4=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>NH/nBTI88+D7KqA26IahTsf6bpXqSdBsGf/Bk4gvtJ9NGzfEJ14Pxh9fhYgq47begLqSY61Whoek6CQs908p8Qck37BFtvxR4gsWFULGIbb7ybsEMqajBR0pVDarxcC04bgXTa2nmMs2CO0lwl6lBMAEOQaf/03W8SxLSh2ovPQ=</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIICpzCCAhACCQDuFX0Db5iljDANBgkqhkiG9w0BAQsFADCBlzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCVBhbG8gQWx0bzEQMA4GA1UECgwHU2FtbGluZzEPMA0GA1UECwwGU2FsaW5nMRQwEgYDVQQDDAtjYXByaXphLmNvbTEmMCQGCSqGSIb3DQEJARYXZW5naW5lZXJpbmdAY2Fwcml6YS5jb20wHhcNMTgwNTE1MTgxMTEwWhcNMjgwNTEyMTgxMTEwWjCBlzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCVBhbG8gQWx0bzEQMA4GA1UECgwHU2FtbGluZzEPMA0GA1UECwwGU2FsaW5nMRQwEgYDVQQDDAtjYXByaXphLmNvbTEmMCQGCSqGSIb3DQEJARYXZW5naW5lZXJpbmdAY2Fwcml6YS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJEBNDJKH5nXr0hZKcSNIY1l4HeYLPBEKJLXyAnoFTdgGrvi40YyIx9lHh0LbDVWCgxJp21BmKll0CkgmeKidvGlr3FUwtETro44L+SgmjiJNbftvFxhNkgA26O2GDQuBoQwgSiagVadWXwJKkodH8tx4ojBPYK1pBO8fHf3wOnxAgMBAAEwDQYJKoZIhvcNAQELBQADgYEACIylhvh6T758hcZjAQJiV7rMRg+Omb68iJI4L9f0cyBcJENR+1LQNgUGyFDMm9Wm9o81CuIKBnfpEE2Jfcs76YVWRJy5xJ11GFKJJ5T0NEB7txbUQPoJOeNoE736lF5vYw6YKp8fJqPW0L2PLWe9qTn8hxpdnjo3k6r5gXyl8tk=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">redactedemail@relativity.com</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="2023-01-04T20:09:55.383Z" Recipient="https://p-dv-vm-tee8won:443/Relativity/Identity/98E1EA02C6824DB779469702E3C3A50DF55188D3"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2023-01-04T19:59:55.383Z" NotOnOrAfter="2023-01-04T20:09:55.383Z"><saml:AudienceRestriction><saml:Audience>https://p-dv-vm-tee8won/Relativity</saml:Audience></saml:AudienceRestriction><saml:OneTimeUse></saml:OneTimeUse></saml:Conditions><saml:AuthnStatement AuthnInstant="2023-01-04T19:59:55.383Z" SessionNotOnOrAfter="2023-01-04T20:09:55.381Z" SessionIndex="_samling_4122095_80192495"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion></samlp:Response>
解决方案
由于使用的是ITfoxtec Identity SAML2库,需继承该库的Saml2ResponseSecurityTokenHandler并重写ValidateOneTimeUseCondition方法,替换默认处理器即可实现自定义验证逻辑。
1. 自定义SAML响应处理器
创建自定义处理器类,重写ValidateOneTimeUseCondition方法:
using ITfoxtec.Identity.Saml2.Tokens; using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens.Saml2; public class CustomSaml2ResponseSecurityTokenHandler : Saml2ResponseSecurityTokenHandler { protected override void ValidateOneTimeUseCondition(Saml2SecurityToken securityToken, TokenValidationParameters validationParameters) { // 方案1:完全跳过OneTimeUse验证(适合测试环境或完全信任IDP的场景) // 无需任何代码,直接跳过默认验证逻辑 // 方案2:实现自定义一次性使用校验(生产环境推荐) // 示例:使用缓存存储已使用的断言ID,防止重复使用 // var assertionId = securityToken.Assertion.Id.Value; // var cacheKey = $"SamlOneTimeUse_{assertionId}"; // if (MemoryCache.Default.Contains(cacheKey)) // { // throw new SecurityTokenValidationException("该SAML断言已被使用过,无法重复验证"); // } // // 按断言有效期设置缓存过期时间 // var expiration = securityToken.Assertion.Conditions.NotOnOrAfter ?? DateTime.UtcNow.AddMinutes(10); // MemoryCache.Default.Add(cacheKey, true, expiration); } }
2. 替换默认处理器
在服务配置中注册自定义处理器,替换默认的Saml2ResponseSecurityTokenHandler:
using ITfoxtec.Identity.Saml2; using Microsoft.Extensions.DependencyInjection; // 在Startup.cs或Program.cs的服务配置中添加 services.Configure<Saml2Configuration>(Configuration.GetSection("Saml2")) .AddSaml2() .AddSaml2SecurityTokenHandler<CustomSaml2ResponseSecurityTokenHandler>();
说明
之前修改选项类无效的原因是:OneTimeUse条件的验证逻辑硬编码在Saml2SecurityTokenHandler的方法中,必须通过重写对应方法才能修改行为。且由于使用了ITfoxtec的封装库,必须替换该库提供的处理器,而非直接修改Microsoft的基础处理器。
内容的提问来源于stack exchange,提问作者mtron

