PHPでSAML認証を実装する(SimpleSAMLphp)

前回のPHP環境構築はこれのため、以下の環境で実装する。
https://trueman-developer.blogspot.com/2019/02/vagrantphp.html




環境


Vagrant
CentOS 7.0
PHP7.2


SAMLについて



雑に説明するとシングルサインオンの共通仕様。
フォーマットはXML。
ログイン専用のサービスを介して様々なサービスで同じ認証情報を使い回す。

アクセスされた時に認証されていない場合idPに認証を丸投げ。
SPは認証後各種情報を受け取る。
ドメインが異なっても認証情報は引き継がれる。


SAMLの用語について



Identity Provider(IdP)


認証するサーバ。
ID,PASSなどを受け取り認証情報を返す。
サービスとして利用するケースが多い。

Service Provider (SP)


認証情報を利用する。
要するに自社システム。

アサーション


認証情報
SPが受け取り利用する。



SimpleSAMLphpについて



SimpleSAMLphpを使用することでSPだけでなくIdPも構築できるようだ。
https://simplesamlphp.org/
開発時Idpが用意されているとは限らないのでIdPも構築して認証するまで実装する。

もしSP側(自社システム)をSAML対応させるだけの場合は
・アクセスがあった時に認証要求用のメッセージ(XML)作成、送信。
・認証メッセージを受け取り、情報を取り出す。
理論上上記だけで良い(いろいろハマりどころがあるらしい)

今回はVagrant上でPHP環境を構築している。
https://trueman-developer.blogspot.com/2019/02/vagrantphp.html
恐らくphp-xmlとphp-zip1を導入しておけばいけるはず。


IdPを実装する場合もSPを実装する場合も以下からSimpleSAMLphpをダウンロードする。
https://simplesamlphp.org/download




SimpleSAMLphpでIdPを実装する



展開した
simplesamlphp-1.16.3のwwwフォルダをsimplesamlphpにリネームしてホストに配置する。
フォルダ構成の例は以下

.vagrant
html
simplesamlphp
Vagrantfile


ApacheのAliasを設定してSimpleSAMLphpの管理画面を表示する


simplesamlphp以下のwwwフォルダを
ApacheのAliasに設定する。

su
vi /etc/httpd/conf/httpd.conf
/Alias → エンターで検索

Alias /simplesaml /vagrant/simplesamlphp/www

service httpd restart

ホストのVagrantfileがあるディレクトリにhtml, simplesamlphpディレクトリを配置している。
http://192.168.33.10/simplesaml にアクセスされた時にsimplesamlphp/www が実行される
はずなのだが

You don't have permission to access /simplesaml on this server
が発生する。

chown -R apache:apache /vagrant/simplesamlphp
sudo chmod 777 -R /vagrant/simplesamlphp/www/
どちらもうまくいかない。


https://stackoverflow.com/questions/23337446/getting-a-403-forbidden-error-for-simplesaml-after-apache-upgrade
設定方法が変わったらしい

以下はVagrant上(PHP実行環境)での作業。

vi /etc/httpd/conf.d/hosts.conf

hosts.confを以下のように編集する。
<VirtualHost *:80>
ServerName test.test
DocumentRoot /var/www/html

<Directory /var/www/html>
Require all granted
</Directory>

Alias /simplesaml /vagrant/simplesamlphp/www

<Directory /vagrant/simplesamlphp/www/>
Require all granted
</Directory>

</VirtualHost>

ServerName自体はなんでもいい。

service httpd restart


以上でwebsite/simplesamlにアクセスした時に、/vagrant/simplesamlphp/wwwつまりホスト側のsimplesamlphp/wwwを見にいくようになる。

http://192.168.33.10/simplesaml にアクセスした時に
http://192.168.33.10/simplesaml/module.php/core/frontpage_welcome.php にリダイレクトされて
SimpleSAMLphp設定ページ が表示されたらOK(ちなみに上記設定だけだとServerNameが効いていない)


ログインパスワード設定


デフォルトは123だが変更しないとログインできない。

simplesamlphp/config/config.php を編集する。

'auth.adminpassword' => '123',
を任意の値に変更

'enable.saml20-idp' => false,

をtrueに変更。
ちなみに直下の
'enable.shib13-idp' => false,
はシボレスという認証形式になる。今回はやらない。

変更したら「管理者からログイン」から試してみる。


認証を有効化するためにenableファイルを作成
touch simplesamlphp/modules/exampleauth/enable
空ファイルを作成するだけでいいようだ。

simplesamlphp/config/authsources.php を書き換え。

コメントアウトされているexample-userpassのコメントを解除する。
これで単純なIDとPASSにより認証ができるようになる。


証明書作成


https://www.geotrust.co.jp/support/ssl/csr/apache_openssl_new.html

simplesamlphp/cert 以下に証明書ファイルを配置する。

cd /simplesamlphp/cert
openssl req -newkey rsa:2048 -new -x509 -days 3652 -nodes -subj "/C=JP/ST=Tokyo/CN=test.test" -out server.crt -keyout server.pem


simplesamlphp/metadata/saml20-idp-hosted.php
を出力した証明書ファイルに設定(server.crt, server.pemにしているなら不要)
ちなみにここでexample-userpassを使用するという設定をしているようだ。



IdPの指定

/simplesamlphp/config/authsources.php
'default-sp' => array(
'idp' => 'http://192.168.33.10/simplesaml/saml2/idp/metadata.php',

URLの確認方法はSimpleSAMLphp設定ページ → 連携 → SAML 2.0 IdPメタデータ → メタデータを表示 から取得できる。


IdP連携設定

SPの情報をIdPに設定する。
今回SPとIdPが同一なので混同しないように注意。
SAML 2.0 SPメタデータ(default-sp) → メタデータ表示 → 表示された一番下のPHPコードをコピー


$metadata['http://192.168.33.11/simplesaml/module.php/saml/sp/metadata.php/default-sp'] = array (
 'SingleLogoutService' => 
 array (
   0 => 
   array (
  'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
  'Location' => 'http://192.168.33.11/simplesaml/module.php/saml/sp/saml2-logout.php/default-sp',
   ),
 ),
 'AssertionConsumerService' => 
 array (
   0 => 
   array (
  'index' => 0,
  'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
  'Location' => 'http://192.168.33.11/simplesaml/module.php/saml/sp/saml2-acs.php/default-sp',
   ),
   1 => 
   array (
  'index' => 1,
  'Binding' => 'urn:oasis:names:tc:SAML:1.0:profiles:browser-post',
  'Location' => 'http://192.168.33.11/simplesaml/module.php/saml/sp/saml1-acs.php/default-sp',
   ),
   2 => 
   array (
  'index' => 2,
  'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
  'Location' => 'http://192.168.33.11/simplesaml/module.php/saml/sp/saml2-acs.php/default-sp',
   ),
   3 => 
   array (
  'index' => 3,
  'Binding' => 'urn:oasis:names:tc:SAML:1.0:profiles:artifact-01',
  'Location' => 'http://192.168.33.11/simplesaml/module.php/saml/sp/saml1-acs.php/default-sp/artifact',
   ),
 ),
  );

simplesamlphp/metadata/saml20-sp-remote.php に貼り付け。
管理画面の連携画面に表示されたらOK.




以上でIdPとしてサーバが機能するようになる。
用途としてはテストくらいで自分で実装することはあまりないかもしれない。





SimpleSAMLphpでSPを実装する



SPだけでいいなら以下の手順だけでいい。


SP側連携設定


IdPの情報を取得

IdPによって変わる。SimpleSAMLPHPの例。
SimpleSAMLphp設定ページ → 連携
http://192.168.33.10/simplesaml/module.php/core/frontpage_federation.php


SAML 2.0 IdPメタデータ → メタデータ表示 → 表示された一番下のPHPコードをコピー
$metadata['http://192.168.33.11/simplesaml/saml2/idp/metadata.php'] = array (
'metadata-set' => 'saml20-idp-remote',
'entityid' => 'http://192.168.33.11/simplesaml/saml2/idp/metadata.php',
'SingleSignOnService' =>
array (
0 =>
array (
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
'Location' => 'http://192.168.33.11/simplesaml/saml2/idp/SSOService.php',
),
),
'SingleLogoutService' =>
array (
0 =>
array (
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
'Location' => 'http://192.168.33.11/simplesaml/saml2/idp/SingleLogoutService.php',
),
),
'certData' => 'MIIDeTCCAmGgAwIBAgIJAO9A91A+/kZ8Ci1ZmNbdPmOrquI4eTI=',
'NameIDFormat' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
);

ldpメタデータは今回同じところから取得しているが基本的にldpサーバからxmlなどでメタデータを受け取って登録することになる。

simplesamlphp/metadata/saml20-ldp-remote.php に貼り付け。
既存の$metadataは削除かコメントアウト。
spの情報はxxxx-sp-remote.phpにldpの情報はxxxx-ldp-remote.phpに記述すると覚えておけば良い。

連携画面に追加されていたら成功。




認証チェック


http://192.168.33.10/simplesaml/module.php/core/frontpage_auth.php
SimpleSAMLphp設定ページ → 認証 → 設定されている認証元をテスト から
default-sp
→ simplesamlphp/config/authsources.php のexample-userpassからstudent:studentpass を入力してみる。
認証に成功して以下のような画面が表示されることを確認する。

SimpleSAMLphp 認証チェック


エラーになる場合,saml20-ldp-remote.phpとsaml20-sp-remote.phpが逆になっていないか、どこかでshib13を誤って設定していないか確認する。




テストサーバを立ち上げてログインする



ごく単純な画面からシングルサインオンを確認する。


html/test.php



<?php
require_once('../simplesamlphp/lib/_autoload.php');

$auth = new SimpleSAML_Auth_Simple('default-sp');
$auth->requireAuth();
$attributes = $auth->getAttributes();

print_r($attributes);
echo ($attributes["uid"][0]);
echo ($attributes["eduPersonAffiliation"][0]);
echo ($attributes["eduPersonAffiliation"][1]);

echo "<div><a href='http://192.168.33.11/logout.php'>ログアウト</a></div>";


require_onceでsimplesamlphpの_autoload.phpを指定する。
$auth->requireAuth(); で未認証の場合は認証画面へリダイレクト。
$attributes の中には以下のような認証情報が入っている。
今回のケースだとsimplesamlphp/config/authsources.php のdefault-sp['example-userpass'] が認証情報になる。
studentとemployeeそれぞれで認証して値が変わることを確認する。

Array ( [uid] => Array ( [0] => test ) [eduPersonAffiliation] => Array ( [0] => member [1] => student ) )

上記のように表示されたら成功


ログアウト処理



html/logout.php

<?php
require_once('../simplesamlphp/lib/_autoload.php');

$auth = new SimpleSAML_Auth_Simple('default-sp');

$auth->logout('http://192.168.33.11/logout.html');


html/logout.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>ログアウト</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
    <span>ログアウトしました</span>
</body>
</html>


$auth->logout ではログアウト後のURLを指定する必要がある。

使用可能なAPI一覧は以下
https://simplesamlphp.org/docs/1.7/simplesamlphp-sp-api




参考



https://simplesamlphp.org/docs/stable/
https://blog.cybozu.io/entry/4224
https://qiita.com/akihira207/items/cd904a54daa79aeff650
https://auth0.com/blog/jp-how-saml-authentication-works/
http://siblog.seiwatec.co.jp/?p=344
https://qiita.com/haya43/items/c74d2710cd9b57d2cbb4

2019年2月7日木曜日