このバージョンはまだ開発中であり、まだ安定しているとは見なされていません。最新の安定バージョンについては、Spring Security 6.4.5 を使用してください!

<saml2:AuthnRequest> の作成

前述のように、Spring Security の SAML 2.0 サポートは、アサーティングパーティとの認証を開始するための <saml2:AuthnRequest> を生成します。

Spring Security は、フィルターチェーンに Saml2WebSsoAuthenticationRequestFilter を登録することで、これを部分的に実現します。このフィルターは、デフォルトでエンドポイント /saml2/authenticate/{registrationId} および /saml2/authenticate?registrationId={registrationId} に応答します。

例: rp.example.com (英語)  にデプロイされ、登録に okta の ID を付与した場合、次の場所に移動できます。

結果は、署名され、デフレートされ、エンコードされた <saml2:AuthnRequest> を含む SAMLRequest パラメーターを含むリダイレクトになります。

<saml2:AuthnRequest> エンドポイントの構成

エンドポイントをデフォルトとは異なる設定にするには、saml2Login に値を設定します。

  • Java

  • Kotlin

@Bean
SecurityFilterChain filterChain(HttpSecurity http) {
	http
        .saml2Login((saml2) -> saml2
            .authenticationRequestUriQuery("/custom/auth/sso?peerEntityID={registrationId}")
        );
	return new CustomSaml2AuthenticationRequestRepository();
}
@Bean
fun filterChain(http: HttpSecurity): SecurityFilterChain {
    http {
        saml2Login {
            authenticationRequestUriQuery = "/custom/auth/sso?peerEntityID={registrationId}"
        }
    }
    return CustomSaml2AuthenticationRequestRepository()
}

<saml2:AuthnRequest> の保存方法の変更

Saml2WebSsoAuthenticationRequestFilter は、Saml2AuthenticationRequestRepository を使用して AbstractSaml2AuthenticationRequest インスタンスを永続化してから、<saml2:AuthnRequest> をアサート側に送信します。

さらに、Saml2WebSsoAuthenticationFilter および Saml2AuthenticationTokenConverter は、<saml2:Response> の認証の一部として、Saml2AuthenticationRequestRepository を使用して任意の AbstractSaml2AuthenticationRequest をロードします。

デフォルトでは、Spring Security は HttpSessionSaml2AuthenticationRequestRepository を使用します。HttpSessionSaml2AuthenticationRequestRepository は AbstractSaml2AuthenticationRequest を HttpSession に格納します。

Saml2AuthenticationRequestRepository のカスタム実装がある場合は、次の例に示すように、@Bean として公開することで構成できます。

  • Java

  • Kotlin

@Bean
Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository() {
	return new CustomSaml2AuthenticationRequestRepository();
}
@Bean
open fun authenticationRequestRepository(): Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> {
    return CustomSaml2AuthenticationRequestRepository()
}

Caching the <saml2:AuthnRequest> by the Relay State

If you don’t want to use the session to store the <saml2:AuthnRequest>, you can also store it in a distributed cache. This can be helpful if you are trying to use SameSite=Strict and are losing the authentication request in the redirect from the Identity Provider.

It’s important to remember that there are security benefits to storing it in the session. One such benefit is the natural login fixation defense it provides. For example, if an application looks the authentication request up from the session, then even if an attacker provides their own SAML response to a victim, the login will fail.

On the other hand, if we trust the InResponseTo or RelayState to retrieve the authentication request, then there’s no way to know if the SAML response was requested by that handshake.

To help with this, Spring Security has CacheSaml2AuthenticationRequestRepository, which you can publish as a bean for the filter chain to pick up:

  • Java

  • Kotlin

@Bean
Saml2AuthenticationRequestRepository<?> authenticationRequestRepository() {
	return new CacheSaml2AuthenticationRequestRepository();
}
@Bean
fun authenticationRequestRepository(): Saml2AuthenticationRequestRepository<*> {
    return CacheSaml2AuthenticationRequestRepository()
}

<saml2:AuthnRequest> の送信方法の変更

デフォルトでは、Spring Security は各 <saml2:AuthnRequest> に署名し、それを GET としてアサーティングパーティに送信します。

多くの主張当事者は、署名された <saml2:AuthnRequest> を必要としません。これは、RelyingPartyRegistrations を介して自動的に構成することも、次のように手動で指定することもできます。

署名済み AuthnRequests は不要
  • Boot

  • Java

  • Kotlin

spring:
  security:
    saml2:
      relyingparty:
        okta:
          identityprovider:
            entity-id: ...
            singlesignon.sign-request: false
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta")
        // ...
        .assertingPartyMetadata(party -> party
            // ...
            .wantAuthnRequestsSigned(false)
        )
        .build();
var relyingPartyRegistration: RelyingPartyRegistration =
    RelyingPartyRegistration.withRegistrationId("okta")
        // ...
        .assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party
                // ...
                .wantAuthnRequestsSigned(false)
        }
        .build()

それ以外の場合は、Spring Security が送信前に <saml2:AuthnRequest> に署名できるように、RelyingPartyRegistration#signingX509Credentials に秘密鍵を指定する必要があります。

デフォルトでは、Spring Security は rsa-sha256 を使用して <saml2:AuthnRequest> に署名しますが、一部のアサートパーティは、メタデータに示されているように、異なるアルゴリズムを必要とします。

RelyingPartyRegistrations を使用して、アサート側のメタデータに基づいてアルゴリズムを構成できます。

または、手動で提供することもできます。

  • Java

  • Kotlin

String metadataLocation = "classpath:asserting-party-metadata.xml";
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations.fromMetadataLocation(metadataLocation)
        // ...
        .assertingPartyMetadata((party) -> party
            // ...
            .signingAlgorithms((sign) -> sign.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512))
        )
        .build();
var metadataLocation = "classpath:asserting-party-metadata.xml"
var relyingPartyRegistration: RelyingPartyRegistration =
    RelyingPartyRegistrations.fromMetadataLocation(metadataLocation)
        // ...
        .assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party
                // ...
                .signingAlgorithms { sign: MutableList<String?> ->
                    sign.add(
                        SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512
                    )
                }
        }
        .build()
上記のスニペットは、OpenSAML SignatureConstants クラスを使用してアルゴリズム名を提供します。しかし、単に便宜のためです。データ型は String であるため、アルゴリズムの名前を直接指定できます。

一部のアサーティングパーティは、<saml2:AuthnRequest> の POST を要求します。これは、RelyingPartyRegistrations を介して自動的に構成することも、次のように手動で指定することもできます。

  • Java

  • Kotlin

RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta")
        // ...
        .assertingPartyMetadata(party -> party
            // ...
            .singleSignOnServiceBinding(Saml2MessageBinding.POST)
        )
        .build();
var relyingPartyRegistration: RelyingPartyRegistration? =
    RelyingPartyRegistration.withRegistrationId("okta")
        // ...
        .assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party
            // ...
            .singleSignOnServiceBinding(Saml2MessageBinding.POST)
        }
        .build()

OpenSAML の AuthnRequest インスタンスのカスタマイズ

AuthnRequest を調整する理由はいくつかあります。例: ForceAuthN を true に設定することができます。Spring Security はデフォルトで false に設定します。

次のように、OpenSaml4AuthenticationRequestResolver を @Bean として公開することにより、OpenSAML の AuthnRequest の要素をカスタマイズできます。

  • Java

  • Kotlin

@Bean
Saml2AuthenticationRequestResolver authenticationRequestResolver(RelyingPartyRegistrationRepository registrations) {
    RelyingPartyRegistrationResolver registrationResolver =
            new DefaultRelyingPartyRegistrationResolver(registrations);
    OpenSaml4AuthenticationRequestResolver authenticationRequestResolver =
            new OpenSaml4AuthenticationRequestResolver(registrationResolver);
    authenticationRequestResolver.setAuthnRequestCustomizer((context) -> context
            .getAuthnRequest().setForceAuthn(true));
    return authenticationRequestResolver;
}
@Bean
fun authenticationRequestResolver(registrations : RelyingPartyRegistrationRepository) : Saml2AuthenticationRequestResolver {
    val registrationResolver : RelyingPartyRegistrationResolver =
            new DefaultRelyingPartyRegistrationResolver(registrations)
    val authenticationRequestResolver : OpenSaml4AuthenticationRequestResolver =
            new OpenSaml4AuthenticationRequestResolver(registrationResolver)
    authenticationRequestResolver.setAuthnRequestCustomizer((context) -> context
            .getAuthnRequest().setForceAuthn(true))
    return authenticationRequestResolver
}