LoginSignup
6
3

More than 3 years have passed since last update.

MySqlに保存している認証情報を用いてSimpleSAMLphpでSSOする

Last updated at Posted at 2020-10-04

Index

前提

  • ここではXAMPPを使っています、なのでデータベースはMySqlです。
  • こちらの記事の環境構築をした後に本記事を書いています。
  • SimpleSAMLphpはすでにダウンロード済でApache制御下のフォルダに配置済という想定です。
  • SimpleSAMLphpのフォルダ名をssoIdpに変更して使用しており、以下のような構成になっています。


  C:\xampp\htdocs (←Apacheの制御下のフォルダ、環境の設定によって変わる)
        └ ssoIdp(←SimpleSAMLphpのサイトからダウンロードして解凍した
              フォルダ[simplesamlphp-1.19.0-rc1]をリネームしています。)

MySqlにユーザアカウントを作成

  • phpMyAdminのコンソール画面を開きます

XAMPPのコンソールからMySQLを「Start」した後、[Admin]ボタンをクリックしてphpMyAdminのコンソール画面を開きます。
image.png

  • データベースを作成します

phpMyAdminにログイン(IDとパスワードを設定している場合は認証してログイン)して、左端カラムの[新規作成]をクリックします。
image.png

データベースの名前に「simplesaml」と入力し、[作成]ボタンをクリックします。
image.png

  • テーブルを作成します

このページ(sqlauth:sql)記載のSQLを実行すればよいです。
データベース"simplesaml"の"SQL"タブでCREATE文を入力し、[実行]ボタンをクリックします。
image.png

データベース"simplesaml"に、"users"テーブルと"usergroups"テーブルが作成されました。
image.png

  • PHPのプログラムからユーザを登録

"users"テーブルの"password"カラムの値は、『saltとpassword平文を結合した文字列にハッシュをかけたもの』です。なのでユーザの登録がちょっと面倒ですからPHPでさくっと登録用画面のプログラムを書きました、割と適当ですが。

addUserToSimpleSaml.php
<?php
class simpleSamlDb
{
    //各人のMySqlの環境に合わせて設定してください。
    const DATA_SOURCE_NAME = "mysql:dbname=simplesaml;host=localhost";
    const DB_USER_NAME = "root";
    const DB_PASSWORD = null;

    private $dbh;

    function  __construct()
    {
        try {
            $this->dbh = new PDO(self::DATA_SOURCE_NAME, self::DB_USER_NAME, self::DB_PASSWORD);
        } catch (Exception $ex) {
            var_export($ex->getMessage());
        }
    }

    public function addUser($uid, $password, $givenname, $email)
    {

        $sql = "insert into users (uid, password, salt, givenname, email, eduPersonPrincipalName) "
            . "values (:uid, SHA2(:password, 256), :salt, :givenname, :email, :eduPersonPrincipalName)";

        try {
            $salt = substr(bin2hex(random_bytes(32)), 0, 32); //ランダム文字列を生成

            $this->dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
            $stmt = $this->dbh->prepare($sql);
            $stmt->bindValue(":uid", $uid, PDO::PARAM_STR);
            $stmt->bindValue(":password", $salt . $password, PDO::PARAM_STR);
            $stmt->bindValue(":salt", $salt, PDO::PARAM_STR);
            $stmt->bindValue(":givenname", $givenname, PDO::PARAM_STR);
            $stmt->bindValue(":email", $email, PDO::PARAM_STR);
            $stmt->bindValue(":eduPersonPrincipalName", $email, PDO::PARAM_STR); //とりあえずemailと同じ値にしてます。eduPersonの仕様にはそんなに詳しくないので。
            if($stmt->execute() == 1 ){
                echo "登録しました";
            };
        } catch (PDOException $ex) {
            echo "hopge";
            var_export($ex->getMessage());
        }
    }
}

if (
    array_key_exists("uid", $_POST) &&
    array_key_exists("password", $_POST) &&
    array_key_exists("givenname", $_POST) &&
    array_key_exists("email", $_POST)
) {
    $db = new simpleSamlDb();
    $db->addUser($_POST["uid"], $_POST["password"], $_POST["givenname"], $_POST["email"]);
}

?>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>SimpleSAMLphpのDBにユーザを追加したりする</title>
</head>
<body>
    <form method="POST" action="#">
        <label>uid</label><br />
        <input id="uid" name="uid" value="testuser" /><br />
        <label>password</label><br />
        <input id="password" name="password" value="testpass" /><br />
        <label>givenname</label><br />
        <input id="givenname" name="givenname" value="testgivenname" /><br />
        <label>email</label><br />
        <input id="email" name="email" value="test@example.simplesaml.com" /><br />
        <input type="submit" id="submit" value="送信" />
    </form>
</body>
</html>

このPHPのソースコードをWebブラウザで表示するとこんな感じになります。入力欄に値を設定して[送信]ボタンをクリックすればMySqlにユーザが登録されます。
ここでは(ユーザ名:testuser、パスワード:testpass)で登録しています。
image.png

Webブラウザから登録したユーザをphpMyAdminのコンソールから確認しました。ちゃんと登録されてますね。
image.png

IdPの設定

  • 設定ファイル「ssoIdp\config\config.php」を編集します。

URLのパスを変更します。

ssoIdp\config\config.php
'baseurlpath' => 'simplesaml/',
             ↓↓↓
'baseurlpath' => 'ssoIdp/', 

アドミニストレータのパスワードも変更します。(初期設定の値から変更しておかないとログインできないので。)

ssoIdp\config\config.php
'auth.adminpassword' => '123',
             ↓↓↓
'auth.adminpassword' => '123456',

IdPでのsaml2.0の機能を有効化します。

ssoIdp\config\config.php
'enable.saml20-idp' => false,
             ↓↓↓
'enable.saml20-idp' => true,

sqlauthを有効化します。

ssoIdp\config\config.php 」のmocule.enableに「'sqlauth' => true」を追加します。

ssoIdp\config\config.php
     'module.enable' => [
         'exampleauth' => false,
         'sqlauth' => true,
         'core' => true,
         'saml' => true
     ],
  • ssoIdp/config/authsources.phpの設定

MySqlに接続して、認証情報取得のために実行するSQLクエリを設定を追加します。(※'dsn','username','password'は各自の環境に合わせて設定してください。 )

config/authsources.php
    /*
    'example-sql' => [
        'sqlauth:SQL',
        'dsn' => 'pgsql:host=sql.example.org;port=5432;dbname=simplesaml',
        'username' => 'simplesaml',
        'password' => 'secretpassword',
        'query' => 'SELECT uid, givenName, email, eduPersonPrincipalName FROM users WHERE uid = :username ' .
            'AND password = SHA2(CONCAT((SELECT salt FROM users WHERE uid = :username), :password), 256);',
    ],
    */

          ↓↓↓

    'example-sql' => [
        'sqlauth:SQL',
        'dsn' => 'mysql:host=localhost;port=3306;dbname=simplesaml',
        'username' => 'root',
        'password' => '',
        'query' => 'SELECT uid, givenName, email, eduPersonPrincipalName FROM users WHERE uid = :username ' .
            'AND password = SHA2(CONCAT((SELECT salt FROM users WHERE uid = :username), :password), 256);'
    ]
  • IdPサーバのメタデータを編集します

使用する認証ソースを'example-userpass'から'example-sql'に変更します。

ssoIdp\metadata\saml20-idp-hosted.php
    /*
     * Authentication source to use. Must be one that is configured in
     * 'config/authsources.php'.
     */
    'auth' => 'example-userpass',
          ↓↓↓
    'auth' => 'example-sql',
  • 鍵ペアを ssoIdp\cert 配下に配置します。

opensslなどを使って鍵ペアを生成すればいいです。
一応、こちらのページサンプルの鍵ペア(秘密鍵をを公開しているので実用には耐えない)の中身を用意してあるので、そちらを使ってもらってもかまいません。
こんな感じのファイル構成になります。

C:\xampp\htdocs (←Apacheの制御下のフォルダ、環境の設定によって変わる)
      └ ssoIdp
         └ cert
            ├ server.pem
            └ server.crt

【補足】
ssoIdp\metadata\saml20-idp-hosted.php にて以下のように秘密鍵と証明書の設定をしているのですが、秘密鍵と証明書のファイル名を同じにしているので、こちらの設定を変更する必要はないです。

ssoIdp\metadata\saml20-idp-hosted.php
    // X.509 key and certificate. Relative to the cert directory.
    'privatekey' => 'server.pem',
    'certificate' => 'server.crt',
  • Apacheの設定

<Directory "C:/xampp/htdocs">の下あたりにでも以下のように設定を追加します。

\xampp\apache\conf\httpd.conf
DocumentRoot "C:/xampp/htdocs"
<Directory "C:/xampp/htdocs">
   ~省略~
</Directory>

<Directory "C:\xampp\htdocs\ssoIdp\www">
    Allow from localhost
</Directory>
<Directory "C:\xampp\htdocs\ssoIdp">
    Deny from all
</Directory>

Alias /ssoIdp/ "C:/xampp/htdocs/ssoIdp/www/"
  • MySqlに登録したユーザアカウントを用いて認証テストをしてみます

WEBブラウザで表示した設定画面の[認証]タブの「設定されている認証元をテスト」をクリックします。
image.png

先程設定した example-sql を選択します。
image.png

先程MySqlに追加したユーザアカウントのIDとパスワード(ユーザ名:testuser、 パスワード : testpass)を入力し、[ログイン]をクリックしてください。ちなみにすでにIdPでの認証が済んでいる場合は、この画面はスキップされます。
image.png

認証に成功すると以下のような画面が表示されます。"属性"の項を見てみると、config.phpで設定したSQL (SELECT uid, givenName, email, eduPersonPrincipalName FROM users ....)によりMySqlからパラメータを取得できていることがわかります。
image.png

SPとSAML連携してみる

SPサーバの構築の仕方についてはこちらを参考にしてください。

  • SPサーバの設定画面>[認証]タブ>"設定されている認証元をテスト">"example-sql">プルダウンでIdPを[選択]して、SPからSAMLRequestをIdPに飛ばす。

image.png

  • IdPサーバの認証画面へ遷移するので、先程MySqlに追加したユーザアカウントのIDとパスワード(ユーザ名:testuser、 パスワード : testpass)を入力し、[ログイン]をクリックしてください。

image.png

  • IdPでの認証に成功すると、IdPからSAMLResponseSPに送信され、それの検証によりSPでの認証が完了します。下の画像はSPでの認証に成功したときのものです。

image.png

SAMLResponseにパラメータを含ませる

先に書いちゃいますけど、SAMLの仕様によりSAMLResponse内のAttributeStatement要素に諸々の属性を追加できます。
下の例だと、上から順に"uid","givenName","email","eduPersonPrincipalName","groups"のパラメータが含まれています。
"uid"の値によってSP側で利用ユーザを特定できるのは当然として、"groups"はユーザが属しているグループを意味していますから、それによってSP側で利用可能な機能や権限を管理するといった使い方もできます。この例のユーザだとpoprockという2つのグループに属しています。

<saml:AttributeStatement>
  <saml:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
    <saml:AttributeValue xsi:type="xs:string">testuser</saml:AttributeValue>
  </saml:Attribute>
  <saml:Attribute Name="givenName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
    <saml:AttributeValue xsi:type="xs:string">testgivenname</saml:AttributeValue>
  </saml:Attribute>
  <saml:Attribute Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
    <saml:AttributeValue xsi:type="xs:string">test@example.simplesaml.com</saml:AttributeValue>
  </saml:Attribute>
  <saml:Attribute Name="eduPersonPrincipalName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
    <saml:AttributeValue xsi:type="xs:string">test@example.simplesaml.com</saml:AttributeValue>
  </saml:Attribute>
  <saml:Attribute Name="groups" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
    <saml:AttributeValue xsi:type="xs:string">pop</saml:AttributeValue>
    <saml:AttributeValue xsi:type="xs:string">rock</saml:AttributeValue>
  </saml:Attribute>
</saml:AttributeStatement>
  • では、ユーザとグループを紐づけてみましょう。phpMyAdminのコンソールから"usergroups"テーブルに"testuser"に紐づくグループのデータを挿入します。ここではrockpopというグループに"testuser"を紐づけています。値を入力して[実行]ボタンをクリックすればいいです。 image.png

ちなみにSQLだとこのようなクエリになります。

INSERT INTO `usergroups` (`uid`, `groupname`) VALUES ('testuser', 'rock'), ('testuser', 'pop');
  • データが挿入されました。
    image.png

  • IdPssoIdp/config/authsources.php で設定している、MySqlから認証情報を取得するために実行するSQLクエリを変更します。

config/authsources.php
    'example-sql' => [
        'sqlauth:SQL',
        'dsn' => 'mysql:host=localhost;port=3306;dbname=simplesaml',
        'username' => 'root',
        'password' => '',
        'query' => 'SELECT users.uid, givenName, email, eduPersonPrincipalName, groupname AS groups ' .
            'FROM users LEFT JOIN usergroups ON users.uid = usergroups.uid ' .
            'WHERE users.uid = :username AND password = SHA2(CONCAT((SELECT salt FROM users WHERE uid = :username), :password), 256);'
    ],

このページ(sqlauth:sql)記載のSQLを少し変更したものです、"usergroups"テーブルをJOINしてSELECTするSQLに変更してます。
(このSQLを実行すると複数行を返しますが、その場合は属性単位で纏める仕様らしいです。)

  • 以下は実際にSAML連携をしてみたときのSPの画面です。groupsの情報がIdPからSPに伝えられてますね。

image.png

【備考】
このようなIdPから受け取ったパラメータをSPで利用することにより、Just-In-Time(JIT)プロビジョニング連携をすることもできます。

JIT連携とは‥‥一般的なSSOサービスを利用する場合、事前に同じユーザ情報をIdPSPの両方で登録しておく必要があります。しかし、IdPからのSAMLResponseをSPが受け取ったタイミング(Just-In-Time)でユーザを新規登録することにより、事前のユーザ登録をすることなくSSOを利用できる仕組みです。

以上

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3