Auth0 に Redmine のユーザーがパスワードごとインポートできなかった話
はじめに
最近認証基盤を統合しようというような話が周りで出ておりまして、Redmine 上にあるユーザーを Auth0 にインポートしてみようという実験をしていました。ところが Auth0 の仕様上それが難しいということが発覚したので、後学のためにまとめておきます。
tl;dr
Auth0 がサポートしているのは HASH(salt + raw_password)
形式でハッシュ化されたパスワードだけ。
Redmine ユーザーにおけるパスワードハッシュ化の仕様について
Redmine 本体のソースコードによると、ユーザーのパスワードは SHA1(salt + SHA1(clear_password))
といった形式でハッシュ化されたものが DB に保存されているようです。
Auth0 の仕様について
Auth0 Management API には「Bulk User Import」という機能が備わっており、指定された JSON スキーマを用意して API 経由でアップロードすると、一括でユーザーをインポートしてくれます。その中でパスワードも一緒にインポートする機能が付いており、認証基盤を乗り換えたとしてもいままでと同じパスワードで認証が可能となる素晴らしい環境が整っています。
JSON スキーマ自体は公式ドキュメントに記載されているとおりで、比較的シンプルな作りとなっています。今回は以下のようなJSONを用意してみました。
Redmine の仕様に合わせて custom_password_hash
の algorithm
を sha1
に、hash
の encoding
を hex
に、salt
の position
を prefix
に指定してあります。
[
{
"email": "aruneko@example.com",
"email_verified": false,
"family_name": "foo",
"given_name": "bar",
"user_id": "aruneko@example.com",
"nickname": "Aruneko",
"custom_password_hash": {
"algorithm": "sha1",
"hash": {
"value": "xxxxxxxxxx",
"encoding": "hex"
},
"salt": {
"value": "yyyyyyyyyy",
"position": "prefix"
}
}
}
]
これでインポート用のエンドポイントを叩き、ユーザーをインポートしてみます。
$ curl -v --request POST \
--url 'https://YOUR_DOMAIN.jp.auth0.com/api/v2/jobs/users-imports' \
--header 'authorization: Bearer HOGEHOGE' \
--form 'users=@users.json' \
--form 'connection_id=con_HOGEHOGE' \
--form 'upsert=false' \
--form 'send_completion_email=true'
インポートが完了し、ここでいざ元のパスワードを使ってログインを試みても成功しません。そこで目を皿のようにして JSON Schema を読んでいると、salt の position
プロパティについて何やら怪しげな記述を発見します。
"position": {
"type": "string",
"enum": [
"prefix",
"suffix"
],
"description": "The position of the salt when the hash was calculated. For example; MD5('salt' + 'password') = '67A1E09BB1F83F5007DC119C14D663AA' would have \"position\":\"prefix\"."
}
つまり、Auth0 の内部ロジックとして、任意のハッシュ関数を HASH
としたときに、HASH(salt + raw_password)
とした計算結果をログインの成否に使っているのではないかという仮説を立てることができます。これを検証するため、元のパスワードを SHA1
でハッシュ化したものをパスワード欄に入力すると、見事認証が通るという結果を得ることができました。
しかしハッシュの逆計算は不可能なため、手元のデータベースにある情報をインポートしたとしてもパスワードごと移行するという夢は潰えたことになります。
おわりに
Auth0 のユーザーインポート機能は非常に便利ですが、HASH(salt + raw_password)
の形式でデータベースに保存されている場合にのみ、パスワードごとインポート可能なようです。まだ検証していませんが、Auto Migration が使える可能性は残されているかと思います。あるいは、思い切ってパスワードの再設定を全員に行わせるという選択肢もアリかと思います。
認証基盤の PaaS は非常に便利ですが、牧歌的にユーザー情報はまるごと移行できるから大丈夫だろうと思っていたところ、こうした落とし穴があるとは使ってみるまで気づきもしませんでした。もちろん Auth0 がサポートしている形式でハッシュ化されたパスワードを保存している際には問題なく移行できるかと思います。使い始める前に一度確認することをお勧めします......
Discussion