Linux の /etc/shadow にはパスワードが暗号化されて格納されていますが、"$1$salt$hash" というように "$1$" で始まるものは MD5 暗号が用いられているということを聞きました。ですが、一般に言う MD5 は salt を持たず、入力に対して固定のハッシュ値を持つはずです。また、この2通りでそれぞれ生成したパスワードを比較しても長さ等が異なるようです。調べてみると MD5 の亜種には salt を利用する SMD5 というものがあるようですが、shadow ファイルの暗号はこれなのでしょうか?特殊な MD5 なのであれば、salt を利用しない通常の MD5 ハッシュ生成関数を用いて同じ形式の MD5 値を生成できるでしょうか?


また、この形式のパスワードは Apache の htpasswd でも利用されることがありますが、 主に Linux や FreeBSD 等のオープンソース系のどのソフトでどのような暗号が使われているのかをまとめている書籍やホームページなどはありますでしょうか?

回答の条件
  • 1人2回まで
  • 登録:
  • 終了:2011/03/10 17:59:59
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

ベストアンサー

id:JULY No.3

回答回数966ベストアンサー獲得回数247

ポイント27pt

PHP の crypt(PHP: crypt - Manual)は、おそらく、glibc の crypt 関数に依存する物だと思います。

Manpage of CRYPT

md5("plain_text") で MD5 ハッシュを発生させることもできますが、これらの間で「salt が要不要の違いがある」「出力されたハッシュの長さの違いがある」という違いがあったため、

「長さ」の違いは、文字表現にした時の違いだと思います。

そもそも、MD5 の値は 128 bit の値なので、16 バイトのバイナリ値です。これを文字に直す時に、md5() では 16 進数表記に、crypt で生成された文字列のハッシュ値部分は、Base64 になっていると思います。

16 進数表記であれば、1 バイト 2 文字なので、32 文字。Base64 だと 6 bit で一文字なので、128 / 6 = 21.333... で 22 文字になります。

id:quesit

なるほど、出力データが 32 バイト文字列であると勝手に頭の中で固定化されてしまっていました。MD5 は 128 bit とは定義されていますが、文字列表現の形式までは定義してなかったのですね。データ長も計算どおりということで、今ずっともやもやしたものがすっきりしました。

二度もお付き合い頂き、本当にありがとうございました!

2011/03/10 17:57:23

その他の回答3件)

id:t-wata No.1

回答回数82ベストアンサー獲得回数13

ポイント18pt

このページが参考になります。

http://oku.edu.mie-u.ac.jp/~okumura/c/auth.html

少し古い方式ですと以下のページが参考になります。

http://www.icepp.s.u-tokyo.ac.jp/~sensha/software/Linux/JF/Shado...

それと格納されているのは単方向変換なのでいわゆる「暗号」とはちょっと違います。復号できないですから。

いずれにせよ、LinuxなどUnix系OSではパスワードなど復号する必要の無いものの保存にはcrypt関数が良く使われています。自前で下手に実装するよりはずっといいですから。

id:quesit

回答ありがとうございます。すみません、暗号化とハッシュ計算はごちゃごちゃに発言してしまっていますね。

passwd や shadow の仕組みについてはわかっているのですが、ここで用いられている crypt の MD5 についての詳細が書かれたページの情報が欲しい感じです。参考サイトにもある "$1$wpkFeWyW$dRnpRo1XDyGJQkc1IM3CT1" というハッシュ値は salt 付き 34 Byte(salt を抜くと 22 Byte)ですが、 MD5 は salt 無し 32 Byte のハッシュを出力という認識でしたので、この crypt の MD5 出力とは一体なんぞや?という話なのです。

2011/03/10 10:20:06
id:JULY No.2

回答回数966ベストアンサー獲得回数247

ポイント18pt

MD5 の亜種には salt を利用する SMD5 というものがあるようですが、

ちょっと、この辺に混乱があるようなので、補足しておきます。

MD5 は t-wata さんが指摘されているように「暗号」とは違い、変換の結果から元に戻せない「一方向関数」、または、「ハッシュ関数」と呼ばれるものの一つで、その他に有名なところとしては SHA というものもあります。

で、SMD5 は、結果として保存しているデータが、元のデータにソルトの値を付加したものを入力として、MD5 の値を保存したものを呼びます。もし、ハッシュ関数に SHA を使えば、SSHA と呼ばれます。先頭の S は「Salted」の略です。

/etc/shadow の書式に関しては、下記のページが参考になります。

Shadow password - Wikipedia, the free encyclopedia

英語ですが、真ん中あたりに、$1$ が MD5 で、$2$ が Blowfish(blowfish 自体はハッシュ関数では無いですが、入力をキーとして特定の文字列を blowfish で暗号化した結果をハッシュ値として扱います) で、$5$ が SHA-256 で、といった説明が出ています。

/etc/shadow ファイルのパスワードフィールドの書式としては、'$' で始まる3つのフィールドがあって、先頭がハッシュ関数の種類、2つ目がソルトの値を表し、3つ目が「パスワード文字列に、2つ目にあるソルト値として付加したものを、1つ目のフィールドに指定されたハッシュ関数で計算した結果」という意味になります。

なので、パスワードフィールドの3つ目の値は、

  • パスワードにソルトを付加したものを入力とした、MD5 の結果

または、

  • パスワードを入力とした、SMD5 の結果

という事になります。

SMD5 と言っても、MD5 というハッシュ値を求める処理自体には違いがなく、入力値にソルトを付加することで、Rainbow Table のような「予め、ハッシュ値を計算したデータベースを作って、元のパスワードを求める」という攻撃に対抗している、ということになります。

手前味噌ですが、OpenLDAP で使える「SSHA」に関して、その計算手順を追ってみた事があります。参考になれば幸いです。

塩加減は重要? - JULYの日記

id:quesit

詳細な回答ありがとうございます。手元の Ubuntu では SHA-512 を表す "$6$" が使われていますね。参考になります。参考サイトも読ませていただきますね。

t-wara さんの返信にも書いたのですが、うーむ、すみません、質問が悪かったですね。もっとも端的に言うと、PHP では crypt("plain_text", "$1$salt") で MD5 ハッシュを発生させることも、md5("plain_text") で MD5 ハッシュを発生させることもできますが、これらの間で「salt が要不要の違いがある」「出力されたハッシュの長さの違いがある」という違いがあったため、これらの関係性が知りたかったという感じです。crypt と md5 関数で同じ平文から同じ MD5 ハッシュを出力を得ることはできるでしょうか?

2011/03/10 10:35:28
id:JULY No.3

回答回数966ベストアンサー獲得回数247ここでベストアンサー

ポイント27pt

PHP の crypt(PHP: crypt - Manual)は、おそらく、glibc の crypt 関数に依存する物だと思います。

Manpage of CRYPT

md5("plain_text") で MD5 ハッシュを発生させることもできますが、これらの間で「salt が要不要の違いがある」「出力されたハッシュの長さの違いがある」という違いがあったため、

「長さ」の違いは、文字表現にした時の違いだと思います。

そもそも、MD5 の値は 128 bit の値なので、16 バイトのバイナリ値です。これを文字に直す時に、md5() では 16 進数表記に、crypt で生成された文字列のハッシュ値部分は、Base64 になっていると思います。

16 進数表記であれば、1 バイト 2 文字なので、32 文字。Base64 だと 6 bit で一文字なので、128 / 6 = 21.333... で 22 文字になります。

id:quesit

なるほど、出力データが 32 バイト文字列であると勝手に頭の中で固定化されてしまっていました。MD5 は 128 bit とは定義されていますが、文字列表現の形式までは定義してなかったのですね。データ長も計算どおりということで、今ずっともやもやしたものがすっきりしました。

二度もお付き合い頂き、本当にありがとうございました!

2011/03/10 17:57:23
id:TransFreeBSD No.4

回答回数668ベストアンサー獲得回数268

ポイント27pt

まず、MD5ですが、これはハッシュ値の長さは固定で128bit(=16byte)です。

PHPはじめ良くあるのは、この128bitを16進数表記(2^4)の32文字に変換します(4bit*32文字)。

http://www.php.net/manual/en/function.crypt.php

一方、crypt関数ですが、こちらは128bitをBase64風に64文字(2^6)を使用して22文字に変換します(6bit*22文字、4bitはあまり)。

また、crypt関数はパスワードを安全に保管するための関数であるので、互換性や安全性が考えられて作られています。

http://www.unixuser.org/~haruyama/security/SinSec20101026/build/...

これの「1.1.5. 実際の処理」「1.1.6. stretchとは」あたりにあります。

このため「単純にmd5関数で変換しただけ」というわけではありませんが、使っている関数(または計算方法)は同じですから、同じようにすれば同じ平文から同じ文字列が得られます(そうでないと困ります)。

実際の方法は下記です。16年前から方法は変わっていません。変えるとログインできなくなってしまいますから。

http://svn.freebsd.org/viewvc/base/head/lib/libcrypt/crypt.c?rev...

以降、*BSDやlinuxではこれが採用されていき、時代と共に拡張されて今に至ります。

id:quesit

やはりハッシュの長さは固定だったのですね。具体的な処理内容に興味があったのですが、ずばり crypt 関数の中身に関する資料ありがとうございました。勉強に crypt() 関数を md5() 関数で再現してみようかな、とも思いましたが思いのほか複雑ですね。

なんにせよ、この資料があれば crypt の MD5 に関する脳内の関連付けが完全に把握できました!回答ありがとうございました。

2011/03/10 17:59:07
  • id:JULY
    実際の /etc/shadow の結果と、ソルトとパスワードから MD5 を計算しても答えが合わなくて、なんでだろうと glibc のソースを見てみたところ、かなり複雑な計算しているようです。

    glibc の crypt/md5-crypt.c にある __md5_crypt という関数が実体のようですが、まず、パスワード文字列 + ソルト値 + パスワード文字列、というデータの MD5 の値を求め、さらに、先の MD5 の入力に、この MD5 の結果をした付加して MD5 の値を求め、さらに... と、かなり複雑なアリコリズムになっています。

    最終的な 128 bit の値を Base64 の表記で、というところには違いは無いですが、私が OpenLDAP の SSHA の処理を追っかけたときとは大違いでした。
  • id:TransFreeBSD
    私もソース見るまでBase64だとばっかり思ってましたが、よく見ると違うんです。
    Base64は0から「[A-Z][a-z][0-9]+/」の順です。
    http://ja.wikipedia.org/wiki/Base64
    一方、cryptでは、実は「./[0-9][A-Z][a-z]」の順だったりするんです。
  • id:JULY
    > 一方、cryptでは、実は「./[0-9][A-Z][a-z]」の順だったりするんです。

    わっ、ほんとだ。

    文字変換するところのマクロの名前が「b64_from_24bit」だから、てっきり Base64 しているものだと思ったら、確かに違いますね。

    どうりで「base64 -d ほにゃらら」ってやったら、「invalid input」って怒られる訳だ(^^;
  • id:quesit
    説明を読む限り、わざと計算を複雑にして計算時間をかかるようにして、総当り攻撃に対処する、ということみたいですね。あと、計算の最後に行われるエンコードは「Base64 風エンコード」ということなのですね。Base64 ではないにしても、6 bit で一文字 = エンコード後 22 文字という部分は同じですね。

    皆様、このたびは本当に勉強になりました。有難うございました!
  • id:TransFreeBSD
    あ、一つ忘れてた。単なるBase64 風エンコードじゃなくて、バイト並びも換えてある。
    16byteが、0,6,12,1,7,13...の順でエンコードされている。ソース上、きれいに並んでるから見落としがち。
    なんつーかPHK氏の頭の中身は分かりませんw

この質問への反応(ブックマークコメント)

トラックバック

  • /etc/shadow のパスワードフィールド question:1299688445 を切っ掛けに、/etc/shadow ファイルのパスワードフィールドを調べてみたら、思いの外、複雑な処理をしているのが分かったので、実際にこ
「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません