ワンタイムパスワードソフトウェアSlinkOTPのインストール手順について解説します。 SlinkOTPは、時刻同期式のワンタイムパスワードで、アルゴリズムにRFC標準のOATHを採用しています。
SlinkOTPは以下からダウンロードして下さい。
epelをyumリポジトリに追加してから、次のソフトウェアをインストールして下さい。
ionCube loaderを http://www.asial.co.jp/ioncube/encoder/download_loaders.php からダウンロードして、インストールして下さい。
SlinkOTP認証サーバダウンロードして下さい。 ダウンロードしたファイルを展開して、インストールスクリプトを実行して下さい。
# tar zxvf slink-otp-1.0.x.tgz
# cd slink-otp-1.0.x
# ./install.sh install
“/etc/rsyslog.conf”に以下の設定を追加して下さい。
local5.* /var/log/auth.log
データベースotp_dbを作成して、SlinkOTPに収録されているSQLファイル”otpuser.sql”をDBサーバに登録して下さい。
# mysql –user=dbuser –password=dbpasswd otp_db < conf/otpuser.sql
“/var/www/conf/config.ini-consumer”を”/var/www/conf/config.ini”に置き換え、環境に合わせて変更して下さい。
[password]
storage = “DB”
dsn = “mysql://<DBに接続するユーザ>:<DBに接続するパスワード>@<DBのホスト名>/otp_db”
table = user_tbl
idcol = user_id
usercol = user_id
mailcol = mail
secretcol = otp_secret
keyfile = “/var/www/conf/auth_tkt.conf”
[otp]
pwmaxfailure = 30 ;アカウントをロックするまでの認証失敗回数
pwfailureinterval = 600 ;認証失敗回数をカウントする期間
pwloduration = 1800 ;アカウントロックを解除するまでの時間
auto_register = 1
次に”/var/www/conf/auth_tkt.conf”に暗号化キーの設定を行います。SlinkOTPはワンタイムパスワードのシークレットを暗号化してDBに保存しますので、その際の暗号化にこのキーを使用します。
TKTAuthSecret <任意の文字列>
“/var/www/conf/sam.conf”を環境に合わせて変更して下さい。
dsn = “DBI:mysql:database=otp_db;host=<DBのホスト名>;port=3306″
admin = “<DBに接続するユーザ>”
passwd = “<DBに接続するパスワード>”
table = “user_tbl”
id_def = “user_id”
pwd_def = “password”
memcache_host = <memcachedのホスト名:ポート番号>
maildir = /var/www/conf/mail
is_api = 1
“/var/www/conf/mail/config.ini”にメールサーバの設定を行って下さい。
postmaster = “<送信元メールアドレス>”
smtp = “<メールサーバのホスト名>”
smtpauth_user = “”
smtpauth_pass = “”
また、”/var/www/conf/mail/motp.mail”にワンタイムパスワードを送信するメールのテンプレートがありますので、適宜内容を変更して下さい。
ユーザがワンタイムパスワード認証の開始、停止を設定する機能です。
ユーザがワンタイムパスワード認証の開始、停止を設定するには、コンシューマサイトでユーザの認証を行った際に、ユーザの認証情報としてユーザIDとメールアドレスを以下の形式でmemcachedに登録しておく必要があります。
erName=<ユーザID>\r\nmail=<メールアドドレス>\r\nauthenticated=0\r\n
memcachedに認証情報を登録した際のキーは、”AuthMemCookie”という名前のクッキーにセットして、ユーザに発行して下さい。
以下は、Basic認証でユーザを認証した場合のサンプルプログラムです。”slinkotp.php”は、SlinkOTPの”devel”フォルダに収録されています。
<?php session_start(); require_once('slinkotp.php'); $dsn = 'mysql:host=localhost;dbname=otp_db'; $dbuser = 'root'; $dbpasswd = 'password'; if (isset($_SERVER['REMOTE_USER'])) { $userid = $_SERVER['REMOTE_USER']; $mail = $_SERVER['REMOTE_USER']; $sessid = setSession(array('127.0.0.1'), $userid, $mail); $_SESSION['userid'] = $userid; try { $type = getOtpType($dsn, $dbuser, $dbpasswd, $userid); } catch (Exception $e) { print($e->getMessage()."<br>"); exit(1); } // ==================== OTP省略 ==================== if (isset($_COOKIE['auth_otp']) && $_COOKIE['auth_otp']) { $url = "https://localhost/pub/psession.php"; $timeout = 3; $postdata = "sessid=".urlencode($_COOKIE['auth_otp'])."&useragent=".urlencode($_SERVER['HTTP_USER_AGENT']); $response = getContentsByUrl($url, $timeout, $postdata); // 登録成功であれば、クッキーを発行する if (preg_match('/<code>([^<]*)<\/code>[^<]*<message>([^<]*)<\/message>[^<]*<id>([^<]*)<\/id>[^<]*<sessid>([^<]*)<\/sessid>/', $response, $matches)) { $code = $matches[1]; $message = $matches[2]; $id = $matches[3]; $newsessid = $matches[4]; if ($id && $id == $userid) { $type = 0; if ($newsessid) { setcookie("auth_otp", $newsessid, time()+36000, '/'); } } else { // } } else { $message = $response; } print("<br>response: $response<br>code: $code<br>message: $message<br>id: $id<br>newsessid: $newsessid<br>"); } // ==================== OTP省略 ==================== switch ($type) { case 1: //SeciossOTP header('Location: otplogin.php'); exit(0); break; case 2: //e-mail header('Location: motplogin.php'); exit(0); break; case 3: //Google Authenticator header('Location: otplogin.php'); exit(0); break; default: print("ログインしました。<br>sessid: $sessid"); setAuthenticated(array('localhost'), $sessid); } } ?>
また、”/etc/httpd/conf.d/otp.conf”の”ErrorDocument 401″に上記のプログラムのURLを設定して下さい。
認証後は、”https://<認証サーバのホスト名>/user/”にアクセスすると、ワンタイムパスワードの設定画面が表示されます。
パスワードの受け取り方法でスマートフォンアプリを利用する場合は、iPhoneについてはApp Storeから、AndroidについてはGoogle Playから”SlinkOTP”を検索して、ダウンロードして、SeciossLinkユーザガイドの”4.5 ワンタイムパスワードのシークレットキー登録”の手順に従って、シークレットの登録を行って下さい。
パスワードの受け取り方法でメールを選択した場合、memcachedに登録されているメールアドレス(mail)にワンタイムパスワードが送信されるようになります。
slink-otp-1.0.x内のスキーマファイルを、OpenLDAPにコピーして下さい。
# cp conf/secioss.schema /etc/openldap/schema
OpenLDAPの設定ファイル”/etc/openldap/slapd.conf”に次の設定を追加して下さい。
include /etc/openldap/schema/secioss.schema
ユーザ情報の登録を行います。ユーザのobjectClassにはseciossOtpUserを追加して下さい。
例えば、以下のようなLDIFをLDAPサーバに登録して下さい。
dn: uid=user01,ou=People,…
objectClass: inetOrgPerson
objectClass: seciossOtpUser
uid: user01
cn: user01
sn: user01
userPassword: password01
“/var/www/conf/config.ini”を環境に合わせて変更して下さい。
[password]
storage = “LDAP”
uri = <LDAPサーバのURI>
binddn = <LDAPサーバに接続するユーザのDN>
bindpw = <LDAPサーバに接続するパスワード>
basedn = <ユーザを検索するベースDN>
keyfile = “/var/www/conf/auth_tkt.conf”
次に”/var/www/conf/auth_tkt.conf”に暗号化キーの設定を行います。SlinkOTPはワンタイムパスワードのPINとシークレットを暗号化してLDAPサーバに保存しますので、その際の暗号化にこのキーを使用します。
TKTAuthSecret <任意の文字列>
ソフトウェアトークンは、iPhoneについてはApp Storeから、AndroidについてはGoogle Playから”SlinkOTP”を検索して、ダウンロードして下さい。
また、以下のソフトウェアトークンも利用可能です。
インストールしたらSlinkOTPを起動し、SeciossLinkユーザガイドの”4.5 ワンタイムパスワードのシークレットキー登録”の手順に従って、シークレットの登録を行って下さい。ただし、手順の中のWebサーバのURLは、今回構築したSlinkOTP認証サーバのURLとして下さい。
SlinkOTPが対応しているハードウェアトークンは、以下になります。
ハードウェアトークンのシークレットを登録するため、以下の形式のCSVファイルを作成します。
ユーザID>,<シークレット>,<ワンタイムパスワードの文字数>,<ワンタイムパスワードの生成間隔>
以下のコマンドを実行すると、LDAPにユーザのシークレットが登録されます。
# /opt/secioss/sbin/otpcmd.php add <CSVファイル>
ユーザのシークレットを削除する場合は、ユーザIDを記載したCSVファイルを作成し、以下のコマンドを実行して下さい。
# /opt/secioss/sbin/otpcmd.php del <CSVファイル>
Webアプリケーションからワンタイムパスワード認証を行うには、以下の方法で認証サーバにユーザIDとワンタイムパスワードを送信して下さい。
レスポンスは、以下のXML形式になります。
<?xml version=”1.0″ encoding=”UTF-8″?>
< response>
<code>エラーコード</code>
<message>メッセージ</message>
< /response>
<?xml version=”1.0″ encoding=”UTF-8″?>
< response>
<code>エラーコード</code>
<message>メッセージ</message>
<id>ユーザID</id>
<sessid>セッションID</sessid>
< /response>
エラーコードは、以下の値を取ります。
以下はワンタイムパスワード認証のサンプルプログラムです。Basic認証のサンプルスプログラムから呼び出されます。
setMethod(HTTP_REQUEST_METHOD_POST); $req->addPostData('password', $_POST['password']); $res = $req->sendRequest(); if (PEAR::isError($res) || $req->getResponseCode() != 200) { print("Authentication server returns error response
"); exit(1); } $xml = simplexml_load_string($req->getResponseBody()); $rc = intval($xml->code); if ($rc == 0) { // ==================== OTP省略 ==================== $code = NULL; $id = NULL; if (isset($_POST['remember']) && $_POST['remember']) { $url = "https://localhost/pub/psession.php?create=true&userid=$userid"; $timeout = 3; $postdata = "useragent=".urlencode($_SERVER['HTTP_USER_AGENT'])."&devicename=".urlencode($_POST['device_name']); $response = getContentsByUrl($url, $timeout, $postdata); // 登録成功であれば、クッキーを発行する if (preg_match('/([^<]*)<\/code>[^<]*
([^<]*)<\/message>[^<]* ([^<]*)<\/id>[^<]* ([^<]*)<\/sessid>/', $response, $matches)) { $code = $matches[1]; $message = $matches[2]; $id = $matches[3]; $newsessid = $matches[4]; if ($newsessid) { setcookie("auth_otp", $newsessid, time()+36000, '/'); } } else { $message = $response; } print("
response: $response
code: $code
message: $message
id: $id
newsessid: $newsessid
"); } // ==================== OTP省略 ==================== setAuthenticated(array('localhost'), $sessid); print("ログインしました。"); exit(0); } else { $message = "ログインに失敗しました。
"; } } print('User ID: '.$userid.'
ワインタイムパスワードの停止'.$message.'
'); ?>
setMethod(HTTP_REQUEST_METHOD_POST); $req->addPostData('password', $_POST['password']); $res = $req->sendRequest(); if (PEAR::isError($res) || $req->getResponseCode() != 200) { print("Authentication server returns error response
"); exit(1); } $xml = simplexml_load_string($req->getResponseBody()); $rc = intval($xml->code); if ($rc == 0) { // ==================== OTP省略 ==================== $code = NULL; $id = NULL; if (isset($_POST['remember']) && $_POST['remember']) { $url = "https://localhost/pub/psession.php?create=true&userid=$userid"; $timeout = 3; $postdata = "useragent=".urlencode($_SERVER['HTTP_USER_AGENT'])."&devicename=".urlencode($_POST['device_name']); $response = getContentsByUrl($url, $timeout, $postdata); // 登録成功であれば、クッキーを発行する if (preg_match('/([^<]*)<\/code>[^<]*
([^<]*)<\/message>[^<]* ([^<]*)<\/id>[^<]* ([^<]*)<\/sessid>/', $response, $matches)) { $code = $matches[1]; $message = $matches[2]; $id = $matches[3]; $newsessid = $matches[4]; if ($newsessid) { setcookie("auth_otp", $newsessid, time()+36000, '/'); } } else { $message = $response; } print("
response: $response
code: $code
message: $message
id: $id
newsessid: $newsessid
"); } // ==================== OTP省略 ==================== setAuthenticated(array('localhost'), $sessid); print("ログインしました。"); exit(0); } else { $message = "ログインに失敗しました。
"; } } if (!isset($_SESSION['ismotpsent'])) { $req =& new HTTP_Request($send_url); $req->setMethod(HTTP_REQUEST_METHOD_POST); $req->addPostData('username', $userid); $res = $req->sendRequest(); if (PEAR::isError($res) || $req->getResponseCode() != 200) { print("Failed to send OTP mail
"); exit(1); } $json = json_decode($req->getResponseBody()); $rc = intval($json->code); if ($rc) { print("Failed to send OTP mail: ".strval($json->message)."
"); exit(1); } $_SESSION['ismotpsent'] = true; } print('User ID: '.$userid.'
ワインタイムパスワードの停止'.$message.'
'); ?>