シングルサインオン(SSO)
シングルサインオン(SSO)は、複数の異なるシステムやアプリケーションに対して、一度の認証を行うことで、ユーザーがそれらのシステムやアプリケーションにシームレスにアクセスできる仕組みとなる。これにより、ユーザーは複数のシステムに別々にログインする必要がなくなり、一度のログイン情報を使って、複数のシステムやアプリケーションにアクセスできるため、ユーザーの利便性が向上する。
SSOの仕組みは、通常、認証を担当する中央のシステム(認証サーバー)を介して行われる。ユーザーが最初に認証サーバーにログインすると、認証サーバーはユーザーを認証し、認証情報を発行する。その後、ユーザーは認証情報を使用して、複数のシステムやアプリケーションにアクセスすることができる。各システムやアプリケーションは、認証サーバーに認証を委任し、ユーザーの認証状態を確認するものとなる。
SSOの利点は、ユーザーの利便性向上、パスワードの管理簡素化、セキュリティの向上などがある。一度のログインで複数のシステムにアクセスできるため、ユーザーは複数のパスワードを覚える必要がなくなる。また、認証情報が一元的に管理されるため、セキュリティの向上やアクセス権の一元管理が可能になる。一方で、適切なセキュリティ対策を施さない場合には、一つの認証情報が漏洩した場合に複数のシステムにアクセスされるリスクもある。
SAML(Security Assertion Markup Language)
SAML(Security Assertion Markup Language)は、ウェブベースのSSOを実現するためのXMLベースの標準的な認証および認可フレームワークとなる。SAMLは、企業や組織が異なるドメイン間でユーザーの認証情報を共有するためのセキュリティ標準として使用されている。
SAML認証は、以下の主要なコンポーネントから構成されている。
-
サービスプロバイダ(SP): ユーザーがアクセスしようとしているアプリケーションやサービスを提供するプロバイダ。
-
イデンティティプロバイダ(IdP): ユーザーの認証情報を管理し、ユーザーが正当なユーザーであることを確認するプロバイダ。IdPは、ユーザーを認証し、SAMLアサーションと呼ばれるXMLドキュメントを生成してSPに送信する。
-
ユーザー: アクセスしようとしているアプリケーションやサービスを利用するユーザー。
SAML認証のフローは以下のようになる。
-
ユーザーがSPにアクセスしようとすると、SPはユーザーをIdPにリダイレクトする。
-
IdPはユーザーを認証し、SAMLアサーションを生成する。
-
IdPは生成されたSAMLアサーションをSPに送信する。
-
SPはSAMLアサーションを検証し、ユーザーを正当なユーザーとして認証する。
-
以上により、ユーザーはSPのアプリケーションやサービスにアクセスできるようになる。
SAMLには2つのパターンの認証の流れがある。(シングルサインオンでよく聞くSAML認証とは? 仕組みの概要、導入のメリットを解説!より)
- SPを起点とするSP Initiatedの場合
-
- ユーザーがSPにアクセス
- SPがSAML認証要求を作成してユーザーに応答
- ユーザーはSPから受け取ったSAML認証要求をIdPに送信
- IdPの認証画面が表示される
- ユーザーはログインIDとパスワードを入力してIdPとの間で認証処理を行なう
- 認証が成功したらIdPからSAML認証応答が発行される
- ユーザーはIdPから受け取ったSAML認証応答をSPに送信
- SPにSAML認証応答が届くとログインできる
- IdPを起点とするIdP Initiatedの場合
-
- ユーザーがIdPにアクセス
- IdPの認証画面が表示される
- ユーザーはログインIDとパスワードを入力してIdPとの間で認証処理を行なう
- 認証が成功したらIdPにログインできる
- IdPの画面からアプリケーションのアイコン(対象のSP)を選択
- IdP側でSAML認証応答が発行される
- ユーザーはIdPから受け取ったSAML認証応答をSPに送信
- SPにSAML認証応答が届くとログインできる
PythonによるSAMLの実装例
Pythonを使用してSAMLを実装するには、 python3-saml
ライブラリを使用することができる。以下は、 python3-saml
ライブラリを使用してSAML認証を実装する方法の概要となる。
-
python3-saml
ライブラリをインストールする。これは、pip install python3-saml
を実行してインストールできる。 -
SP (Service Provider) 用の設定を
settings.py
ファイルに追加する。これには、SPのエンティティID、証明書、プライベートキーなどが含まれる。
from saml2 import BINDING_HTTP_POST
from saml2 import entity
from saml2.config import Config
from saml2.sigver import get_xmlsec_binary
BASEDIR = "/path/to/saml/files"
config = Config()
config.load_file(f"{BASEDIR}/sp_conf.py")
sp = entity.SP(config=config)
- IdP (Identity Provider) からのSAMLレスポンスを受信するエンドポイントを作成する。
from flask import Flask
from flask import redirect
from flask import request
from flask import session
app = Flask(__name__)
app.secret_key = "secret_key"
@app.route("/saml/sso", methods=["POST"])
def saml_auth():
authn_response = request.form["SAMLResponse"]
# SAMLレスポンスの検証
if sp.is_valid_response(authn_response, [BINDING_HTTP_POST]):
# レスポンスが有効な場合の処理
session["name_id"] = sp.get_nameid(authn_response)
return redirect("/")
else:
# レスポンスが無効な場合の処理
return "Invalid SAML response"
- SAML認証リクエストを生成して、IdPにリダイレクトする。
@app.route("/login")
def login():
reqid, info = sp.prepare_for_authenticate()
session["reqid"] = reqid
redirect_url = None
for key, value in info["headers"]:
if key == "Location":
redirect_url = value
break
return redirect(redirect_url)
実際のアプリケーションでSAMLを使用する場合は、さまざまなセキュリティ上の考慮事項に留意する必要があるため、十分なテストとセキュリティの対策が必要となる。
ClojureでのSAMLの実装
ClojureでのSAMLの実装には、 clj-saml
ライブラリを使用することができる。以下は、 clj-saml
ライブラリを使用してSAML認証を実装する方法の概要となる。
clj-saml
ライブラリをプロジェクトに依存関係として追加する。project.clj
ファイルに以下の依存関係を追加することで行える。
[clj-saml "0.5.0"]
- SP (Service Provider) 用の設定を作成する。これには、SPのエンティティID、証明書、プライベートキーなどが含まれる。
(ns myapp.saml
(:require [clj-saml.core :as saml]))
(def sp-settings
{:entity-id "https://sp.example.com/metadata"
:private-key "/path/to/sp-key.pem"
:certificate "/path/to/sp-cert.pem"
:acs-url "https://sp.example.com/acs"
:single-logout-url "https://sp.example.com/logout"
:nameid-format "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
:force-authn false})
- IdP (Identity Provider) からのSAMLレスポンスを受信するエンドポイントを作成する。
(ns myapp.routes
(:require [clj-saml.core :as saml]
[ring.util.response :refer [redirect]]
[ring.util.session :refer [update-session! get-session]]))
(defn saml-auth [request]
(let [authn-response (-> request :params :SAMLResponse)
session (get-session request)]
;; SAMLレスポンスの検証
(if (saml/valid-response? sp-settings authn-response)
;; レスポンスが有効な場合の処理
(do (update-session! session assoc :name-id (saml/get-nameid authn-response))
(redirect "/"))
;; レスポンスが無効な場合の処理
(str "Invalid SAML response"))))
- SAML認証リクエストを生成して、IdPにリダイレクトする。
(ns myapp.routes
(:require [clj-saml.core :as saml]
[ring.util.response :refer [redirect]]
[ring.util.session :refer [update-session! get-session]]))
(defn saml-login [request]
(let [session (get-session request)
authn-request (saml/authn-request sp-settings)]
;; セッションにAuthnRequestを保存
(update-session! session assoc :authn-request authn-request)
;; IdPにリダイレクト
(redirect (saml/get-authn-request-url sp-settings authn-request))))
OAuth(Open Authorization)
OAuth(Open Authorization)は、異なるアプリケーション間での認可を実現するためのオープンスタンダードのプロトコルとなる。OAuthは、ユーザーがあるサービス(リソースオーナー)のリソースに対して、別のサービス(クライアント)が代理アクセスするための認可を取得するために使用される。
一般的に、OAuthはWeb APIなどのリソースを保護し、他のアプリケーションに対して安全な方法でアクセスを許可するために使用される。例えば、ユーザーが自分のTwitterアカウントを使ってあるアプリケーションにログインする際に、そのアプリケーションがユーザーのTwitterアカウントに代理アクセスするための認可を得る場合にOAuthが使用される。
OAuthは、認可プロセスを通じてアクセストークンというトークンを発行し、これを使ってクライアントがリソースオーナーのリソースにアクセスすることができる。OAuthには、バージョン1.0および2.0の2つのメジャーバージョンがあり、多くのプラットフォームで広く使用されている。
OAuthの主な利点は、ユーザーの認証情報を第三者アプリケーションと共有することなく、セキュアな方法でリソースにアクセスを許可できる点で、権限を制御するための細かいスコープ設定が可能であり、ユーザーのプライバシーを保護するための強力な仕組みを提供するポイントとなる。
Pythonでの実装例
PythonでOAuthの実装には、requests-oauthlib
ライブラリを使用することができる。以下に、requests-oauthlib
ライブラリを使用してOAuth認証を実装するための基本的な手順を示す。
requests-oauthlib
ライブラリをインストールする。
pip install requests requests-oauthlib
-
OAuth認証に必要な情報を取得する。これには、OAuthサービスプロバイダーから提供されるクライアントID、クライアントシークレット、承認エンドポイント、アクセストークンエンドポイント、リダイレクトURLなどが含まれる。
-
OAuth2Session
オブジェクトを作成する。これには、client_id
、client_secret
、redirect_uri
、scope
などが含まれる。
from requests_oauthlib import OAuth2Session
client_id = 'your-client-id'
client_secret = 'your-client-secret'
redirect_uri = 'http://localhost:8000/callback'
scope = ['scope1', 'scope2']
oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scope)
- 承認URLを取得して、ユーザーをOAuthサービスプロバイダーの認証画面にリダイレクトする。
authorization_url, state = oauth.authorization_url(authorization_endpoint)
print('Please go to %s and authorize access.' % authorization_url)
authorization_response = input('Enter the full callback URL: ')
- 認証画面からリダイレクトされたコールバックURLから、アクセストークンを取得する。
token = oauth.fetch_token(
token_endpoint,
authorization_response=authorization_response,
client_secret=client_secret
)
実際には、各OAuthサービスプロバイダーによって異なる仕様があるため、より詳細な実装が必要になる場合がある。
Clojureでの実装
以前具体的なAuthの実装としては”マイクロサービスでのセキュリティ- ClojureでのAuthとPedestalを使ったAPI“にてClojureのPedestalを用いたものについて述べた。ここではもう少しシンプルな形態について述べる。
ClojureでOAuthの実装には、 clj-oauth
ライブラリを使用することができる。以下は、clj-oauth
ライブラリを使用してOAuth認証を実装するための基本的な手順となる。
clj-oauth
ライブラリを依存関係に追加します。以下の行をproject.clj
に追加する。
[clj-oauth "1.6.0"]
-
OAuth認証に必要な情報を取得する。これには、OAuthサービスプロバイダーから提供されるクライアントID、クライアントシークレット、承認エンドポイント、アクセストークンエンドポイント、リダイレクトURLなどが含まれる。
-
OAuthクライアントを作成する。これには、OAuthサービスプロバイダーから提供される情報を含め、
clj-oauth
ライブラリのoauth-client
マクロを使用する。
(require '[clj-oauth.client :as client])
(def oauth-client
(client/oauth-client
{:client-id "your-client-id"
:client-secret "your-client-secret"
:auth-url "https://oauth.example.com/authorize"
:token-url "https://oauth.example.com/token"
:redirect-uri "http://localhost:8000/callback"}))
- 承認URLを取得して、ユーザーをOAuthサービスプロバイダーの認証画面にリダイレクトする。
(require '[clj-oauth.core :as oauth])
(def authorization-url
(oauth/authorization-url
oauth-client
{:state "some-state-param"
:scope "scope1,scope2"}))
(println "Please go to " authorization-url " and authorize access.")
- 認証画面からリダイレクトされたコールバックURLから、アクセストークンを取得する。
(def access-token
(oauth/fetch-access-token
oauth-client
{:grant-type :authorization-code
:code "authorization-code"
:redirect-uri "http://localhost:8000/callback"}))
参考図書
汎用的な参考図書としては「「Auth0」で作る! 認証付きシングルページアプリケーション」等がある。
本書を用いることで、簡単なwebページ用のAuth認証の実装を生部ことができる。内容は以下のようになる。
「Auth0」で作る! 認証付きシングルページアプリケーション 土屋 貴裕
第1章 ウェブアプリケーションと認証
1.1 モノリシックなアプリケーション
1.2 モノリシックなアプリケーションとクッキー認証
1.3 モバイルアプリケーションとトークン認証
1.4 SPAと認証
1.5 モダンなアプリケーションの構成とIdP
第2章 トークンベース認証の基礎
2.1 認証と認可
2.2 OAuth2
2.3 OpenID Connect(OIDC)
第3章 JSON Web Token
3.1 JWTとは何か?
3.2 JWTの使い所
3.3 JWTの構造
3.4 暗号アルゴリズム
3.5 APIへのリクエスト
3.6 トークンの保存場所
3.7 JWTHandbook
第4章 Auth0
4.1 Auth0とは
4.2 Auth0のよい点
4.3 名寄せ
4.4 認証を丸投げする不安
4.5 Auth0を使ってみる
第5章 Nuxtで作るSPA
5.1 Nuxt.jsとは
5.2 Nuxt.jsを使ってみよう
5.3 ビルド
第6章 NuxtにAuth0を組み込む
6.1 2種類のライブラリ
6.2 Lockを組み込む
6.3 トークンを管理する
6.4 ログイン状態の判定
6.5 Auth0APIへのアクセス
第7章 NuxtとRailsを共存させる
7.1 1つのリポジトリで管理する
7.2 ディレクトリ構成の変更
7.3 Railsの構築環境
7.4 Rails New
7.5 APIを作成してみる
7.6 Nuxtの出力先を変更する
7.7 開発時のProxyを設定する
7.8 Proxyの動作確認
第8章 RailsとKnockによる認証
8.1 Knockとは
8.2 Knockの導入
8.3 鍵設定
8.4 ユーザーの作成
8.5 認証付コントローラーの作成
8.6 認証必須なAPIを直接叩いてみる
8.7 NuxtからAPIを呼び出す
第9章 プロダクションビルドとデプロイ
9.1 データベースの切り替え
9.2 プロダクションビルド
9.3 Auth0のセキュリティ設定
9.4 ソーシャルアカウントのAPIキー設定
第10章 設定のカスタマイズ
10.1 複数のソーシャルアカウントログインを許可する
10.2 パスワードログインを無効化する
10.3 メールアドレスでログイン制限をかける
10.4 名寄せを実現する
10.5 トークンを更新する
終わりに
SAMLとOAuthの違い
シングルサインオンを実現するという点ではSAML認証とOAuthは同じといえるが、両者の違いを理解するためのポイントは“認証”と“認可”の違いとなる。認証とはユーザーを証明するプロセスであり、認可はアクセスの権限を与えるプロセスとなる。SAML認証は認証と認可を行ない、OAuthは原則認証を行なわずに認可のみとなる。つまり、OAuthではAPIを利用する際に認証が行なわれていない、という点がSAML認証との大きな違いとなる。
もう少し具体的に述べると、SAMLとOAuthの主な違いは、使用目的とプロトコルの設計となる。SAMLは、異なるドメイン間での認証および認可を実現するためのXMLベースの認証プロトコルであり、SSOや組織間での信頼性のある認証を主に対象としている。一方、OAuthは、異なるアプリケーション間での認可を実現するためのプロトコルであり、Web APIなどのリソースを保護し、他のアプリケーションに対して安全な方法でアクセスを許可することを主に対象としているものとなる。
コメント
[…] シングルサインオン(SSO)とSAMLとOAuthの相違点 […]