12

私は、samba 共有、ssh(scp) サーバー、Google アプリなど、他のさまざまなサービスとデータを同期する python/django アプリに取り組んでいます。そのため、これらのサービスにアクセスするには資格情報を保存する必要があります。それらを暗号化されていないフィールドとして保存することは、SQL インジェクション攻撃によって資格情報が取得される可能性があるため、悪い考えだと思います。したがって、保存する前に資格情報を暗号化する必要があります-これを実現するための信頼できるライブラリはありますか?

資格情報が暗号化されたら、使用する前に復号化する必要があります。私のアプリには 2 つの使用例があります。

  • 1 つはインタラクティブです。この場合、ユーザーは資格情報のロックを解除するためのパスワードを提供します。
  • もう 1 つは自動同期です。これは、cron ジョブなどによって開始されます。ここでエクスプロイトのリスクを最小限に抑えるために、パスワードをどこに保管すればよいでしょうか?

または、この問題に対して私が取るべき別のアプローチがありますか?

4

3 に答える 3

12

私は同じ問題を抱えており、過去数日間これを調査しています。@Rostislav によって提示されたソリューションはかなり優れていますが、不完全で少し古くなっています。

アルゴリズム層について

まず、適切にCryptographyと呼ばれる暗号化用の新しいライブラリがあります。PyCrypto の代わりにこのライブラリを使用する理由はたくさんありますが、私が惹かれた主な理由は次のとおりです。

  • 中心的な目標は、自分の足を撃たれないようにすることです。たとえば、MD2 のような非常に古いハッシュ アルゴリズムはありません。
  • それは強力な制度的サポートを持っています
  • さまざまなプラットフォームでの継続的な統合による 500,000 のテスト!
  • 彼らのドキュメンテーション Web サイトには、より優れた SSL 構成があります (平凡な B 評価ではなく、ほぼ完璧な A+ スコア) 。
  • 脆弱性の開示ポリシーがあります。

LWN で新しいライブラリを作成する理由について詳しく読むことができます。

次に、もう 1 つの回答では、SHA1 を暗号化キーとして使用することを推奨しています。SHA1 は危険なほど弱く、弱くなっています。SHA1 の代替は SHA2 であり、その上で、実際にハッシュをソルト化し、 bcryptまたはPBKDF2を使用してストレッチする必要があります。ソルティングはレインボー テーブルに対する保護として重要であり、ストレッチはブルート フォーシングに対する重要な保護です。

(Bcrypt はあまりテストされていませんが、大量のメモリを使用するように設計されており、PBKDF2 は低速になるように設計されており、NIST によって推奨されています。私の実装では、PBKDF2 を使用しています。違いについて詳しく知りたい場合は、こちらをお読みください。)

暗号化には、前述のように、128 ビット キーを使用した CBC モードの AES を使用する必要があります。現在はFernet と呼ばれる仕様にまとめられていますが、これは変更されていません。初期化ベクトルはこのライブラリで自動的に生成されるため、安全に忘れることができます。

鍵の生成および保管レイヤーについて

他の回答は、キーの処理を慎重に検討し、可能であれば OAuth などを選択する必要があることを示唆するのに非常に適切です。しかし、それが不可能であると仮定すると (私の実装ではそうではありません)、Cron ジョブと対話型の 2 つのユース ケースがあります。

cron ジョブの使用例は、キーを安全な場所に保管し、それを使用して cron ジョブを実行する必要があるという事実に要約されます。私はこれを研究していないので、ここでは意見を述べません。これを行うには多くの良い方法があると思いますが、最も簡単な方法はわかりません。

対話型のユース ケースでは、ユーザーのパスワードを収集し、それを使用してキーを生成し、そのキーを使用して保存されている資格情報を復号化する必要があります。

持ち帰り

暗号化ライブラリを使用して、上記のすべてを行う方法は次のとおりです。

from cryptography.fernet import Fernet
from cryptography.hazmat.primitives.hashes import SHA256
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend

secret = "Some secret"

# Generate a salt for use in the PBKDF2 hash
salt = base64.b64encode(os.urandom(12))  # Recommended method from cryptography.io
# Set up the hashing algo
kdf = PBKDF2HMAC(
    algorithm=SHA256(),
    length=32,
    salt=str(salt),
    iterations=100000,  # This stretches the hash against brute forcing
    backend=default_backend(),  # Typically this is OpenSSL
)
# Derive a binary hash and encode it with base 64 encoding
hashed_pwd = base64.b64encode(kdf.derive(user_pwd))

# Set up AES in CBC mode using the hash as the key
f = Fernet(hashed_pwd)
encrypted_secret = f.encrypt(secret)

# Store the safe inputs in the DB, but do NOT include a hash of the 
# user's password, as that is the key to the encryption! Only store 
# the salt, the algo and the number of iterations.
db.store(
    user='some-user', 
    secret=encrypted_secret,
    algo='pbkdf2_sha256', 
    iterations='100000', 
    salt=salt
)

復号化は次のようになります。

# Get the data back from your database
encrypted_secret, algo, iterations, salt = db.get('some-user')

# Set up the Key Derivation Formula (PBKDF2)
kdf = PBKDF2HMAC(
    algorithm=SHA256(),
    length=32,
    salt=str(salt),
    iterations=int(iterations),
    backend=default_backend(),
)
# Generate the key from the user's password
key = base64.b64encode(kdf.derive(user_pwd))

# Set up the AES encryption again, using the key
f = Fernet(key)

# Decrypt the secret!
secret = f.decrypt(encrypted_secret)
print("  Your secret is: %s" % secret)

攻撃?

DB がインターネットに流出したとします。攻撃者は何ができますか? 暗号化に使用したキーは、ユーザーのソルト化されたパスワードの 100,000 番目の SHA256 ハッシュを取得しました。ソルトと暗号化アルゴリズムをデータベースに保存しました。したがって、攻撃者は次のいずれかを行う必要があります。

  • ハッシュのブルート フォースを試す: ソルトと考えられるすべてのパスワードを組み合わせて、100,000 回ハッシュします。そのハッシュを取得して、復号化キーとして試してください。攻撃者は、1 つのパスワードを試すために 100,000 回のハッシュを実行する必要があります。これは基本的に不可能です。
  • 可能なすべてのハッシュを復号化キーとして直接試してください。これは基本的に不可能です。
  • 事前に計算されたハッシュでレインボー テーブルを試してみませんか? いいえ、ランダムな塩が関係している場合ではありません。

これはかなり固いと思います。

ただし、もう1つ考えるべきことがあります。PBKDF2 は遅くなるように設計されています。多くの CPU 時間を必要とします。これは、ユーザーが PBKDF2 ハッシュを生成する方法がある場合、DDOS 攻撃にさらされていることを意味します。これに備えてください。

あとがき

これはすべて言った、私はあなたのためにこれのいくつかを行うライブラリがあると思います. django encrypted fieldなどについては、Google で検索してください。それらの実装について約束することはできませんが、おそらく他の人がこれをどのように行ったかについて何かを学ぶでしょう.

于 2015-05-31T19:01:16.783 に答える
1

多数のシステムにログインするのに十分な資格情報をサーバーに最初に保存することは、悪夢のように見えます。サーバー上のコードを侵害すると、暗号化が何であれ、それらすべてが漏洩します.

タスク (つまり、ファイルの同期) を実行するために必要な資格情報のみを保存する必要があります。サーバーの場合はRSyncなどの同期サーバーの使用を検討する必要があります。Google の場合はOAuthなどのプロトコルを使用する必要があります。この方法では、サーバーが侵害された場合、システムへのアクセスではなくデータのみが漏洩します。

次は、これらの資格情報の暗号化です。暗号化については、PYCryptoを使用することをお勧めします。

暗号化で使用するすべての乱数について、十分に強力であることを確認するために、Crypto.Random (またはその他の強力な方法) によって乱数を生成します。

同じキーで異なる資格情報を暗号化しないでください。私がお勧めする方法は次のとおりです。

  1. サーバーには、マスター シークレットM (/dev/random から派生) が必要です。root が所有し、root のみが読み取り可能なファイルに保存します。
  2. サーバーがルート権限で起動すると、ファイルがメモリに読み込まれ、クライアントにサービスを提供する前にその権限が削除されます。これは、Web サーバーやその他のデーモンにとって通常の慣習です。
  3. 新しい資格情報を作成する (または既存の資格情報を更新する) 場合は、ランダム ブロックSを生成します。前半を取り、ハッシュK=H(S 1 ,M)を計算します。それが暗号化キーになります。
  4. CBC モードを使用してデータを暗号化します。S 2から初期化ベクトル (IV)を取得します。
  5. Sを暗号化されたデータと一緒に保存します

復号化する必要がある場合は、 Sを取り出してKを作成し、同じ IV で復号化します。

ハッシュにはSHA1、暗号化にはAESをお勧めします。ハッシュと対称暗号は十分に高速であるため、より大きな鍵サイズを使用しても問題はありません。

このスキームは、いくつかの場所で少し行き過ぎていますが、これも害にはなりません。

ただし、資格情報を保存する最善の方法は、資格情報を保存しないことです。必要な場合は、タスクを実行できる最小限の権限を持つものを使用してください。

于 2012-10-16T07:12:10.663 に答える
-2

作成することにより、マルチユーザースキームに頼ることができるかもしれません:

  • 資格情報にアクセスする権限を持たないDjango を実行しているユーザー (例: django)
  • これらの権限を持つユーザー (例: sync)。

両方ともdjangoグループに参加して、アプリにアクセスできるようにすることができます。その後、必要manage.py sync-externalなものを同期するスクリプト (たとえば などの Django コマンド) を作成します。

これにより、djangoユーザーはアプリと同期スクリプトにアクセスできますが、資格情報にはアクセスできません。これは、ユーザーだけsyncアクセスできるためです。誰かが資格情報なしでそのスクリプトを実行しようとすると、もちろんエラーになります。

Linux パーミッション モデルに依存することは、私の意見では "良い考え" ですが、私はセキュリティの専門家ではないので、そのことを心に留めておいてください。上記の内容について何か言いたいことがあれば、ためらわないでください。

于 2012-10-15T22:10:51.243 に答える