<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()
}
リレーステートによる <saml2:AuthnRequest> のキャッシュ
<saml2:AuthnRequest> をセッションに保存したくない場合は、分散キャッシュに保存することもできます。これは、SameSite=Strict を使用しようとしていて、アイデンティティプロバイダからのリダイレクトで認証リクエストが失われている場合に役立ちます。
セッションに保存することにはセキュリティ上の利点があることを覚えておくことが重要です。その利点の一つは、自然なログイン固定防御を提供することです。たとえば、アプリケーションがセッションから認証リクエストを参照する場合、攻撃者が被害者に独自の SAML レスポンスを提供したとしても、ログインは失敗します。 一方、認証リクエストを取得するために InResponseTo または RelayState を信頼する場合、SAML レスポンスがそのハンドシェイクによってリクエストされたかどうかを知る方法はありません。 |
これを支援するために、Spring Security には CacheSaml2AuthenticationRequestRepository があり、これを Bean として公開して、フィルターチェーンが取得できるようにすることができます。
Java
Kotlin
@Bean
Saml2AuthenticationRequestRepository<?> authenticationRequestRepository() {
return new CacheSaml2AuthenticationRequestRepository();
}
@Bean
fun authenticationRequestRepository(): Saml2AuthenticationRequestRepository<*> {
return CacheSaml2AuthenticationRequestRepository()
}
<saml2:AuthnRequest> の送信方法の変更
デフォルトでは、Spring Security は各 <saml2:AuthnRequest> に署名し、それを GET としてアサーティングパーティに送信します。
多くの主張当事者は、署名された <saml2:AuthnRequest> を必要としません。これは、RelyingPartyRegistrations を介して自動的に構成することも、次のように手動で指定することもできます。
Boot
Java
Kotlin
spring:
security:
saml2:
relyingparty:
registration:
okta:
assertingparty:
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
}