パスワードを忘れて2013年に取ったMovableTypeのバックアップにアクセス出来ない~パスワードは暗号化してSQLに保存されている

MovableTypeのロゴ PC

MovableTypeのバックアップから復元したい

引っ越し前に使っていたMovableType 3.36のバックアップを現在のWordpress環境にインポートするつもりで延び延びになっていました。

ローカル環境でMovableTypeを動かすことにした

MovableTypeのバージョンもかなり古いのですが、Windowsパソコンのローカルに古いXAMPP 1.7.7をインストールしました。(PHPやPerlのバージョンを当時に合わせる必要があったためXAMPP 1.7.7を探してきた)

Apache、PHP、Perl、MySQLの環境がローカルにできました。Apacheの設定などは手抜きです。MovableTypeを動かしてエクスポートしたいだけなので簡素で十分です。

ドメイン名はWindowsのHostsファイルで偽装解決する

当時のブログで使っていたサブドメインをWindowsのHostsファイルに記してローカルPCで処理させました。XAMPPにはphpMyAdminも入っているのでMySQLにもアクセスできてDBを直接書き換えることができます。

当時のMovableTypeで動かしていたブログサイトがローカルで観られるようになって、懐かしいという気持ちと共に、これログインどうやるんだったっけな?という疑問が。完全に忘れてしまっています。調べてみたら当時のURLにmt.cgiを追加してログイン画面を出すんでした。

http://blog.sasapurin.com
http://blog.sasapurin.com/mt.cgi

ところが、ログインできません。覚えているパスワードを入れても弾かれます。どうやらパスワードが間違っているもよう。しかし色々試してみてもログインできません。そんなに凝ったパスワードにしたかなぁ?もう記憶していた全てを試しましたがダメです。

phpMyAdminにアクセスして、MovableType用のDBテーブルを確認しました。「mt_author」にユーザー情報が入っているのは何となく覚えていたのでphpMyAdminから強制的にパスワードを書き換えれば行けると思ったのですが事は甘くありませんでした・・・パスワードがハッシュ化されてる。

色々やってみたのですが上手くいかないので保留することにしました。それから何か月経ったことやら・・・半分あきらめモードです。でもあきらめたくはない。

サンプルとなるスクリプトを公開してくれているサイトを発見した

このXAMPPによってローカルでMovableType 3.36動かしているノートパソコンはWindows10のノートパソコンです。このためだけに環境を置いたままにしていたのですが、そろそろこの無駄な環境を整理したいので、この問題にカタをつけたいと考えました。

何気にスマートフォンを使って検索していたところ、ふと思い立ちました。パスワード情報をハッシュ化している仕組みがどこかにあるので、「ヒントが欲しい」と探してみました。

sasapurin
sasapurin

MovableType、パスワード忘れ
とかで検索してみました

すると有力な情報を公開して下さっているサイトを見つけました。

こちらの情報によると、MovableTypeの場合は「mt_author」というテーブルの「author_password」カラムに格納されているとのことですが、やはり暗号化されているのでphpMyAdminだけでは解決しないということが分かりました。

テーブルだけじゃ把握できなかったのでMT本体を解析したところ「lib/MT/Author.pm」に暗号化関連のset_passworrd関数を見つけました。

https://qiita.com/_shogo_/items/ea24889f068e86720d74

簡易パッチを作成したということでソースも公開して下さっています。(引用させていただきます)
makepwd.pl

#!/usr/bin/perl -w
use strict;
use lib qw ( lib extlib );
use MT::Util;

my $pass = $ARGV[0];
my @alpha = ( 'a' .. 'z', 'A' .. 'Z', 0 .. 9 );
my $salt = join '', map $alpha[ rand @alpha ], 1 .. 16;
my $crypt_sha;

if ( eval { require Digest::SHA } ) {
    # Can use SHA512
    $crypt_sha
        = '$6$'
        . $salt . '$'
        . Digest::SHA::sha512_base64( $salt . $pass );
}
else {
    # Use SHA-1 algorism
    $crypt_sha
        = '{SHA}'
        . $salt . '$'
        . MT::Util::perl_sha1_digest_hex( $salt . $pass );
}
print $crypt_sha;
Code language: Perl (perl)
https://qiita.com/_shogo_/items/ea24889f068e86720d74

MTディレクトリ直下に「makepwd.pl」を配置して実行してみました。

確かに動作しましたが、ちょっと違う感があります。おそらくMovableTypeのバージョン違いなのでしょうが、MovableType 3.36の場合はパスワードのハッシュがこんなに長くないのです。どうやら修正が必要そうです。

Geminiに解説してもらう

ヒントは貰えたので、Google Gemin(AI)を頼ってみることにします。正直言ってPerlは全然わかりません(なんとなく雰囲気だけしかわからない)。

Geminiに「lib/MT/Author.pm」の中から「set_passworrd関数」を見つけてその部分について解説を依頼しました。

sub set_password {
    my $auth = shift;
    my($pass) = @_;
    my @alpha = ('a'..'z', 'A'..'Z', 0..9);
    my $salt = join '', map $alpha[rand @alpha], 1..2;
    $auth->column('password', crypt $pass, $salt);
}
Code language: Perl (perl)

するとどういう処理をしているのか教えてくれました。

「sub set_password」は、引数として受け取ったパスワードをハッシュ化して、データベースなどに保存するために使用されるPerl関数です。この関数は、パスワードをそのまま保存するのではなく、セキュリティを高めるためにソルトと呼ばれるランダムな文字列を付加してハッシュ化します。


関数の各行の解説

この関数は以下のステップで動作します。

① 引数の受け取り

my $auth = shift;
my($pass) = @_;Code language: PHP (php)

shift@_ は、関数に渡された引数にアクセスするための方法です。

  • my $auth = shift;
    • shiftは、引数リスト@_の最初の要素を取り出して$authに代入します。このコードでは、おそらくユーザー認証オブジェクトのようなものが渡されることを想定しています。
  • my($pass) = @_;
    • @_は、関数に渡されたすべての引数を含む特殊な配列です。ここでは、@_の最初の要素($authを取り除いた後)を$passに代入しています。この行は、関数に渡されたパスワード$passに格納しています。

② ソルトの生成

my @alpha = ('a'..'z', 'A'..'Z', 0..9);
my $salt = join '', map $alpha[rand @alpha], 1..2;Code language: PHP (php)

ここでは、ハッシュ化のためのソルトを生成しています。

  • my @alpha = ('a'..'z', 'A'..'Z', 0..9);
    • パスワードのソルトに使用する文字を**@alpha**配列に定義しています。これは、小文字、大文字のアルファベット、数字を全て含みます。
  • my $salt = join '', map $alpha[rand @alpha], 1..2;
    • rand @alphaは、@alpha配列のインデックスからランダムな数(0から配列の要素数-1まで)を生成します。
    • map $alpha[rand @alpha], 1..2は、このランダムなインデックスを使って@alpha配列から2つのランダムな文字を選び出します。
    • join ''は、選ばれた2つの文字を結合して一つの文字列(ソルト)にします。これにより、2文字のランダムなソルトが**$salt**に格納されます。

③ パスワードのハッシュ化

$auth->column('password', crypt $pass, $salt);Code language: PHP (php)

ここでは、crypt関数を使用してパスワードをハッシュ化し、$authオブジェクトに設定しています。

  • crypt $pass, $salt
    • Perlの**組み込み関数cryptは、パスワード$passとソルト$salt**を引数として、パスワードのハッシュ値を生成します。このハッシュ値は、不可逆な一方向の暗号化によって生成されるため、元のパスワードを復元することはできません。
  • $auth->column('password', ...)
    • これはオブジェクト指向プログラミングの構文で、$authオブジェクトのcolumnメソッドを呼び出しています。このメソッドの第1引数には**passwordという列名が指定されており、第2引数にはハッシュ化されたパスワード**が渡されます。

要約: この関数は、ユーザーのパスワードを受け取り、セキュリティのためにランダムなソルトを生成し、そのソルトを使ってパスワードをハッシュ化し、最終的に**$auth**というオブジェクトのpassword列にハッシュ値を設定する一連の流れを実装しています。

Google Gaminiの応答

おそらくこの「set_passworrd関数」のコードが古いため(MovableTypeのバージョンが古い)それに見合う「makepwd.pl」に修正する必要があると考えました。

Geminiに「makepwd.pl」を修正してもらう

「makepwd.pl」のソースは参照先ページで提示して下さっているので、それをサンプルとして、先に解説させた「set_passworrd関数」に合う様に修正依頼しました。するとGeminiが修正案を出してくれました。
makepwd-kai.pl

#!/usr/bin/perl -w
use strict;
use lib qw ( lib extlib );
use MT::Util;

# コマンドライン引数からパスワードを取得
my $pass = $ARGV[0];

# ソルトを生成するための文字セット
my @alpha = ( 'a' .. 'z', 'A' .. 'Z', 0 .. 9 );

# ランダムなソルトを2文字生成
my $salt = join '', map $alpha[ rand @alpha ], 1 .. 2;

# crypt関数を使用してパスワードをハッシュ化
my $crypted_pass = crypt($pass, $salt);

# 結果を出力
print $crypted_pass;
Code language: Perl (perl)

Geminiによる解説

元のスクリプトは、コマンドライン引数として与えられたパスワードをハッシュ化するためのものです。しかし、最初の関数sub set_passwordは、UNIXのcrypt関数を使用して、特定の形式のハッシュを生成します。

これを踏まえて、サンプルコードを**sub set_password**関数が生成するハッシュ形式に適合するように修正します。


修正されたPerlスクリプト

#!/usr/bin/perl -w
use strict;
use lib qw ( lib extlib );
use MT::Util;

my $pass = $ARGV[0];

# sub set_password 関数と同じソルト生成ロジックを使用
my @alpha = ('a'..'z', 'A'..'Z', 0..9);
my $salt = join '', map $alpha[rand @alpha], 1..2;

# crypt 関数を使用
my $crypt_pass = crypt $pass, $salt;

print $crypt_pass;Code language: PHP (php)

修正内容の解説

  1. ソルトの生成:
    • 元のサンプルでは、ソルトを16文字で生成していましたが、sub set_password関数に合わせるため、2文字のランダムなソルトを生成するように変更しました。
  2. ハッシュ化アルゴリズムの変更:
    • 元のサンプルはDigest::SHAMT::Utilモジュールを使用してSHA-512やSHA-1でハッシュ化していました。
    • sub set_password関数では、Perlの組み込み関数であるcryptを使用しているため、この**crypt関数**を使うように変更しました。これにより、sub set_password関数と同じハッシュ形式を生成できます。
  3. 不要なコードの削除:
    • Digest::SHAモジュールを使用するロジックや、MT::Utilを使用する代替ロジックは不要になったため削除しました。これにより、スクリプトがシンプルになり、sub set_passwordのロジックと完全に一致します。

この修正により、スクリプトは**sub set_password**関数が生成するハッシュと完全に互換性のあるパスワードハッシュを生成するようになります。

Google Gaminiの応答

やはりMovableTypeのバージョンが古かったため、暗号化強度が弱かった様でそれに合わせてGeminiが修正してくれました。ハッシュ文字列の長さが短かったのはバージョンが古くてその当時はこの程度のハッシュ文字数で良かったのでしょう。

期待していた通りのハッシュ文字の長さになりました。これはいけるんじゃないか?

変更したいパスワード(ローカル環境で動かしているのでシンプルなので良い)を生成させて、phpMyAdminで「mt_author」テーブルの「author_password」カラムを書き換えました。

パスワードを変更したのでログインを試してみた

その後、MovableTypeのログインページ(mg.cgi)からユーザー名とパスワード(今回書き換えた)を入力すると、無事にログインに成功しました!

sasapurin
sasapurin

この画面を見たのは何年ぶりだろう。
懐かしいしアクセスできたことに感激する。

もう何か月もログインできない状態のまま保留しっぱなしだったので、無事にログイン出来てめっちゃスッキリしました。Gemini(AI)によるプログラミング解説は凄いですね。コードの良し悪しとかわからないので目的達成で十分感謝です。

MT過去記事の復元(Export)

MovableTypeの管理ページに10年以上ぶりにログインすることが出来て、懐かしくなりました。それはさて置き書き出しができるか確認してみたところ、無事にMovableType形式でエクスポートすることが出来ました。テキストファイルで4MB弱あるのでまあまあの文字数が含まれていると思います。

WordPressはMovableType形式の記事をインポートすることができますので、過去記事を現在のWordPress環境に取り込むことができます。ローカル環境で過去記事を読んでみたところ、個人的には結構重要なことを記録していたりして復元させたかったのです。

これで一歩前進というか、MTのバックアップファイルから取り出すことができたので、後は当サイト(WordPress)にインポートして画像ファイルのPATHを合わせてやることで取り込みが実現します。

一番古い記事を見ると2005年7月となっているので20年以上前の記事です。その頃の私の格闘している様子とか考えていたこととかが記録されているので、これも貴重な財産だと思ってインポートさせようと思います。(画像のURLを合わせるのがちょっと面倒なので後日時間をかけて効率よくミスのない方法で)

コメント

タイトルとURLをコピーしました