Pythonでパスワードをソルトしてハッシュする


97

このコードは、パスワードをソルトでハッシュすることになっています。ソルトとハッシュ化されたパスワードはデータベースに保存されています。パスワード自体はそうではありません。

操作のデリケートな性質を考慮して、私はすべてがコーシャであることを確認したかった。

import hashlib
import base64
import uuid

password = 'test_password'
salt     = base64.urlsafe_b64encode(uuid.uuid4().bytes)


t_sha = hashlib.sha512()
t_sha.update(password+salt)
hashed_password =  base64.urlsafe_b64encode(t_sha.digest())

なぜb64でsaltをエンコードしているのですか?ソルトを直接使用してから、b64で両方を一緒にエンコードする方が簡単t_sha.digest() + saltです。デコードされたハッシュパスワードは正確に32バイトであることがわかっているので、ソルトハッシュパスワードをデコードした後で、ソルトを再度分割できます。
ダンカン

1
@ Duncan-奇妙な問題を心配することなく、ソルトに対して強力な操作を実行できるように、base64でソルトをエンコードしました。「バイト」バージョンは文字列として機能しますか?その場合は、t_sha.digest()をbase64エンコードする必要もありません。ハッシュ化されたパスワードとソルトを一緒に保存することはおそらくないでしょう。なぜなら、それがもう少し複雑で、少し読みにくいように見えるからです。
Chris Dutrow 2012年

Python 2.xを使用している場合、bytesオブジェクトは文字列として完全に機能します。Pythonは、文字列に含めることができるものに制限を設けていません。ただし、データベースなどの外部コードに文字列を渡す場合は、同じことが当てはまらない可能性があります。Python 3.xはバイトタイプと文字列を区別するため、その場合、ソルトで文字列操作を使用することは望ましくありません。
ダンカン

4
Pythonでそれを行う方法を説明することはできませんが、プレーンなSHA-512は悪い選択です。PBKDF2、bcrypt、scryptなどの低速ハッシュを使用してください。
CodesInChaos 2012年

補足:暗号のランダム性のソースとしてUUIDを使用しないことをお勧めします。はい、CPython使用される実装は暗号的に安全ですが、Pythonの仕様やUUID仕様によって規定されておらず、脆弱な実装が存在します。安全なUUID4を使用せずにPython実装を使用してコードベースを実行すると、セキュリティが弱まります。それはありそうもないシナリオかもしれませんが、secrets代わりに使用するのに費用はかかりません。
マークアメリー

回答:


49

編集:この答えは間違っています。SHA512の1回の反復は高速であるため、パスワードハッシュ関数としての使用には不適切です。代わりに、ここで他の回答の1つを使用してください。


私には元気そうです。ただし、実際にはbase64は必要ないと確信しています。あなたはこれを行うことができます:

import hashlib, uuid
salt = uuid.uuid4().hex
hashed_password = hashlib.sha512(password + salt).hexdigest()

問題が発生しない場合は、ソルトとハッシュ化されたパスワードを16進文字列ではなく生のバイトとして保存することで、データベースにわずかに効率的なストレージを取得できます。そのためには、交換するhexbytesしてhexdigestdigest


1
はい、16進数は問題なく機能します。文字列が少し短いので、base64が好きです。短い文字列を渡して操作を行う方が効率的です。
Chris Dutrow 2012年

では、パスワードを取り戻すためにどのように逆にするのですか?
nodebase 2014

28
パスワードを元に戻すことはありません。パスワードを元に戻すことはありません。そのため、ハッシュし、暗号化しません。入力パスワードを保存されているパスワードと比較する必要がある場合は、入力をハッシュしてハッシュを比較します。パスワードを暗号化すると、そのキーを持っている人なら誰でもパスワードを復号化して見ることができます。安全ではありません
Sebastian Gabriel Vinci

4
uuid.uuid4()。hexは、生成されるたびに異なります。同じuuidを取り戻せない場合、チェックのためにパスワードをどのように比較しますか?
LittleBobbyTables 2015年

3
@LittleBobbyTablessaltデータベースと塩辛いハッシュパスワードにも保存されていると思います。
clemtoy 2015年

72

この質問に対する他の回答に基づいて、bcryptを使用して新しいアプローチを実装しました。

なぜbcryptを使うのか

私が正しく理解していれば、bcryptオーバーを使用するという議論SHA512は、bcrypt遅くなるように設計されているということです。bcryptハッシュ化されたパスワードを初めて生成するときの速度を調整するオプションもあります。

# The '12' is the number that dictates the 'slowness'
bcrypt.hashpw(password, bcrypt.gensalt( 12 ))

悪意のあるパーティがハッシュされたパスワードを含むテーブルを手にした場合、ブルートフォース攻撃がはるかに困難になるため、低速が望ましいです。

実装

def get_hashed_password(plain_text_password):
    # Hash a password for the first time
    #   (Using bcrypt, the salt is saved into the hash itself)
    return bcrypt.hashpw(plain_text_password, bcrypt.gensalt())

def check_password(plain_text_password, hashed_password):
    # Check hashed password. Using bcrypt, the salt is saved into the hash itself
    return bcrypt.checkpw(plain_text_password, hashed_password)

ノート

以下を使用して、Linuxシステムにライブラリを非常に簡単にインストールできました。

pip install py-bcrypt

しかし、Windowsシステムにインストールするのにもっと苦労しました。パッチが必要なようです。このスタックオーバーフローの質問を参照してください:Win 764ビットPythonにインストールされているpy-bcrypt


4
12はgensaltのデフォルト値です
Ahmed

2
pypi.python.org/pypi/bcrypt/3.1.0によると、bcryptの最大パスワード長は72バイトです。それを超える文字はすべて無視されます。このため、最初に暗号化ハッシュ関数を使用してハッシュし、次にハッシュをbase64エンコードすることをお勧めします(詳細については、リンクを参照してください)。py-bcrypt補足:古いpypiパッケージのようで、名前がに変更されましたbcrypt
balu 2018

48

賢いのは、自分で暗号を書くのではなく、passlibのようなものを使用することです:https://bitbucket.org/ecollins/passlib/wiki/Home

安全な方法で暗号コードを書くのを台無しにするのは簡単です。厄介なのは、暗号化されていないコードでは、プログラムがクラッシュして機能していないときにすぐに気付くことがよくあるということです。暗号コードを使用していると、遅くなってデータが危険にさらされて初めてわかることがよくあります。そのため、この主題について知識があり、戦闘でテストされたプロトコルに基づいている他の誰かによって書かれたパッケージを使用する方が良いと思います。

また、passlibには、使いやすく、古いプロトコルが壊れていることが判明した場合に新しいパスワードハッシュプロトコルに簡単にアップグレードできる優れた機能がいくつかあります。

また、sha512の1ラウンドだけが、辞書攻撃に対してより脆弱です。sha512は高速になるように設計されており、パスワードを安全に保存しようとすると、これは実際には悪いことです。他の人々はこの種の問題すべてについて長く懸命に考えてきたので、あなたはこれをよりよく利用することができます。


5
crypoライブラリを使用するというアドバイスは良いと思いますが、OPはすでにhashlibを使用しています。これは、(passlibとは異なり)Python標準ライブラリにもある暗号ライブラリです。OPの状況にある場合は、引き続きhashlibを使用します。
dgh 2012年

18
@dghubblehashlibは、暗号化ハッシュ関数用です。passlibパスワードを安全に保管するためのものです。それらは同じものではありません(多くの人がそう考えているようですが..そしてユーザーのパスワードが解読されます)。
ブレンダンロング

3
誰かが疑問に思っている場合:passlib返されたハッシュ文字列に格納される独自のソルトを生成します(少なくともBCrypt + SHA256などの特定のスキームでは)-したがって、それについて心配する必要はありません。
z0r 2015年

22

これをPython3で機能させるには、たとえばUTF-8エンコードする必要があります。

hashed_password = hashlib.sha512(password.encode('utf-8') + salt.encode('utf-8')).hexdigest()

それ以外の場合は、次のようになります。

トレースバック(最後の最後の呼び出し):
ファイル ""、1行目、
hashed_pa​​ssword = hashlib.sha512(password + salt).hexdigest()
TypeError:Unicode-オブジェクトはハッシュする前にエンコードする必要があります


7
いいえ。パスワードのハッシュにshaハッシュ関数を使用しないでください。bcryptのようなものを使用してください。理由については、他の質問へのコメントを参照してください。
josch 2018年

13

Python 3.4以降、hashlib標準ライブラリのモジュールには、「安全なパスワードハッシュ用に設計された」鍵導出関数が含まれています

したがって、を使用hashlib.pbkdf2_hmacして生成されたソルトで、のようなものの1つを使用しos.urandomます。

from typing import Tuple
import os
import hashlib
import hmac

def hash_new_password(password: str) -> Tuple[bytes, bytes]:
    """
    Hash the provided password with a randomly-generated salt and return the
    salt and hash to store in the database.
    """
    salt = os.urandom(16)
    pw_hash = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)
    return salt, pw_hash

def is_correct_password(salt: bytes, pw_hash: bytes, password: str) -> bool:
    """
    Given a previously-stored salt and hash, and a password provided by a user
    trying to log in, check whether the password is correct.
    """
    return hmac.compare_digest(
        pw_hash,
        hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)
    )

# Example usage:
salt, pw_hash = hash_new_password('correct horse battery staple')
assert is_correct_password(salt, pw_hash, 'correct horse battery staple')
assert not is_correct_password(salt, pw_hash, 'Tr0ub4dor&3')
assert not is_correct_password(salt, pw_hash, 'rosebud')

ご了承ください:

  • 16バイトのソルトの使用とPBKDF2の100000回の反復は、Pythonドキュメントで推奨されている最小数と一致します。反復回数をさらに増やすと、ハッシュの計算が遅くなり、安全性が高まります。
  • os.urandom 常に暗号的に安全なランダム性のソースを使用します
  • hmac.compare_digestで使用されるis_correct_password、は基本的==に文字列の演算子ですが、短絡する機能がないため、タイミング攻撃の影響を受けません。それはおそらく実際には追加のセキュリティ値を提供しませんが、それも害はないので、私は先に進んでそれを使用しました。

優れたパスワードハッシュを作成するための理論と、パスワードのハッシュに適した他の関数のリストについては、https://security.stackexchange.com/q/211/29805を参照してください


11

passlibは、既存のシステムによって保存されたハッシュを使用する必要がある場合に役立つようです。フォーマットを制御できる場合は、bcryptやscryptなどの最新のハッシュを使用してください。現時点では、bcryptはPythonからはるかに使いやすいようです。

passlibはbcryptをサポートしており、バックエンドとしてpy-bcryptをインストールすることをお勧めします:http//pythonhosted.org/passlib/lib/passlib.hash.bcrypt.html

passlibをインストールしたくない場合は、py-bcryptを直接使用することもできます。readmeには基本的な使用例があります。

参照:Pythonでscryptを使用してパスワードとソルトのハッシュを生成する方法



0

最初にインポート:-

import hashlib, uuid

次に、メソッドでこれに従ってコードを変更します。

uname = request.form["uname"]
pwd=request.form["pwd"]
salt = hashlib.md5(pwd.encode())

次に、このソルトとunameをデータベースのSQLクエリに渡します。ログインの下には、テーブル名があります。

sql = "insert into login values ('"+uname+"','"+email+"','"+salt.hexdigest()+"')"

uname = request.form ["uname"] pwd = request.form ["pwd"] salt = hashlib.md5(pwd.encode())次に、このソルトとunameをデータベースのSQLクエリに渡します。ログインの下にテーブル名があります。 :は- SQL = "ログイン値への挿入( ' "+は、uname +"'、 ' "+メール+"'、 ' "+ salt.hexdigest()+"')"
Sheetal Jhaに

-1 md5は非常に高速であるため、md5の1回の反復を使用することは、パスワードハッシュ関数には適していません。
マークアメリー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.