アルファサード株式会社 代表取締役 野田 純生のブログ


PHPerのためのMovable Type講座(その14)


公開日 : 2014-05-22 10:53:03


ログイン処理をPHPで実装してみよう、編。

PHPerのためのMovable Type

DynamicMTMLを使うと簡単に書けますが、今回はDynamicMTMLなしでも動くコードを紹介します。

ログインの処理フロー

  • ユーザー名とパスワードをポストする
  • ユーザー名から mt_author オブジェクトをロード
  • 渡されたパスワードを暗号化してデータベースの値と比較
  • 正しければ、クッキーをセット
  • mt_session レコードにセッション情報を保存

mt_session の session_kind は 'SI' とする。クッキー名は 'mt_commenter'。Session.pm のドキュメントにありますね。

=item SI

Active commenter sessions are held in SI sessions.

<?php include( '/path/to/mt/php/mt.php' ); $mt = MT::getinstance( 1, '/path/to/mt/mt-config.cgi' ); if ( $SERVER['REQUESTMETHOD'] != 'POST' ) { exit(); // POSTメソッドのみ受け付ける } if ( ( isset( $REQUEST[ 'username' ] ) ) && ( isset( $REQUEST[ 'password' ] ) ) ) { $username = $REQUEST[ 'username' ]; $password = $REQUEST[ 'password' ]; $author = $mt->db()->fetchauthorbyname( $username ); // もしくは、 // $username = $mt->db()->escape( $username ); // requireonce( 'class.mtauthor.php' ); // $author = new Author; // $author->Load( "authorname='${username}'" ); if ( isset( $author ) ) { if ( isvalidpassword( $author, $password ) ) { $expires = 0; if ( isset( $REQUEST[ 'remember' ] ) && $REQUEST[ 'remember' ] ) { // パスワードを記憶する $expires = time() + 60 * 60 * 24 * 365; } if ( isset( $REQUEST[ 'blogid' ] ) ) { $blogid = $REQUEST[ 'blogid' ]; $blog = $mt->db()->fetchblog( $blogid ); } if ( $blog ) { $siteurl = $blog->siteurl(); $path = pregreplace( '!^https{0,1}://.{1,}?(/)!', '/', $siteurl ); } else { $path = $mt->config( 'CookiePath' ); } $token = md5( uniqid( '', 1 ) ); // SessionIDを生成 $usercookie = 'mtcommenter'; setcookie( $usercookie, $token, $expires, $path ); $session = $mt->db()->fetchsession( $token ); $now = time(); if ( empty( $session ) ) { requireonce( 'class.mtsession.php' ); $session = new Session; $session->sessionid = $token; $session->sessionkind = 'SI'; $session->sessionemail = $author->email; $session->sessionname = $username; $session->sessionstart = $now; $session->Save(); } else { $session->sessionstart = $now; $session->Update(); } } } }

function is_valid_password ( $author, $password ) {
    // 正しいパスワードかどうかをチェックする
    $real_pass = $author->password;
    if ( strpos( $real_pass, '$6$' ) === 0 ) {
        // sha512
        // 「$6$暗号化文字列$暗号化されたパスワード」という形式になっている
        $search = preg_quote( '$6$', '/' );
        $real_pass = preg_replace( "/^$search/", '', $real_pass );
        list ( $salt, $real_pass ) = explode( '$', $real_pass );
        $digest = base64_encode( hash( 'sha512', $salt . $password, 1 ) );
        $password = rtrim( $digest, '==' );
    } elseif ( strpos( $real_pass, '{SHA}' ) === 0 ) {
        // sha1
        // 「{SHA}暗号化文字列$暗号化されたパスワード」という形式になっている
        $search = preg_quote( '{SHA}', '/' );
        $real_pass = preg_replace( "/^$search/", '', $real_pass );
        list ( $salt, $real_pass ) = explode( '$', $real_pass );
        $password = hash( 'sha1', $salt . $password );
    } else {
        // 古いMTの形式
        $password = crypt( $password, $real_pass );
    }
    if ( $real_pass === $password ) {
        return TRUE;
    }
    return FALSE;
}
?>

コードのコメントにも書いていますが、MTのパスワードは、sha512で暗号化されており、「$6$暗号化文字列$暗号化されたパスワード」という形式になっています。 ということで、ユーザーのパスワードを生成するロジックは以下。

function make_password ( $password, $hash = 'sha512' ) {
    $strs = 'abcdefghkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ012345679'; 
    $strs = preg_split( "//", $strs, 0, PREG_SPLIT_NO_EMPTY ); 
    $salt = '';
    for ( $i = 0 ; $i < 16; $i++ ) { 
        $salt .= $strs[ array_rand( $strs, 1 ) ]; 
    }
    if ( $hash == 'sha512' ) {
        $crypt_sha = '$6$' . $salt . '$' . base64_encode( hash( 'sha512', $salt . $password, 1 ) );
        $crypt_sha = rtrim( $crypt_sha, '==' );
    } else {
        $crypt_sha = '{SHA}' . $salt . '$' .  hash( 'sha1', $salt . $password );
    }
    return $crypt_sha;
}

せっかくなのでたまには課題

では、これでログイン処理ができたとして、以降のリクエストのログインチェック、ユーザー情報の取得とコンテクストへのセットはどのようにすればよいでしょうか?



このブログを書いている人
野田純生の写真
野田 純生 (のだ すみお)

大阪府出身。ウェブアクセシビリティエバンジェリスト。 アルファサード株式会社の創業者であり、現役のプログラマ。経営理念は「テクノロジーによって顧客とパートナーに寄り添い、ウェブを良くする」。 プロフィール詳細へ