AWS Directory Service(Simple AD)とWebアプリケーションをSAML認証で連携する

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

社内で使われるWebアプリケーションを作成する場合、そのWebアプリケーション用のユーザー情報を新規に作成します。一方で企業は既にActive Directoryのようなディレクトリサービスを使いユーザー情報を管理しています。新しく作成したWebアプリケーションでもActive Directoryで管理しているユーザー情報を使い、シングルサインオンができればアカウント管理が非常に簡単になります。
今回はAWS Directory ServiceのSimple ADとAWS上にActive Directory Federation Service(ADFS)を構築してユーザー情報の管理、認証を行い、認証成功後はWebアプリケーションに連携させてみます。 認証情報の連携にはSAML認証を使います。

SAML連携

SAML(Security Assertion Markup Language)

SAMLはインターネット上で、IDやパスワードなどの認証情報を連携するためのXMLベースの仕様です。

SAMLは主にエンタープライズアプリケーション間の認証で使われています。最近ではクラウドの発展に伴いクラウド上のWebサービスとの連携でも使われています。社内ネットワークにあるADFSの認証情報を使い、Office365やGoogle Appsのようなクラウドサービスを使うときに使われています。

SAML仕様の詳細について知りたい方は以下のページを参照してください(かなりボリューム大きく複雑です)
en.wikipedia.org/wiki/SAML2.0

SAMLではADFSのように認証情報を提供する側をIdentity Provider(IdP)、認証情報を利用する側(今回はWebアプリケーション)をService Provider(SP)と呼びます。

今回、試すSAML連携のシーケンスは次のようになります

SP initiated SSO

  1. ユーザーがWebアプリケーション(Service Provider)にアクセスします
  2. WebアプリケーションがADFSへの認証要求メッセージを作成します
  3. ユーザーが認証要求メッセージ(SAMLRequest)を受け取りADFS(Identity Provider)に送ります(WebアプリケーションからのHttp Redirect)
  4. ADFSはユーザー認証を行う
  5. ADFSはWebアプリケーションへの認証応答メッセージ(SAMLResponse)を生成します
  6. ユーザーは認証応答メッセージをWebアプリケーションに送ります(ADFSからのHttp Post)
  7. Webアプリケーションは認証応答メッセージを受け取りメッセージの検証をします
  8. ユーザーはWebアプリケーションにログインします

上記のフローで認証するシーケンスをSP initiated SSOと呼びます。このシーケンスの場合、IdPとSPが直接通信できない場合でも認証情報を連携できるメリットがあります(IdPが社内ネットワークにある場合など)。

なお、認証要求メッセージ、認証メッセージの送信に利用するSAML Bindingを決める必要があるのですが、今回は以下のようにしました。

  • SPからIdPへのメッセージ送信:HTTP Redirect Binding
  • IdPからSPへのメッセージ送信:HTTP POST Binding

環境構築

AWS Simple ADの構築はこのページを参考にしてください
AWS Directory ServiceでActive Directoryを立ててWindowsを参加させる

ADFSの設定

Windows Serverに事前にADFSをインストールしてください。

1「Administrative Tools」から「AD FS Management」を起動し、「Add Relying Party Trust Wizard」を起動します。

ADFS-2

2 Select Data Sourceで「import data about the relying party from a file」を選択し、この先の手順の「信頼関係の構築」で作成したSP情報を設定したXMLファイルを選択します。

ADFS-3

3 そのまま指示に従い必要な情報を入力、Choose Issuance Authorization Rulesでは「Permit all users to access this relying party」を選択し設定を完了してください。

4 「Edit Claim Rules for ***」ダイアログが表示されるので「Add Rule」をクリック、Claim rule templateで「Send LDAP Attributes ad Claims」を選択する

5 Claim Rule nameに好きなルール名を入力、Attribute storeに「Active Directory」を選択、Mapping of LDAP attributes to outgoing claim typesのLDAP Attributeに「SAM-Account-Name」、Outgoing Claim Typeに「Name ID」を選択して設定を完了してください。

ADFS-4

6 ADFS Manager左ペインの「サービス > Certificates」を選択し、中央ペインの「Token-signing」を右クリックし「View Certificate」を選択する

ADFS-5

7 Certificateダイアログの「Details」タブから「Copy to File」をクリックし「Certificate Export Wizard」を表示します

8 Export File FormatのSelect the format you want to useで「DER encoded binary X.509(.CER)」を選択します

9 ファイル名を入力し、設定を完了すると公開鍵証明書が保存されます。この鍵情報をつかってSPはIdPから送信されたSAMLResponseのSignatureを検証します。

ADFS 対応ブラウザの設定

ADFSでは初期設定ではIEしか対応していません。Firefox, Chrome, Safariでも認証できるようにADFSを構築したWindows Server上で以下のコマンドを投入してください。

(Get-AdfsProperties).WIASupportedUserAgents
$old=(Get-AdfsProperties).WIASupportedUserAgents
$new=$old+"Mozilla/5.0/"
$new
Set-AdfsProperties -WIASupportedUserAgents $new
(Get-AdfsProperties).WIASupportedUserAgents

信頼関係の構築

SAML連携をするには、事前にIdP(今回はADFS)とSP(今回はWebアプリケーション)間で信頼関係を構築する必要があります。
IdPにSPの情報を登録します。IdPがADFSの場合、手動で入力することもmetadata.xmlを読み込ませることもできます。今回はSP情報を設定したXMLを作成しました。

<md:EntityDescriptor
  xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
  xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
  entityID="https://saml.example.com">
  <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
    <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="https://saml.example.com/acs" 
      index="0"/>
  </md:SPSSODescriptor>
</md:EntityDescriptor>

以下、重要な要素・属性の説明です。

要素・属性 説明
EntityDescriptor entityID SPのURL
AssertionConsumerService binding SPがIdPから認証応答メッセージを受け取る際のSAML Bindigの方式
AssertionConsumerService Location SPがIdPから認証応答メッセージを受け取るエンドポイント

ここで作成したXMLは環境構築のADFSの設定時に使用します。

認証連携機能の実装

SAMLRequest(認証要求メッセージ)

今回のアプリケーションで使った認証要求メッセージは以下のXMLです

<samlp:AuthnRequest
    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
    ID="aaf23196-1773-2113-474a-fe114412ab72"
    Version="2.0"
    IssueInstant="2015-09-28T00:00:00Z"
    AssertionConsumerServiceURL="https://saml.example.com/acs"
    ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST">
    <saml:Issuer>https://saml.example.com</saml:Issuer>
    <samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" />
  </samlp:AuthnRequest>

リクエストに指定する要素・属性は各アプリケーション毎に必要なものを設定する必要があります。 以下重要な要素・属性の説明です。

要素・属性 説明
ID メッセージ毎のユニークなID
AssertionConsumerServiceURL IdPが認証応答メッセージを返すSPのエンドポイント
ProtocolBinding 認証応答メッセージを受け取る際のSAML Bindingの方式

SAMLRequestはDeflateエンコード、Base64エンコード、URLエンコードの順序でエンコードして、IdPのSSOエンドポイントにHTTP Redirectで送信します。
URLは以下のようになります。
「 https://idp.sample.com/adfs/ls?SAMLRequest=<エンコードしたXML> 」

SAMLResponse(認証応答メッセージ)

ユーザー認証に成功すると、IdPはSPに認証応答メッセージを送信します。 IdPはSAML ResponseをBase64エンコード、URLエンコードの順序でエンコードするので、受信するSPのプログラムでは逆の順序でデコードする必要があります。
SAML Responseの内容は大きいのでここには書きませんが、アプリケーション毎に必要な項目の検証を行います。

  • samlp:StatusCode要素のValue属性:認証に成功しているかを確認する
  • ds:SignatureValue要素の値:署名の検証、IdPの公開鍵をつかって検証する

応答メッセージを確認し問題がなければ、Webアプリケーションにログインします。
ここまでの設定でSAML認証でADFSとWebアプリケーションを連携させることができるようになります。

検証

1 Webアプリケーションにアクセスします

SAML-1

2 ADFSのページにリダイレクトされるので認証情報を入力します(ユーザー名はドメインも指定する必要があります)

SAML-2

3 認証に成功するとWebアプリケーションにSAML ResponseがPostされるので、メッセージを検証し問題なければアプリケーションにログインします

サンプルコード

今回の検証で使用したプログラムです。言語はRuby、WebアプリケーションFWにはSinatraを使用しました。

require 'sinatra/base'
require 'zlib'
require 'base64'
require 'cgi'

class MyApp < Sinatra::Base

  # SAML連携のスタート SAML Requestを作成しIdPに送信する
  get '/saml/start' do
    # IdPに送信するXMLデータ
    param=<<EOS
<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="szqd0c3d0u3vpz5jwna4p24iso42opc4" Version="2.0" IssueInstant="2015-09-28T00:00:00Z" AssertionConsumerServiceURL="https://saml.example.com/acs" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"><saml:Issuer>https://saml.example.com</saml:Issuer><samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" /></samlp:AuthnRequest>
EOS

    # 送信するXMLはデフレートエンコード、Base64エンコード、URLエンコードの順序でエンコードする
    deflated = Zlib::Deflate.deflate(param, 9)[2..-5]
    encoded = Base64.encode64(deflated).gsub(/\n/, "")
    saml_req = CGI.escape(encoded)

    redirect to("https://idp.example.com/adfs/ls?SAMLRequest=#{saml_req}")
  end

  # IdPからの認証応答データを受け取るSPのエンドポイント
  post '/saml/acs' do
    # データのデコードを行う
    decoded = Base64.decode64(params[:SAMLResponse])
    
    # ここで認証応答メッセージの各要素、属性の検証をする
    # SignitureをIdPの公開鍵をつかって検証する
    # 全ての検証が終わったら、IdPから渡されたユーザーの必要なログイン処理(sessionデータの作成など)を行う
        
  end
end

run MyApp

まとめ

WebアプリケーションはBtoCで使われることが多いので、ID連携というとOAuthやOpenIDなどの認証方式がよく使われています。一方、今回のように社内アプリケーションもしくはBtoBアプリケーションとしてRailsなどでWebアプリケーションを作る場合は、SAML認証を利用して既存のアカウント情報を利用したほうがアカウント管理の面でも簡単ですし認証機能の実装もほぼ行わなくて良いのでメリットが多いです。
AWS上のディレクトリサービス(Simple AD)を使うことについても、企業がActive Directoryの構築、運用をする手間が省けるので、運用コストという面でメリットがあります。
もしアカウント管理の集約、Active Directoryの運用コストの削減などを考えているのであれば、今回のSAML認証を検討してみてください。

参考サイト

SAML2.0
強力なSSOを実現するXML認証・認可サービス(SAML) (1/2)
基礎から分かるActive Directory再入門(10): Active Directoryとクラウドの連携
SAML Specifications
SAML認証ができるまで