OpenLDAPアカウント向けパスワードリセットUI

POSTメソッドとスクリプトで、次のようなパスワードリセットUI(Webページ)を作成。
  1. リセット希望者は自身のメールアドレスを入力しsubmit
  2. リセットされたパスワードをメールで通知
  3. リセットに際してのセキュリティのケアは次のとおり
URLはhttp://server1.hoge.local/password_reset.html。

html/password_reset.html

<html>
<head>
<title>password reset (hoge.local)</title>
<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
</head>
<body>
<h2>LDAP(hoge.local)パスワードリセット</h2>
リセットしたパスワードをメールでお知らせします。
<form method="post" action="cgi-bin/password_reset.sh">
メールアドレス:
<input type="text" name="mail">
<input type="submit" value="送信">
</form>
<hr>
<address>
システム管理チーム
</address>
</body>
</html>

cgi-bin/password_reset.sh

#!/bin/sh
#
# "パスワードリセット"
# POST されたメールアドレスの LDAP エントリの userPassword 値を、
# ランダム文字列のハッシュ値におきかえ、そのランダム文字列を、
# メールアドレスへメール通知する。
#

#####################################################################
# 応答画面描画ルーチン
#
# $1: TITLE
# $2: BODY
#
function renderHTML () {

        echo "Content-Type: text/html"
        echo ""
        echo "<!doctype html>"
        echo "<html>"
        echo "<head>"
        echo "<meta http-equiv=\"Content-type\" content=\"text/html; charset=UTF-8\" />"
        echo "<title>$1</title>"
        echo "</head>"
        echo "<body>$2</body>"
        echo "</html>"

}


#####################################################################
# POST されたメールアドレスの抽出
# mail=... の形式で標準入力に渡ってくる
#
read POST
MAIL_ADDRESS=`echo $POST | awk -F= '{print $2}' | sed -e 's/%40/@/g'`
# パーセントエンコーディングによって @ が %40 で渡ってくるので、@ に置換

# メールアドレス形式のチェック
#
# ----- hoge.co.jp 自明
echo $MAIL_ADDRESS | grep "^[A-Za-z0-9_\.\-]\{1,\}@hoge.co.jp$" > /dev/null 2>&1
# ----- 汎用
#echo $MAIL_ADDRESS | grep "^[A-Za-z0-9_\.\-]\{1,\}@[A-Za-z0-9_\.\-]\{1,\}$" > /dev/null 2>&1
if [ $? -ne 0 ]; then
        TITLE="not_mail-address-format"
        BODY="$MAIL_ADDRESSはメールアドレス形式ではないようです。"
        renderHTML $TITLE $BODY
        exit 1
fi


#####################################################################
# LDAP 登録されているメールアドレスかどうかのチェック
#
SEARCH_PATTERN="(&(ObjectClass=inetOrgPerson)(mail=${MAIL_ADDRESS}))"

# anonymous 接続
SEARCH_RET=`/usr/bin/ldapsearch -x -b "dc=hoge,dc=local" ${SEARCH_PATTERN}`

if [ `echo $SEARCH_RET | grep givenName | wc -l` -eq 0 ]; then
        TITLE="no_mail-address_entry"
        BODY="$MAIL_ADDRESSはLDAP(hoge.local)に未登録のアドレスです。"
        renderHTML $TITLE $BODY
        exit 1
fi

DBODY="$DBODY<br>SEARCH_PATTERN=$SEARCH_PATTERN<br>SEARCH_RET=$SEARCH_RET"
#renderHTML "debug" "$DBODY"
#exit 1


#####################################################################
# ランダムパスワード文字列とそのハッシュ値を生成
#
PLAIN_PW=`/sbin/slappasswd -g`
HASHED_PW=`echo -n $PLAIN_PW | slappasswd -T /dev/stdin`

DBODY="$DBODY<br>PLAIN_PW=$PLAIN_PW<br>HASHED_PW=$HASHED_PW"
#renderHTML "debug" "$DBODY"
#exit 1


#####################################################################
# ldapmodify で更新
#

# ----- 1. LDIF 作成
LDIF=/tmp/ldif.`date +%Y%m%d%H%M`
cat << EOL1 > $LDIF
dn: mail=${MAIL_ADDRESS},ou=People,dc=hoge,dc=local
changetype: modify
replace: userPassword
userPassword: ${HASHED_PW}
EOL1

DBODY="$DBODY<br>LDIF=$LDIF"
#renderHTML "debug" "$DBODY"
#exit 1

# ----- 2. パスワードハッシュ値を更新
MODIFY_RET=`/usr/bin/ldapmodify -x -D "cn=admin,dc=hoge,dc=local" -y ./ldap_admin_pw -f $LDIF`

if [ `echo $MODIFY_RET | grep "modifying entry" | wc -l` -ne 1 ]; then
        TITLE="failed_replacing_password-hashed_value"
        BODY="パスワードハッシュ値の更新に失敗しました。"
        renderHTML $TITLE $BODY
        exit 1
fi

# ----- 3. LDIF を削除
rm -f $LDIF

DBODY="$DBODY<br>MODIFY_RET=$MODIFY_RET"
#renderHTML "debug" "$DBODY"
#exit 1


#####################################################################
# メールで通知
#
(env LC_ALL=ja_JP.UTF8 mail -s "password reset (hoge.local)" $MAIL_ADDRESS) << EOL2
LDAP(hoge.local)のあなたのパスワードを
次の値にリセットしました。
    $PLAIN_PW
適宜変更してご利用ください。
EOL2

if [ $? -ne 0 ]; then
        TITLE="failed_mail-sending"
        BODY="メール送信に失敗しました。"
        renderHTML $TITLE $BODY
        exit 1
fi

#renderHTML "debug" "$DBODY"
#exit 1

renderHTML "password reset (hoge.local)" "リセットしたパスワードをメールでお送りしました。"
LDAP接続用のパスワードを別ファイルに記しておく
# echo "cn=admin,dc=hoge,dc=localのパスワード" > /var/www/cgi-bin/ldap_admin_pw
# chown apache:apache /var/www/cgi-bin/ldap_admin_pw
# chmod 600 /var/www/cgi-bin/ldap_admin_pw
※ACLで権限をパスワードリセットに限定した別のLDAPアカウントを用意すると、よりセキュア。
「OpenLDAPとphpLDAPadminによるLinuxとRedmineの認証基盤」に戻る
2018/10/11 Taikou Yamada (t-yamada _at_ ceres.dti.ne.jp)