password

主訴: OpenBSDで man ls とかすると、ls.1 なんて見つかりませんと言われました。

あなたなら、こんな訴えが有った時、どういう対処をしますか?

正直、うろたえました。今まで何度もmanのお世話になってたのに、それが突然、縁を切られた ようで、寂しいです、と、情緒に訴えても、何の解決にもならない。

医者なら、最後にmanしたのは何時ですかって問診するだろうね。それから現在までの間に 何か変わった事がありませんでしたか? 何か悪い事をしませんでしたか?

そんな何時まで動いていたかって聞かれても、manは空気みたいなもので、いつまで動いて いたか気にした事もありません。

まあ、こんなものだろう。昨日の夕食で何を食べましたかって保健所の人に聞かれて、すらすら 答えられる人っているのだろうか? 刑事ドラマで、何年何月の何日の何時にあなたは、あそこ で、犯人とおぼしき人を見ましたかって聞かれて、すらすら答えるシチエーション、絶対に 嘘だと思うぞ。

話を戻して、さあどうする? man manすれば。隣の7機でOpenBSDが元気に動いていたので、 聞いてみた。

manの原稿は、/etc/man.confに登録されてるMANPATHを元に検索されますって書いてあった。 で、どんなPATHが登録されてるかみたら、そんなファイルは無かった。でも、ちゃんと検索 出来て、表示もしてる。どんなからくりが仕込まれているの?

manのソースから、etc/man.confを探し出して、眺めてみろ。/usr/src/usr.bin/mandocが 本拠地みたい。

[ob: mandoc]$ grep /etc/man.conf *.[ch]
manpath.c:#define MAN_CONF_FILE "/etc/man.conf"

それらしい名前のファイルが引っかかってきましたなあ。取り合えず、開腹して、中を覗いて みますか、と、医者の口調を呟いてみる。

#define MAN_CONF_FILE   "/etc/man.conf"
#define MANPATH_DEFAULT "/usr/share/man:/usr/X11R6/man:/usr/local/man"

もう、答えが見えちゃったような気がするぞ。/etc/man.confが無い時は、(諦めて)デフォルトの検索PATHを使うんだろう。/etc/man.confが有ると、そちらを優先かな。

Windows10機のOpenBSDでは案の定、/etc/man.confが登録されてて、そこには、

manpath /usr/local/lib/tcl/tcl8.5/man
manpath /usr/local/lib/tcl/tk8.5/man

が登録されてた。作成日は、今年の1月末。かすかな記憶をたどると、何かのpkgを入れた時、 tcl/tkも一緒に付いてきて、/etc/man.confに登録しとくと、いいよって言われたんで、 登録した覚えがある。

で、登録と同時に、他のmanは無視されるようになっちゃんだな。裏を取っておけ。

manconf_parse(struct manconf *conf, const char *file,
                char *defp, char *auxp)
{
:
        /* MANPATH and man.conf(5) cooperate. */
        defp = getenv("MANPATH");
        if (NULL == file)
                file = MAN_CONF_FILE;

        /* No MANPATH; use man.conf(5) only. */
        if (NULL == defp || '\0' == defp[0]) {
                manconf_file(conf, file);
                return;
        }
:
manconf_file(struct manconf *conf, const char *file)
{
        const char *const toks[] = { "manpath", "output", "_whatdb" };
        char manpath_default[] = MANPATH_DEFAULT;
        :
        if ((stream = fopen(file, "r")) == NULL)
                goto out;
	:
                switch (tok) {
                case 0:  /* manpath */
                        manpath_add(&conf->manpath, cp, 0);
                        *manpath_default = '\0';
                        break;
        :
out:
        if (*manpath_default != '\0')
                manpath_parseline(&conf->manpath, manpath_default, 0);
}

confファイルが有ってそこにmanpathが設定されてたら、デフォルトの設定は無視されるようになってた。なる程ね。 これにて一件落着。

まてまて、今回は近くに別のシステムが有ったから助かったけど、無かった時はどうする? 何せ、OpenBSDは、世間から隔絶された絶海の孤島ですから。自給自足で何とかするように 索を巡らせておけ。それが孤高に生きる道。

えとね、manコマンドを調べ尽くすに限るな。それしか手は無いんだから。

1. /usr/share/man/man1/man.1 の生原稿を頑張って読んでみる
2. ktrace man ls ; kdump して、挙動を探る
3. strings /usr/bin/man | grep man とかやって、ヒントを探す
4. man の一言で、使い方を復習する

思いつくのは、これぐらいかなあ。まだ、何か有りそうな気がするけどな。

password

以前ちょっと妄想した、Webサイトの会員登録方法をコンピュータに計算させちゃうって方法、 我ながら良案と思えるようになってきた。

大体どのサイトを見てもそうだけど、パスワードは、容易に想像されがたく、覚えやすいものを 考えましょうと矛盾した事を言っている。

どうしてもダメなら、パスワードを一括で管理してくれる、パスワード管理プログラムを 使いましょうとも勧めている。そんな管理プログラムが壊れたらどうするんや? オイラーは、そっちの方が心配。

Webにログインするって考えたら、当然目の前にパソコンが有るんだから、そいつに計算 させればいいじゃん。(スマホとかタブレットは、今回は眼中に無いです。)

で、どんなパソコンでも備えているであろうコマンドだけを使って、パスワード計算のための 一行野郎を書いた積り。再掲すると、

[ob: ~]$ echo trump | openssl sha512 | openssl base64 | cut -c 13-21
YThmOGFmY
N2VkNjk4Y
ZGZkYTIzO

trumpってのが秘密のキーワード。秘密なんで初恋の人の名前でも尊敬する人の名前でも 何でもよい。そいつのハッシュを作ってあげる。この結果がどうなるか?

[ob: ~]$ echo trump | openssl sha512
(stdin)= a8f8afb3ef495e10ad718d3235733015bb2e3920c6f8115f7ed698ae4f61815fdaafaa3f6eb745c735e69e38f2b7d097dfda239fa06894a8f87489f6780334e4

こんな16進数の羅列が得られる。それがパイプを流れて行って、64進数に変換される。

[ob: ~]$ echo trump | openssl sha512 | openssl base64
KHN0ZGluKT0gYThmOGFmYjNlZjQ5NWUxMGFkNzE4ZDMyMzU3MzMwMTViYjJlMzky
MGM2ZjgxMTVmN2VkNjk4YWU0ZjYxODE1ZmRhYWZhYTNmNmViNzQ1YzczNWU2OWUz
OGYyYjdkMDk3ZGZkYTIzOWZhMDY4OTRhOGY4NzQ4OWY2NzgwMzM0ZTQK

16進数だと、使われている文字種が0-9a-fまでしか無いから、文字種の幅を広げたって 事になる。そして、最後に適当な所を切り出すって訳。

このアルゴリズムと秘密のキーワードさえ頭の中に叩き込んでおけば、メモを取る必要も なく、プログラムとして保存しておく必要さえ無い。

そんじゃ、複数のサイトで同じidとpasswordを使いまわすのか? 別にそんな危険な事を する必要は無い。サイトの名前と秘密のキーワードを連結すればおk。例えば、@ITなら

[ob: ~]$ echo trump@IT | openssl sha512 | openssl base64 | cut -c 13-21
ZGJjZjVkM
OWNkMjJjN
NzMzNDlkN

こんな具合になる。

なお、キーワードから乱数を作り出すのに、俗に言うsha-2系を使ったけど、セキュリティーに 敏感な人達は、 知られざる暗号の 2017年問題、安全サイトが突如警告サイトに(2/4)こんな警告を発しているから、 ご注意めされ。そして、こんなのも。 SHA-1ハッシュの衝突を現実的な時間で生成する攻撃「Shatterd」

で、ここで使ってるコマンド類はunix敬OSでは普通に入っているはず。問題は素のWindowsだな。特に問題になりそうなのは、openssl。オイラーの場合、mobaXtermと言う偽unixアプリが 有るんで、これに追加してあげればいいだろう。

追加は出来たんだけど、動かしてみるとopensslが応答を返さない。やっぱり偽unixだったよ。 諦めて、Windows用のアプリとして、 WindowsにOpenSSLをインストールして証明書を取り扱う(基本編) を入れてあげたよ。

それから、Windows用のbusybox.exeも入れて みた。その結果、こんな風に使える。

c:\>busybox echo trump | openssl sha512 | openssl base64 | busybox cut -c 13-21
YThmOGFmY
N2VkNjk4Y
ZGZkYTIzO

busyboxの付け忘れがありそうなので、スクリプトにしておくのがよさそう。特に、echoは素のWindowsにも用意されてるけど、挙動が違うので注意の事。面倒なら、下記のような、 バッチを登録しとくと、bashって叩くだけで、Bash on Windows 環境が出来上がるぞ。 この環境の中に入ってしまえば、viも普通に使えるし、面倒な前置詞 busyboxも不要になる。

~ $ cd c:/app/OpenSSL-Win64/bin/
c:/app/OpenSSL-Win64/bin $ cat bash.bat
busybox.exe
busybox.exe sh -

これにて、一件落着。

password 発生器

とまあ、一行野郎の発生器を作った訳だけど、世間一般ではどうやってパスワードを発生 させてるか、参考までに見ておく。

# -*- coding:utf-8 -*-
from string import ascii_lowercase, ascii_uppercase, digits
import random
 
def iter_password(length=8, num_of_numbers=2, num_of_uppers=2):
    numbers = list(digits)
    uppers = list(ascii_uppercase)
    lowers = list(ascii_lowercase)
    num_of_lowers = length - num_of_numbers - num_of_uppers
    while True:
        random.shuffle(numbers)
        random.shuffle(uppers)
        random.shuffle(lowers)
        p = numbers[:num_of_numbers] + \
            uppers[:num_of_uppers] + \
            lowers[:num_of_lowers]
        random.shuffle(p)
        yield ''.join(p)
 
p = iter_password(length=6, num_of_numbers=1, num_of_uppers=1)
print(next(p))
# wSqvb7
print(next(p))
# caw4nR

それぞれの文字種を取り出して、かき回しておき、そこから必要な個数を取ってきて連結。 最後にまたかき回しているんだな。みんなこういう仕組みで、ガラガラ・ポンしてるんだな。

パスワードの強度

最近はパスワードをクラックする技術が発達してて、へなちょこパスワードは直ぐに 見破られてしまうそうだ。チェッカーが出ている。

Password checker

こういうクリチカルな事案は、是非セカンドオピニオンしとけ。

The Password Meter

最初のURLでは、入力したパスワードが、どれぐらいの時間で破られるか教えてくれる。 後の方は、どんな文字をどのように混ぜれば良いか、点数をもって示してくれる。

&X7f9w%z3 なんてのが、Very Strong と言ってきた(減点なしのパターン)。これを 前の方で試すと、4週間で破られますってさ。

前の方でpassなんてのを試すと、即破られます、時間をかけるまでもないとの事。ipadのロックに使ってる4桁の数字だと、200ns。但し、メモリアルな数字0911はあれかと思ったら、通報番号になるのか知らんけど、特別危険な番号らしい。

長い々パスワードを入力したら、4 sextillion years こんな風に言ってきた。こう言われても、どれぐらいの長さか分からん。

「じゅげむ じゅげむ ごこうのすりきれ かいじゃりすいぎょの すいぎょうまつ うんらいまつ ふうらいまつ くうねるところにすむところ やぶらこうじのぶらこうじ ぱいぽ ぱいぽ ぱいぽのしゅーりんがん しゅーりんがんのぐーりんだい ぐーりんだいのぽんぽこぴーの ぽんぽこなーの ちょうきゅうめいのちょうすけ」これぐらいかな?

こういう時は、commonLispに聞いてみる。OpenBSDにRを入れたら、世迷いでclispも紛れ こんできたからね。

[15]> (format nil "~r" 4000000000000000000000)
"four sextillion"

無駄な機能がやっと役にたったわい。ああ、折角だからschemeにも登場願おう。 sha1とかやると、hexで答えが返ってくるんだけど、10進数に直すと、どんだけーー?

[ob: ~]$ echo hoge | openssl sha1
(stdin)= eefd5bc2c547bf82b177b6259c13f7723dc876d9
[ob: ~]$ gsi
Gambit v4.8.4

> #xeefd5bc2c547bf82b177b6259c13f7723dc876d9
1364389885486331479808206927430906730536254338777

schemeだと、#xを前置すると、16進数として扱ってくれる。そんじゃ、pythonとかは?

In [1]: 0xeefd5bc2c547bf82b177b6259c13f7723dc876d9
Out[1]: 1364389885486331479808206927430906730536254338777

rubyも同様だね。

乱数

パスワードには乱数が付きもの。pythonのパスワード発生器でも使われている。オイラーが ハッシュを乱数と見做して、使ってみたけど、本当に乱数なの?

調べてみるか。sha512だと、512Bitの出力なんで、バイトに直すと64Byteになる。 1byteのhex表現は、00からffまでだな。幾つもハッシュを取って、バイトの分布を取って みるか。

解析に使うのは、awkでいいだろう。

[ob: tmp]$ cat hex.awk
# count hex
{
    for (p=0; p<length($0); p+=2) {
        cnt[ substr($0,p,2) ] += 1;
    }
}

END {
    for (k in cnt) {
        print k, cnt[k]
    }
}

2文字づつ切り出して、それを連想配列のキーにして、個数を数える。昔取った杵柄だな。 で、sha512の種はどうする? 3秒考えて、/usr/local/binの下にあるコマンド名を種に すればいいだろう。

[ob: tmp]$ for f in /usr/local/bin/*
> do
> echo $f | openssl sha256 | cut -d' ' -f2 >> seed
> done
[ob: tmp]$ wc seed
    1659    1659  107835 seed

いつの間にかコマンドが増えているな。望まない者も結構密入国してるからなあ。トランプの 気持ちも分からないではないよ。 これで、5万回以上、サイコロを振った事になるな。

じゃ、早速、分類してみる。

[ob: tmp]$ cat seed | awk -f hex.awk | sort
00 218
01 205
02 197
:
fd 183
fe 197
ff 215

大体200回前後の目が出てるな。後はこれを統計分析すればいいのか。

Rで仮説検定(入門)

Rでカイ二乗検定

あやしいサイコロと『隠れマルコフモデル』

Rで実践する統計的検定の初歩 (1/3)

以上で検定のやり方も分かったので、やってみるかとなったんだけど、その前に、16進数を 完全に分解して、その現れ方を調べてみる。(言ってみれば、16面のサイコロを何回も振って、 その出目を調べる訳。)

[ob: tmp]$ cat LOG | awk -f nibble.awk | sort
0 13366
1 13253
2 13155
3 13293
4 13236
5 13239
6 13307
7 13356
8 13163
9 13109
a 13263
b 13378
c 13433
d 13079
e 13380
f 13342

これを見れば、いかさまじゃない事が一目瞭然。検定はpassしても大丈夫だろう。 じゃ、16進数の文字列をそのまま使っちゃえ。

[ob: tmp]$ head LOG | cut -c -10
126b63ea02   1 day     81%
8814f0fe5f
0ad4c06d26
4035811622   300 ms    26%
089c68709b
192b800f4f
4e6dcefb3e
468f9e9eda
b727873ffa
ce567ab1db             58%

生のhexを10桁で、どれぐらいでクラックされるか確認。殆どが1日となった。不幸にも 数字だけのものを選んじゃうと、瞬きする間にクラックされてしまう。

そこで、換字ですよ。1,2,3,a,b,c とかは、人間が好みそうな文字なんで、それを別な文字に ワープさせてみた。

[ob: tmp]$ head LOG | cut -c -10 | tr '123abc' '-@.QRZ'
-@6R6.eQ0@     53 year   100%
88-4f0fe5f
0Qd4Z06d@6
40.58--6@@      1 week   100%
089Z68709R      1 day     89%
-9@R800f4f
4e6dZefR.e
468f9e9edQ      8 month   99%
R7@787.ffQ
Ze567QR-dR      6 year    94%

中にはちょっと強度不足のものも混じっているな。もう少し数字を別の文字に変えるように すれば、良くなるだろう。なお、最初に考えたbase64を使って文字種を散らすというのは、 愚策であった。出現頻度の割合に平等さがなくなり、折角のhex文字出現の平等さが消されて しまう。(出目に偏りが有る、サイコロを作ってたという事です)

/etc/password

パスワードとくれば、/etc/passwordだな。久しぶりに眺めてみたら、大事なパスワードは 管理者しか手が届かない別ファイルに保存されてた。

よくニュースになる、ユーザー情報が何万件も盗まれましたってのは、この別ファイル(データベース)を取られてしまったって事だな。

後は、そのファイルを元に総当たり攻撃で、解読するとな。1アカウントの解読に何日も かかるようじゃ効率が悪いだろうから、せいぜい数分で破れなければ、次のユーザーを試す んだろうね。

上のURLで何日で破れるってのは、特定ユーザーに的を絞って、どうしても破りたい。 破ったら莫大な金銭、あるいは秘密情報を利用出来るようになるって目標を定めた場合 だろう。そういう目的が無いなら、ビットコインでも発掘してた方が儲かるぞ。

で、OpenBSDの場合には、パスワードの暗号化にブロウフィッシュってのが使われて いるのね。どんな風に暗号化されるか試せるようになってた。

[ob: tmp]$ encrypt hoge
$2b$10$onFLvW1TW6RFWigmWFYjdOd.bbTO82mX/drFqS.NOE38jx0BV8rSe

色々な文字種が散りばめられていて、これ自体が良パスワードとして使えそう。 この技を盗めないか。後でソースを参照しとけよ。

crypt_newhashというライブラリィーが核になる部分を担当してた。mainの中で使った バッファー類は、コマンドを終了する時にちゃんと掃除してるのね。抜け目ない。