0.5 to 0.6
Upgrade saml-rs, opensaml, samlify, or samlet from the
latest 0.5.x release to 0.6.x.
The CBC response-signature relaxation changed names. What it does is
unchanged. A new validation context allows five minutes of clock skew, and
a required replay cache keeps a bearer assertion identifier until
SubjectConfirmationData@NotOnOrAfter. RequireSignature now requires an
enveloped #id metadata signature. A login response with a <Conditions>
child other than <AudienceRestriction>, <OneTimeUse>, or
<ProxyRestriction> is rejected. A response may contain several assertions
when a signature covers them. An unsigned HTTP-Redirect that still contains
a protocol ds:Signature returns SamlError::ProtocolProfile. A signed
redirect removes that signature before DEFLATE. accept_unsolicited_sso
rejects a RelayState longer than 80 bytes. A custom LogoutResponse
template must carry the top-level status respond_slo writes. An exhaustive
match on AudienceValidationPolicy or AuthnRequestValidationPolicy
needs the new variant.
CBC responses
Section titled “CBC responses”This breaks callers that name the old method or the old policy variant.
Who must change: code that calls
RespondSso::allow_unsigned_encrypted_cbc_for_compatibility, and code that
names ResponseSignaturePolicy::AllowUnsignedEncryptedCbcForCompatibility.
Call the method that allows an unsigned Response around a CBC-encrypted Assertion:
RespondSso::post().allow_unsigned_encrypted_cbc();Select the same inbound relaxation with:
ResponseSignaturePolicy::AllowUnsignedEncryptedCbcBoth still relax SAML V2.0 Approved Errata 05 E93 only. They are not the Compatibility preset.
SpValidationPolicy::compatibility, IdpValidationPolicy::compatibility,
and LogoutPolicy::compatibility keep their names. Compatibility is the
legacy permissive preset: the samlify-port behaviour kept for a caller leaving
the raw API. It does not claim standards conformance.
Conditions
Section titled “Conditions”This breaks a service provider whose identity provider sends a <Conditions>
child other than <AudienceRestriction>, <OneTimeUse>, or
<ProxyRestriction>, including <Condition>.
Who must change: that integration. finish_sso and accept_unsolicited_sso
reject the response. There is no switch that turns the check off.
<OneTimeUse> and <ProxyRestriction> stay valid. The classification is in
SSO acceptance.
Several assertions
Section titled “Several assertions”This breaks a service provider that treated a second <Assertion> as a
rejected response.
Who must change: that integration. finish_sso and accept_unsolicited_sso
accept the response when a signature covers every assertion and the assertions
share one issuer and one principal. There is no switch that restores the
rejection.
The principal comparison is NameID value, Format, NameQualifier, and
SPNameQualifier. An omitted Format is
urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified. The session comes from
the first assertion with a bearer confirmation. Replay storage keeps every
assertion identifier. The classification is in
SSO acceptance.
Clock skew
Section titled “Clock skew”A new SamlValidationContext allows five minutes of skew on NotBefore and
NotOnOrAfter. That covers assertion conditions, bearer confirmation,
AuthnStatement@SessionNotOnOrAfter, and LogoutRequest@NotOnOrAfter.
Who must change: code that calls SamlValidationContext::new without
with_clock_skew and needs those instants enforced with no tolerance.
Keep zero skew:
let validation = SamlValidationContext::new(now, replay) .with_clock_skew(ClockSkew::strict());If you already set a skew, that value replaces the five minutes.
ClockSkew::default stays zero. Raw clock_drifts are unchanged.
If receive_slo stores a LogoutRequest identifier, that deadline is now
NotOnOrAfter plus the same skew. The allowance is classified in
SSO acceptance
and Logout.
Replay retention
Section titled “Replay retention”Under ReplayPolicy::RequireCache, the bearer assertion identifier is
stored until SubjectConfirmationData@NotOnOrAfter, plus the context
NotOnOrAfter skew. An earlier Conditions or AuthnStatement instant
does not shorten that identifier. The Response identifier is still stored
and can still expire at the earlier instant.
Who must change: code whose cache drops an assertion identifier at the
expires_at passed to ReplayCache::check_and_store, and that assumed
that instant was the earliest of Conditions, bearer
SubjectConfirmationData, and AuthnStatement.
ReplayPolicy::DisabledForCompatibility still stores nothing. Keep that
hatch:
let validation = SamlValidationContext::new( now, ReplayPolicy::DisabledForCompatibility,);Signed metadata
Section titled “Signed metadata”This breaks callers that import metadata with
MetadataTrustPolicy::RequireSignature when the signature is not an enveloped
signature with one #id reference to the root, when it contains a ds:Object,
or when a reference transform is not enveloped-signature or exclusive
canonicalization. A root EntitiesDescriptor is checked the same way.
Who must change: code that imports metadata with
MetadataTrustPolicy::RequireSignature.
If the signature uses inclusive canonicalization, select it and leave the other checks on:
MetadataTrustPolicy::RequireSignatureAllowingOtherCanonicalization { trusted_certificates: &certificates,}For any other rejected shape, sign the metadata root with an enveloped #id
reference and exclusive canonicalization. The accepted shapes are recorded in
Metadata and replay.
An exhaustive match on MetadataTrustPolicy needs an arm for
RequireSignatureAllowingOtherCanonicalization. UnsignedForCompatibility
is unchanged.
HTTP-Redirect
Section titled “HTTP-Redirect”This breaks a typed HTTP-Redirect whose protocol element still has a direct
child ds:Signature.
Who must change: start_sso, start_slo, and respond_slo on that binding,
including a custom template that embeds the signature.
An unsigned redirect returns SamlError::ProtocolProfile. Remove the
protocol signature and attach the detached query signature.
A signed redirect removes that signature before raw DEFLATE. The detached
signature covers SAMLRequest or SAMLResponse, then RelayState when it
is present, then SigAlg. A signature nested on an assertion stays in the
XML. Verify the query signature. The classification is in
SSO generation
and Logout.
RelayState
Section titled “RelayState”This breaks an unsolicited login response whose RelayState is longer than
80 bytes.
Who must change: the caller of accept_unsolicited_sso or
accept_unsolicited_sso_with_outstanding_logout. The call returns
SamlError::Invalid. Shorten RelayState to 80 bytes, or omit it.
finish_sso, receive_sso, and typed Single Logout already enforced this
limit. The classification is in
SSO acceptance.
Logout status
Section titled “Logout status”This breaks a TemplatePolicy whose logout_response_template does not
carry the top-level status respond_slo writes.
Who must change: a session participant or session authority that sets that
field on SpConfig or IdpConfig. Omitting RespondSlo::status still
writes urn:oasis:names:tc:SAML:2.0:status:Success.
Keep the placeholder on the top-level status code:
<samlp:Status> <samlp:StatusCode Value="{StatusCode}"/></samlp:Status>{StatusCode} becomes that URI. After substitution, the top-level
StatusCode must equal the status supplied to respond_slo. A template
that keeps a different value returns SamlError::Invalid.
{StatusCode} holds only the top-level code. To send a subordinate code,
omit logout_response_template and pass RespondSlo::status. The default
renderer writes the top-level code and one subordinate code.
Match arms
Section titled “Match arms”This breaks an exhaustive match on AudienceValidationPolicy or
AuthnRequestValidationPolicy.
Who must change: that match. Add
AudienceValidationPolicy::EvaluatePresentRestrictions and
AuthnRequestValidationPolicy::AllowUnsignedVerifyIfPresent.
Default, new, try_new, and compatibility() still return the 0.5
variants. Config builders still start from strict(). recommended()
selects the new variants. Match SkipForCompatibility and
AllowUnsignedForCompatibility by name: inserting the new variants changed
their numeric discriminants.
strict()
Section titled “strict()”The following release removes strict() and points Default, new,
try_new, and the config builders at Recommended together.
Who must change: code that calls SpValidationPolicy::strict,
IdpValidationPolicy::strict, or LogoutPolicy::strict. In that same release,
code that depends on Default, new, or try_new staying on Compatibility
must change, as must code that depends on a config builder staying on the
strict() bundle.
Start from recommended() and add only the named hardenings you want. A
direct Assertion signature and the RSA-SHA2 XML-DSig profile stay independent
options. Neither is implied by recommended(), and strict() does not gain
the RSA-SHA2 profile. Set one field, the other, or both:
let require_assertion_signature = SpValidationPolicy { assertions: AssertionSignaturePolicy::RequireSigned, ..SpValidationPolicy::recommended()};
let require_rsa_sha2 = SpValidationPolicy { xml_signatures: XmlSignatureProfile::StrictRsaSha2, ..SpValidationPolicy::recommended()};LogoutPolicy::recommended() is the replacement for LogoutPolicy::strict().
Both require a signature on logout requests and responses.
SpValidationPolicy::compatibility, IdpValidationPolicy::compatibility, and
LogoutPolicy::compatibility stay available by name. Raw settings defaults
stay unchanged.