Spring Security の BCryptPasswordEncoder でハッシュ化された password などの文字列を、Perl から使って認証とかに使いたい時だってあるじゃない?
いや、ねーよな。
前提
こんな出来事がありました。
DB に保存されたハッシュ値
$2a$10$Cc6/UiniMuKkyVsVM.FUt.rmgDm0UOQxuhuGSSuL/LzZUGrNeGvxq
こんな値が保存されていた。
Spring Security の仕業らしい。
見慣れぬこのハッシュ値は一体どういう値なんだい。
という状況にハマっていた時、色々ググったが、コレって情報が出てこなかった。
生パスワード
JscxAX:5[4Q]NYoK
とあるログインユーザは
「こいつが俺のパスワードなんだけど合ってる?」
などと、Perl の私に尋ねてきた。
えっ、さっきの Spring Security がハッシュ化して保存されてるあのヘンテコな文字列とこのパスワードの検証?
Perl の私にはわかんないよ?
いやいや、頑張りましょう
案外簡単なのですよ
コード
use strict;
use warnings;
use utf8;
use feature qw(say);
use Crypt::Eksblowfish::Bcrypt qw(bcrypt);
my $raw_password = 'JscxAX:5[4Q]NYoK';
my $hashed_password = '$2a$10$Cc6/UiniMuKkyVsVM.FUt.rmgDm0UOQxuhuGSSuL/LzZUGrNeGvxq';
my $salt = substr($hashed_password, 0, 29); # "$2a$10$" + 22 文字分
my $check = bcrypt($raw_password, $salt);
if ($check eq $hashed_password) {
say "ATTERUYO!";
}
else {
say "ATTENAIYO!";
}
結果
ATTERUYO!
よかった。あってた。
…え、これ、毎回 ATTERUYO!
が出たりしない?
もし、ユーザが間違ったパスワードを入力してたら
my $raw_password = 'JscxAX:4[5Q]NYoK';
一行だけ、微妙に間違ってる感じに変更。
結果
ATTENAIYO!
よかった。
ちゃんと間違いを判定できてる。
Perl からも同じマナーで保存しといてね
Spring Security さんが
「Perl から、パスワード変更して、保存しておく機能作っておいてね」
とおっしゃっている。
えぇ、貴方はその値を検証で使うんですよね?
貴方にも扱える形式にしないとダメですか…?
…当然、そうしないとダメですよね。
嫌われ者の Perl さんにもちゃんと出来るはずよ。だから頑張って
Java に出来て Perl に出来ないとか、ラクダの風上にも置けないこと言っちゃダメです。
コード
use strict;
use warnings;
use utf8;
use feature qw(say);
use Crypt::Eksblowfish::Bcrypt qw(bcrypt en_base64);
use Crypt::Random qw(makerandom_octet);
sub gen_salt {
return en_base64(makerandom_octet(Length => 16));
}
my $raw_password = 'Z86J9_Kr_sDcw#o4'; # 新しいパスワード
my $salt = '$2a$10$' . gen_salt();
my $hashed_password = bcrypt($raw_password, $salt);
say $hashed_password;
結果
$2a$10$/IwrxT05tg1ZlmUrV.7eAOsowJsbkVHs6ku54FC0VHBew23HOm61W
それっぽいのが出来たじゃない
Spring Security で検証してね
Z86J9_Kr_sDcw#o4
というパスワードが上記の Perl のコードによって
$2a$10$/IwrxT05tg1ZlmUrV.7eAOsowJsbkVHs6ku54FC0VHBew23HOm61W
っていうそれっぽい文字列にされて DB に保存されてしまっている。
これ、Java (Spring Security) で本当にこのハッシュを正しく検証が出来るのか…?
コード
いかにも、Java とか普段書かない人間が書いた感の強い、随分と適当なコードによる検証を行った。
package com.example.demo;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
public class DemoApplication {
public static void main(String[] args) {
String rawPassword = "Z86Jq_Kr_sDcw#o4";
String hashedPassword = "$2a$10$/IwrxT05tg1ZlmUrV.7eAOsowJsbkVHs6ku54FC0VHBew23HOm61W";
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
if (passwordEncoder.matches(rawPassword, hashedPassword)) {
System.out.println("ATTERUYO!");
}
else {
System.out.println("ATTENAIYO!");
}
}
}
結果
ATTERUYO!
ちゃんと出来たよよかったね。
… え、これ、毎回 ATTERUYO!
が出たりしない?
もし、ユーザが間違ったパスワードを入力してたら
String rawPassword = "Z86Jq_Kr_sDcw#o4";
一行だけ、微妙に間違ってる感じに変更。
結果
ATTENAIYO!
よかった。
ちゃんと間違いを判定できてる。
他のハッシュ化は?
SCryptPasswordEncoder とか Pbkdf2PasswordEncoder とかあるけど、今時点で自分の環境でそれは課題になってないので、いつか誰かがまとめたらいいね。