こんにちは。セーフィー株式会社 バックエンドエンジニアの河津です。
セーフィーにはクラウドカメラやユーザーアカウントを一括管理できる統合環境である「Safie Manager」というサービスがあり、主にエンタープライズのお客様にご活用いただいています。
7/7(木)にはSSO(シングルサインオン)の機能をリリースしました!
今回はそのリリースされたばかりの新機能「シングルサインオン(以下SSO)」と、それを実現させる仕組み「SAML認証」について解説させていただきます。
SSOについて
SSOとは、1度システム利用開始のユーザー認証 (ログイン) を行うと、複数のシステムを利用開始する際に、都度認証を行う必要がない仕組みのことです。
基本的に一つのWebサービスで使われるID・パスワードなどの認証情報はそれぞれ独立しており、Aのサービスを使用するにはAの認証情報、Bのサービスを使用するにはBの認証情報が必要です。
SSOを使用すると、この各サービスで分かれてしまう認証情報を統一して使用することができるようになります。
例えばAzure Active Directoryのような認証基盤を利用している場合、その認証基盤とWebサービスを連携させることで、認証基盤の方にログインするだけで様々なWebサービスを使うことができるようになります。
SSOのメリットは大きく3つあるかと思っています。
1. 利便性が上がる
認証基盤にログインさえしていれば、各WebサービスでIDやパスワードなどの情報を入力する必要がなくなるため、Webサービスの利便性が上がります。
2. セキュリティリスクが下がる
本当はあるべきではないですが、各WebサービスでIDやパスワードを使い回してしまうユーザーは一定数いるのではないかと思います。ただ、同じID・パスワードを各Webサービスで使い回すと、その分だけ流出する可能性を増やすことになります。
認証基盤を経由するSSOであれば、流出する可能性のあるサービスは認証基盤のみに絞られることになります。
この場合、認証基盤のID/Passwordが漏れた場合は全てのサービスにアクセス可能なリスクが残るものの、認証基盤には2要素認証機能やクライアント証明書の検証機能が含まれていることが多いため、それらの機能を併用する前提であればSSOを使用する方がセキュリティリスクが下がるのではないかと思います。
3. IT部門での管理が容易になる
各Webサービスの認証情報が認証基盤に集約されると、アカウントの停止やパスワードの変更などを行う際は認証基盤を操作するのみでよくなるため、とても楽になります。
SAML認証に関わる用語解説
SSOを実現するための認証手法はいくつかありますが、セーフィーから提供しているSafie ManagerではSAML認証をサポートしております。
これからSAML認証の解説をさせていただきたいと思っていますが、その前に解説に必要となる用語の説明をさせていただきます。
IdP(Identity Provider): 認証情報を提供する側のシステムです。上の説明で出てきた「認証基盤」がこちらになります。
SP(Service Provider): 認証情報を利用する側のシステムです。
Safie ManagerではSSO機能をサポートしていますが、本記事が公開された段階では認証基盤についてAzure Active Directoryを推奨とさせていただいております。上の用語に当てはめると、Safie ManagerはAzure Active Directoryを認証基盤として使用しSAML認証を行うことになるので、この場合はIdP=Azure Active Directory、SP=Safie Manager となります。
以降ではIdP・SPという用語を用いて解説をさせていただきます。
SAML認証について
ここから本格的にSAML認証の話をしていきますが、SAML認証はそのフローの複雑さから初見で理解するのはかなり難しい処理だと(個人的に)思っています。
この記事を一度読むだけですと、もしかすると不明点だらけになってしまう恐れがあるため、2度・3度ほど読み返していただくことをおすすめします。
まずSAMLとは、Security Assertion Markup Languageの略称です。インターネットドメイン間でユーザー認証を行なうためのXML(マークアップ言語)をベースにした標準規格のことを指すのですが、「インターネットドメイン間でユーザー認証を行う」という点がまさしくSSOでやりたいことになります。SSOでは、IdPとSPという異なるドメイン同士で認証を確立させる必要があるためです。
SAML認証ではどのようなシーケンスで処理が行われるのでしょうか?シーケンス図を起こしてみました。
様々な処理が入り乱れていますね。
一つ一つ処理の内容を取り上げていきたいと思いますが、その前にSAML認証の事前準備の話をさせてください。
シーケンスを見ていただければ分かるとおり、SPからIdPにリダイレクトしたり、IdPからSPに戻ってきたりしているようです。このリダイレクト先は、事前にわかっていないとリダイレクトすることができません。SAML認証の事前準備として、SP・IdPそれぞれの認証エンドポイント情報を登録しあう必要があります。
また後に触れますが、IdPから送られてきた情報が正しいものであるのか検証するフローがあり、IdPより発行した公開鍵をSPに登録することも必要となります。それらの事前準備を経ることで、SAML認証が可能となります。
それでは、割り振った番号それぞれで行われている処理について解説します。
1: ユーザーがアプリケーションにアクセス
ここでは、SAML認証を行うためのログインボタン押下などのアクションをイメージしていただけたらと思います。
2: SAML認証要求を生成
ユーザーがアプリケーションにアクセスした際に最初に行われるのは、SP側でSAML認証要求が生成される処理です。
SAML認証要求とはSPからIdPに「今からこのユーザーでSAML認証したいので許可をください」とIdPに要求するためのデータです。 フォーマットとしてはbase64エンコードされたXML形式のデータとなります。
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2022-02-10T08:51:19Z" cacheDuration="PT604800S" entityID="{SPのEntityID}"> <md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="{IdPの認証エンドポイント}" /> <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat> <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="{SPの認証エンドポイント}" index="1" /> </md:SPSSODescriptor> </md:EntityDescriptor>
上記の中には、SSOを行いたいSPに対する固有のIDや、IdP側の情報などが含まれており、この情報をIdPに渡します。IdPではこれらの情報が想定する情報かどうかを検証することになります。
SAML認証要求をIdPに渡すやり方は単純で、下記のようにIdPの認証エンドポイントに対して「SAMLRequest」というキー名のクエリパラメータをつけてリダイレクトすればOKです。
https://{idp_host}/{path}?SAMLRequest=xxxxxxxxxxxxx
3,4: IdPへのリダイレクト
上記にて発行されたURLにリダイレクトします。リダイレクトすると、次はIdP側での処理が始まります。
5: SAML認証要求を受け取り、認証を試みる
IdP側では最初にSAML認証要求を受け取り、想定しているSPからのリクエストかどうかを検証します。 検証といっても、ここではXML内のEntityIDなどを突合するくらいの内容になります。
その後もしIdPにログインしていなければログイン画面を表示し、ユーザーにIdPへのログインを促します。
6,7,8: ユーザー認証
もしIdPにログインしていなければ、このタイミングでログインを行います。
ID・パスワードを入力しつつ、IdPによっては2要素認証が導入されていたり、クライアント証明書の有無によるIdPへのアクセス制御などの機能もあるかと思います。
9: SAML認証応答を作成
IdPにて認証が済んでいることが確認できると、IdPではSAML認証応答を生成します。
SAML認証応答とは、IdPからSPに「IdP側の認証はOKなので、その証明を送るよ」といった形で、SP側で認証情報として取り扱える情報です。 こちらもSAML認証要求と同様、base64エンコードされたXML形式のデータです。
<samlp:Response ID="{SessionIndex}" Version="2.0" IssueInstant="2022-07-14T08:50:33Z" Destination="{SPの認証エンドポイント}" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified" InResponseTo="xxx" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"> <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">{Issuer}</Issuer> <samlp:Status> <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /> </samlp:Status> <Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="{SessionIndex}" IssueInstant="2022-07-14T08:50:33Z" Version="2.0"> <Issuer>{Issuer}</Issuer> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> {署名の内容} </ds:Signature> <Subject> <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:xxx">{NameID}</NameID> <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> <SubjectConfirmationData InResponseTo="xxx" NotOnOrAfter="2022-07-14T08:53:33Z" Recipient="{SPの認証エンドポイント}"></SubjectConfirmationData> </SubjectConfirmation> </Subject> <Conditions NotBefore="2022-07-14T08:50:28Z" NotOnOrAfter="2022-07-14T09:50:33Z"> <AudienceRestriction> <Audience>{SPのEntityID}</Audience> </AudienceRestriction> </Conditions> <AttributeStatement> {IdPから取得できる属性情報} </AttributeStatement> <AuthnStatement AuthnInstant="2022-07-14T08:50:33Z" SessionIndex="{SessionIndex}"> <AuthnContext> <AuthnContextClassRef>urn:federation:authentication:windows</AuthnContextClassRef> </AuthnContext> </AuthnStatement> </Assertion> </samlp:Response>
10: SPへの302 HTTP POST Binding
IdPからSPにSAML応答の情報を渡すには、SP側で用意している認証エンドポイントに対しHTTP POST Bindingし、そのリクエストボディにSAMLResponseというキー名で指定し送信します。
ちなみにこの時HTTP POST BindingとなるかはSPから送るSAML認証要求の内容によって決定されます。
# この辺り <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location=
Safie Managerは執筆時点ではHTTP POST Bindingのみ対応しています。
11,12,13: SAML認証応答を検証し、ユーザーログインを許可
SPには上記フォーマットのSAML認証応答が送られ、メールアドレスやユーザー名などの属性情報も含まれていますが、これらの情報をそのまま使用するわけにはいきません。 不正に変更されたSAML認証応答が送られてくる場合も考えられるためです。
SAML認証応答の中には署名の情報があるのですが、この署名情報を検証することで意図したIdPからの認証応答かどうかを確かめます。 検証するには事前にIdPよりダウンロードできる公開鍵が必要になりますので、この公開鍵をSPにあらかじめ登録しておくことが必要です。
14: ログイン成功
検証が成功すると晴れてSAML認証成功となります!
この後は、SAML認証応答から取得できる情報を元に、SPにログインするための処理へと続いていくこととなります。
元々あるSPの認証基盤に合わせてSAML認証を組み込んでいくことになり、各SPによってIdPから取得しなければいけない属性情報に違いが出てくるため、SPごとに実装内容は分かれてくるところになるかと思います。
Safie ManagerとSSO
SAML認証を行うには事前にSP・IdPそれぞれに認証エンドポイントや公開鍵などを登録し合うことが必要と書きましたが、私たちが提供しているサービス「Safie Manager」ではどのような画面になっているのでしょうか。 見てみたいと思います。
ほとんど黒で塗りつぶしてしまっていてすみません。
「IdPへの登録情報」という欄に書かれている情報を、IdPに登録するための情報として表示してあります。
逆に、IdP側の情報をSafieManagerに登録する欄として「IdP情報」という項目を設けてありますので、「ログインページのURL」「識別子のURL」「証明書」を登録することで、SafieManager(SP)側の準備は完了です。
今度はIdP側の情報を登録する箇所を見てみましょう。 ここでは例としてAzure Active Directoryを取り扱います。
基本的なSAML構成という箇所にSPの情報を入力し、
{アプリ名}のセットアップ(ここではsafie-manager-sso-testという名前)にSP側に登録する情報が乗っており、証明書もダウンロードできます。
まとめ
今回はSAML認証についての解説をさせていただきました。
SAML認証はかなり複雑な内容で、実装する際にはまず概念の理解に時間がかかってしまうため、この記事が理解の助けになれば幸いです。
サービスにSSO機能を組み込む際は、SAML認証を経た後、SP側のユーザーデータに対して新規登録や認証を行う処理につながってきます。その辺りはSPの既存の認証基盤の仕様によって実装の勘所が分かれる箇所だと思っています。その辺りにも試行錯誤ポイントがあるため、SSO機能の実装を予定されている方は、基本設計・詳細設計の時間を長めに取られることをおすすめします。
ここまで読んでいただきありがとうございました!