6

tl;dr -- SQLAlchemy を使用して MySQL DB にパスワードを挿入する前に、PassLib などの Python 側のライブラリを使用してパスワードをハッシュするにはどうすればよいですか?

さて、私はこれを理解しようとして1日か2日机に頭をぶつけていたので、ここに行きます:

Pyramid/SQLAlchemy を使用して Web アプリケーションを作成しており、MySQL データベースの Users テーブルとやり取りしようとしています。

最終的には、次のようなことをしたいと考えています。

パスワードをハッシュと比較します。

if user1.password == 'supersecret'

新しいパスワードを挿入します。

user2.password = 'supersecret'

パスワードがデータベースに送られる前に PassLib を使用してパスワードをハッシュできるようにしたいのですが、ソルト化されていないため、組み込みの MySQL SHA2 関数を使用するのは好きではありません。

ただし、試してみると、SQL側の関数を使用してこれが機能しています。

from sqlalchemy import func, TypeDecorator, type_coerce
from sqlalchemy.dialects.mysql import CHAR, VARCHAR, INTEGER
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column

class SHA2Password(TypeDecorator):
  """Applies the SHA2 function to incoming passwords."""
  impl = CHAR(64)

  def bind_expression(self, bindvalue):
    return func.sha2(bindvalue, 256)

  class comparator_factory(CHAR.comparator_factory):
    def __eq__(self, other):
      local_pw = type_coerce(self.expr, CHAR)
      return local_pw == func.sha2(other, 256)

class User(Base):
  __tablename__ = 'Users'
  _id = Column('userID', INTEGER(unsigned=True), primary_key=True)
  username = Column(VARCHAR(length=64))
  password = Column(SHA2Password(length=64))

  def __init__(self, username, password):
    self.username = username
    self.password = password

これは、http://www.sqlalchemy.org/trac/wiki/UsageRecipes/DatabaseCryptの例 2 からコピーしたものです。

これで動作し、組み込みの MySQL SHA2 関数を ( を呼び出すことでfunc.sha2()) 使用して、自分のやりたいことを正確に行うことができます。ただし、現在、これを Python 側の PassLib に置き換えようとしています。

PassLib は 2 つの関数を提供します。1 つは新しいパスワード ハッシュを作成するためのもので、もう 1 つはパスワードを検証するためのものです。

from passlib.hash import sha256_crypt

new_password = sha256_crypt.encrypt("supersecret")

sha256_crypt.verify("supersecret", new_password)

これを実際に実装する方法がよくわかりません。すべてのドキュメントを読んだ結果、TypeDecorator の別の形式、カスタム型宣言、ハイブリッド値、またはハイブリッド プロパティのいずれかであると思います。私はこれに従ってみましたが、それは本当に意味がなく、そこに提案されているコードは実際に実行されません。

では、私の質問を要約すると、=and==演算子をオーバーロードして、適切なハッシュ関数を介して実行するにはどうすればよいでしょうか?

4

2 に答える 2

6

この問題には、sqlalchemy-utils のPasswordTypeが最適です。passlib を使用します。ドキュメントから抜粋:

次の使用法は、新しいパスワードを pbkdf2_sha512 として自動的にハッシュするパスワード列を作成しますが、既存の md5_crypt ハッシュとパスワードを比較します。パスワードが比較されるとき。データベースのパスワード ハッシュは pbkdf2_sha512 に更新されます。

class Model(Base):
    password = sa.Column(PasswordType(
        schemes=[
            'pbkdf2_sha512',
            'md5_crypt'
        ],
        deprecated=['md5_crypt']
    ))

パスワードの確認は次のように簡単です。

target = Model()
target.password = 'b'
# '$5$rounds=80000$H.............'
target.password == 'b'
# True
于 2015-09-28T14:43:23.917 に答える
3

私が理解しているように、あなたが望むのはこれです:

  1. アカウント作成時にユーザーのパスワードを暗号化します。あなたの塩とアルゴリズムを使用してください
  2. ユーザーがログインすると、パスワードを保存したときと同じ方法でパスワードをハッシュします。
  3. db リクエストで通常の文字列比較を使用して 2 つのハッシュを比較します

したがって、ログインコードは次のようになります。

from passlib.hash import sha256_crypt
passHash = sha256_crypt.encrypt(typed_password)
// call your sqlalchemy code to query the db with this value (below)

// In your SQLAlchemy code assuming "users" is your users table
// and "password" is your password field
s = users.select(and_(users.username == typed_username, users.password == passHash))
rs = s.execute()

rs は、一致するユーザーの結果セットになります (もちろん、0 または 1 である必要があります)。

免責事項 - 私はこれをテストしていません

編集: PassLib が実行されるたびに異なるソルトを使用することを指摘していただきありがとうございます。その場合の最善の策は、sqlalchemy でそれを行う簡単な方法がないように思われるため、以下のとおりです。

s=users.select(users.username == typed_username)
rs = s.execute()
userRow = rs.fetchone()
if (sha256_crypt.verify(userRow.password)):
    # you have a match

また、抽象化の要求に対処するには: この操作を処理するための一般的な方法論は、渡されたログイン資格情報に一致するユーザー (オブジェクト) を取得するための「セキュリティ」ユーティリティ クラスを作成することです。

現在のセットアップの問題は、User コンストラクターには、関連はあるものの必ずしも同じではない 2 つの異なる操作上の目標があることです: ユーザーの認証と User オブジェクトの取得 (たとえば、グループ内のユーザーのリスト)。その場合、コンストラクターは不必要に複雑になります。ユーザー名/パスワードの代わりにセッション ID または SSO トークンを介してユーザーをログインさせるなど、他のセキュリティまたはログイン関連の機能でカプセル化できる場所にそのロジックを配置することをお勧めします。

security.loginUser(username, password)
# or security.loginUser(single_sign_on_token), etc. for polymorphic Security
loggedInUser = security.getLoggedInUser()

... later ...
otherUser = User(username) #single job, simple, clean
于 2013-02-20T04:35:31.693 に答える