blog.monophile.net

コンピュータのこととかのメモ。

Takaaki Yamamoto

東京工業大学において計算機科学と応用数学を学び、 情報科学芸術大学院大学[IAMAS] においてメディア表現を専攻し修了。 2015年にコンビネータ論理を基に計算完備な計算手法 論理珠算を開発し、 それを含む体系である算道を構成した。 その成果により、 第19回 文化庁メディア芸術祭 アート部門 新人賞 (文部科学大臣賞) を2016年に受賞。 現在は digitiminimi において、インフラエンジニアとして生計をたててている。

work

各種システム構築と管理を承ります。

Cloud PlatformOpenstack, GCP, AWS, Azure, ...
Openstackkeystone, glance, cinder, swift, neutron, nova, ...
VirtualizationQEMU+KVM, LXD/LXC, Docker, ...
OSDebian GNU/Linux, Ubuntu, CentOS, ...
NetworksIPSec, L2TP, VXLAN, WirelessAP, ...
WebAppsWordPress, GitLab, Redmine, ...
Configuration ManagementAnsible, Terraform, ...
MonitoringNagios, Munin, ...

posts

Pythonのpasslibでハッシュ値を指定のソルトで計算する

概要

/etc/shadowに書かれるハッシュ値を自動生成したくなったので、調べてみた。 Ansibleと組み合わせたいので、Pythonのpasslibを使う。

他ブログでも方法は紹介されているが、 passlibで非推奨になっているメソッドが使われていたので、 公式ドキュメントみながら確かめてみた。

例えば、2.0になった段階でencrypt()は廃止されて、hash()のみになるようす。 現時点ではpasslibは1.7だが、1.7では両方とも使用可能。

なお、ハッシュ値の形式は次の様になっているらしい。

${{ algorithm }}${{ option }}${{ salt }}${{ hash }}

各種OSの/etc/shadowで使えるハッシュアルゴリズムの一覧

各種OSで使えるアルゴリズムは下記から得られる。

import passlib.hosts
print("Linux: %s" % ", ".join(passlib.hosts.linux_context.schemes()))
print("FreeBSD: %s" % ", ".join(passlib.hosts.freebsd_context.schemes()))
print("OpenBSD: %s" % ", ".join(passlib.hosts.openbsd_context.schemes()))

↑の出力は↓。

Linux: sha512_crypt, sha256_crypt, md5_crypt, des_crypt, unix_disabled
FreeBSD: bcrypt, md5_crypt, bsd_nthash, des_crypt, unix_disabled
OpenBSD: bcrypt, md5_crypt, bsdi_crypt, des_crypt, unix_disabled

FreeBSDは9.1からsha512に対応しているらしい。 細かく設定したい場合は自分でCryptContextを作る。

ソルトの長さを確認しておく

アルゴリズムによって使えるソルトの長さが異なるので、上記で確認しておく。

  • sha512: [./0-9A-Za-z] 0-16文字
  • sha256: [./0-9A-Za-z] 0-16文字
  • bcrypt: [./0-9A-Za-z] 22文字

平文パスワードからハッシュを計算する (ソルトの指定なし)

ソルトの指定をしないでハッシュを計算する。 この場合は自動でソルトが生成されていることがわかる。

import passlib.hash
secret = "secretsecret"
hashed = passlib.hash.sha512_crypt.hash(secret)
print("sha512('%s') -> '%s'" % (secret, hashed))
print("verify: %s" % passlib.hash.sha512_crypt.verify(secret, hashed))

↑の出力は↓。

sha512('secretsecret') -> '$6$rounds=656000$fcD6caqZ.HTe7aO0$0v1Xsq6tFVQVGnNX4M9A9g/UrvhJYeK53JmFl0xXmqAH5n2Mqt8XbqzX9E5VZrxXltek4CsM1ERgpIfxJRQ/l0'
verify: True

平文パスワードからハッシュを計算する (ソルトの指定あり)

ソルトを指定してハッシュを計算する。

import passlib.hash
secret = "secretsecret"
salt = "saltsalt"
hashed = passlib.hash.sha512_crypt.hash(secret, salt=salt)
print("sha512('%s') -> '%s'" % (secret, hashed))
print("verify: %s" % passlib.hash.sha512_crypt.verify(secret, hashed))

↑の出力は↓。

sha512('secretsecret') -> '$6$rounds=656000$saltsalt$Toz2h6NEmqzQZDpBJHIq3Bp1mExBVFK7rSGitHQ9F90ji1pHLC/yEAi5fbGkPpIhpMoBkY9icINEdTceZe6Ur.'
verify: True

(おまけ)ハッシュ値からアルゴリズムを特定し、内容をパースする

対象とするアルゴリズムを指定して、そのハッシュ内容をパースする場合は↓のようにすればよい。

import passlib.context
ctx = passlib.context.CryptContext(schemes=["sha256_crypt", "sha512_crypt"])
hashed = "$6$rounds=656000$saltsalt$Toz2h6NEmqzQZDpBJHIq3Bp1mExBVFK7rSGitHQ9F90ji1pHLC/yEAi5fbGkPpIhpMoBkY9icINEdTceZe6Ur."
scheme = ctx.identify(hashed)
if scheme:
  for (k, v) in ctx.handler(scheme).parsehash(hashed).items():
    print("%s: %s" % (k, v))
else:
  print("No Match Scheme.")

↑の出力は↓。

checksum: Toz2h6NEmqzQZDpBJHIq3Bp1mExBVFK7rSGitHQ9F90ji1pHLC/yEAi5fbGkPpIhpMoBkY9icINEdTceZe6Ur.
salt: saltsalt
rounds: 656000

備考

上記を見ると rounds=656000 となっているためラウンド数は656000回だが、 glibcの実装のデフォルトは5000回らしい。

(おまけ)mkpasswdでハッシュ値を計算する

UbuntuやDebianではwhoisのパッケージにmkpasswdコマンドが含まれている。

$ sudo apt install whois

↑でインストールすれば、↓のようにmkpasswdが使える。

$ mkpasswd -h
Usage: mkpasswd [OPTIONS]... [PASSWORD [SALT]]
Crypts the PASSWORD using crypt(3).

      -m, --method=TYPE     select method TYPE
      -5                    like --method=md5
      -S, --salt=SALT       use the specified SALT
      -R, --rounds=NUMBER   use the specified NUMBER of rounds
      -P, --password-fd=NUM read the password from file descriptor NUM
                            instead of /dev/tty
      -s, --stdin           like --password-fd=0
      -h, --help            display this help and exit
      -V, --version         output version information and exit

If PASSWORD is missing then it is asked interactively.
If no SALT is specified, a random one is generated.
If TYPE is 'help', available methods are printed.

Report bugs to <md+whois@linux.it>.

したがって、sha-512で指定のソルト(saltsalt)とパスワード(secretsecret)をrounds=8500のハッシュ値は↓で求められる。

$ mkpasswd -m sha-512 secretsecret saltsalt -R 8500
$6$rounds=8500$saltsalt$3zc0kGtL5yiczEr5gjeMvryyTpemGuYgwOP9LwtXh0sd8f0aNtPKJ2EipeMSxFXf3YFtg0pJx3MqcCLodK.XX.

参考