Perl関数で暗号化 - DES/MD5共通編
前々章と前章で、DES環境とMD5環境でのcrypt関数についてそれぞれ解説しました。
DESかMD5かということであれば、Webサーバには、大きく分けて次の4種類があるようです。
(1) DESのみ使用可
(2) MD5のみ使用可
(3) DES/MD5両方使用可
(4) DES/MD5両方使用不可
上記の分類で、(4)のケースは論外なので考慮しないこととして、本章では、(1)から(3)の全てのサーバ環境において使用できる共通式のコードを考えてみたいと思います。

4-1. DES/MD5共通の暗号化

saltについては、DESが2文字固定で、MD5が0~8文字です。
DESの場合、3文字以上のsaltを指定しても、先頭の2文字だけが有効で3文字以降は無視されます。
MD5の場合は8文字以内ですが、saltは多い方が強度が増すので、できれば最大の8文字まで使用したいところです。
そこで、saltは8文字を指定することにします。DESのみの環境でも3文字目以降は無視されるだけでエラーにはならないはずです。
次に、crypt関数の実行方法の仕方です。DES方式とMD5方式では、saltの指定方法が異なってきます。
DES/MD5共通環境においては、強度面を考慮してMD5方式にしたいところです。
そこで、最初にMD5方式でcrypt関数を実行して、それでだめだったら、DES方式で実行する、という方法にすることにします。
01use strict;
02 
03# パスワード
04my $passwd = 'secret';
05 
06# 暗号化
07print &encrypt($passwd), "\n";
08 
09#-----------------------------------------------------------
10#  crypt暗号
11#-----------------------------------------------------------
12sub encrypt {
13    my $plain = shift;
14 
15    # saltは8文字作成
16    my @wd = (0 .. 9, 'a' .. 'z', 'A' .. 'Z', '.', '/');
17    my $salt;
18    for (1 .. 8) {
19        $salt .= $wd[int(rand(@wd))];
20    }
21 
22    # 最初にMD5方式で実行し、それでだめだったらDES方式で実行
23    return crypt($plain, '$1$' . $salt . '$') or crypt($plain, $salt);
24}
これで大抵はいいと思うところですが、中には「へそ曲がり」のサーバ環境がありました。
実は、筆者のテスト環境は「Windows7 + Apache2」なのですが、この環境は「DESのみ使用可」です。この環境において、上記のコードを実行すると、MD5方式のcrypt関数をそのまま実行してしまい、先頭の「$1」をsaltと解釈して「$1qmeMiXizJ7A」というような暗号文字を吐き出しました。
まあ、このままでも照合まで正常に処理できるので、悪くはないのですが、saltが「$1」固定となるため、saltの意味を成しません。
そこで、最初のMD5方式のcrypt関数実行の返り値がMD5方式の暗号文字であるかを精査し、OKだったらそのまま採用、NGだったらDES方式のcrypt関数を実行する、ということにしましょう。
以下が(今のところの)完成形です。
01use strict;
02 
03# パスワード
04my $passwd = 'secret';
05 
06# 暗号化
07print &encrypt($passwd), "\n";
08 
09#-----------------------------------------------------------
10#  crypt暗号
11#-----------------------------------------------------------
12sub encrypt {
13    my $plain = shift;
14 
15    # saltは8文字作成
16    my @wd = (0 .. 9, 'a' .. 'z', 'A' .. 'Z', '.', '/');
17    my $salt;
18    for (1 .. 8) {
19        $salt .= $wd[int(rand(@wd))];
20    }
21 
22    # 最初にMD5方式で実行し、MD5式暗号なら返す。NGだったらDES方式で実行して返す
23    return crypt($plain, '$1$' . $salt . '$') =~ /^(\$1\$.+)/
24        ? $1
25        : crypt($plain, $salt);
26}
上記のコードを「へそ曲がり」環境(DESのみ使用可)で実行したところ、「YwNyzkAW/DVds」というようなDES式の文字列を無事に吐き出しました。
また、DES/MD5両方使用可のサーバで試してみたところ、「$1$IUSph7N4$n/.pTrsRhrb4ZJT7mbEOa1」と正常にMD5式で表示されました。
pagetop

4-2. DES/MD5共通の照合処理

照合の仕方については、難しくはないでしょう。暗号文字の先頭にあるsaltの書式から、MD5式なのかDES方式なのかを判別すればいいことになります。
01use strict;
02 
03# パスワード
04my $passwd = 'secret';
05 
06# 暗号化された文字列
07my $crypt = 'YwNyzkAW/DVds';
08 
09# 判定
10if (&decrypt($crypt, $passwd)) {
11    print "OK\n";
12} else {
13    print "NG\n";
14}
15 
16#-----------------------------------------------------------
17#  crypt照合
18#-----------------------------------------------------------
19sub decrypt {
20    my ($crypt, $plain) = @_;
21 
22    # saltの書式を判別する
23    my $salt = $crypt =~ /^(\$1\$.*\$).+/ ? $1 : substr($crypt, 0, 2);
24 
25    # 照合実行
26    return crypt($plain, $salt) eq $crypt ? 1 : 0;
27}
次章では、Perlモジュールを使った方法について解説しましょう。
pagetop