web-dev-qa-db-ja.com

AES-暗号化による暗号化(node-js)/ Pycryptoによる復号化(python)

私はこの問題と答えを書いています。私は多くの苦労(おそらく経験不足のため)をしていて、nodeやpythonで物事を暗号化/復号化する多くの異なる方法で道に迷ってしまいました。

私の事件は将来人々を助けるかもしれないと思った。

私がする必要があったこと:

  • フォームからデータを取得し、Crypto(node-js)を使用して暗号化する
  • 暗号化されたデータをPythonに渡し、PyCryptoを使用して復号化します。

私はAES暗号化を使用することにしました。

ここに私が始めた方法があります(私が試したすべてを通過するつもりはありません):

  • このページ の最後にある例に従いました

    私の場合、それは与えました:

    (これはjavascriptとcoffeescriptの間の非常に悪い組み合わせかもしれません)

    crypto = require "crypto"
    [...]
    key = "mykeywhatever"
    cipher = crypto.createCipher('aes192', key)
    cipher.update('string i want to encode', 'binary', 'hex')
    encoded_string = cipher.final('hex')
    [...]
    

    これは私の文字列をエンコードするのにかなりうまくいきました。

  • 次に、このpythonこの文字列を復号化するスクリプトを PyCryptoのgithubのページ)のreadmeを使用して記述しました

    from Crypto.Cipher import AES
    [...]
    my_string = data_coming_from_rabbitmq
    obj = AES.new('mykeywhatever', AES.MODE_CBC)
    obj.decrypt(ciphertext)
    [...]
    

    これは明らかに機能しませんでした:readmeにはIVがありますが、ノードスクリプトでIVを指定しなかったので、なぜpython one?

さらにグーグルで調べたところ、ノードのCryptoはOpenSSLを使用していますが、PyCryptoは明らかに使用していないことを知りました。だから私はそれを調べて、それらのページを見つけました:

だから物事は複雑になり、誰も同じことをしてデータを復号化していないので、迷ってしまい、助けを求めました。

答えは、私の同僚と私が思いついたものです(まあ、主に私の同僚)。

18
nnaelle

そこで、「解読方法... OpenSSL」の回答から始めました。

  • 次のような暗号化スクリプトを変更する必要があります。

    crypto = require "crypto"
    [...]
    var iv = new Buffer('asdfasdfasdfasdf')
    var key = new Buffer('asdfasdfasdfasdfasdfasdfasdfasdf')
    var cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
    cipher.update(new Buffer("mystring"));
    var enc = cipher.final('base64');
    [...]
    

    ivは16バイトの長さにする必要があり、keyは32バイトです。そして、createCiphercreateCipherivに変更しました。

  • python復号化スクリプトに戻る:

    プロセスは単にPyCryptoのドキュメントを読んで、 (最初のコードと)を比較する でした。

    それから APIに固執する だけにして、ゼロから始めることにしました。そしてそれは与えました:

    from base64 import b64decode
    from Crypto.Cipher import AES
    [...]
    iv = 'asdfasdfasdfasdf'
    key = 'asdfasdfasdfasdfasdfasdfasdfasdf'
    encoded = b64decode('my_encrypted_string')
    
    dec = AES.new(key=key, mode=AES.MODE_CBC, IV=iv)
    value = dec.decrypt(encoded)
    

そして、それはそれと同じくらい簡単でした...それがあなたの一部を助けることを願っています!

更新:

Perseidsが彼の回答のコメントに書いたように、IVはランダムで、メッセージごとに異なる必要があります

15
nnaelle

構築しているシステムはおそらく安全ではありません

ストレージを除いて、基本的には単にデータを暗号化するだけでなく、authenticate暗号化もしたくありません。このコンテキストでの認証とは、有効なメッセージを生成できるのは、キーを知っている人だけであることを意味します。広く使用されている認証方式は [〜#〜] hmac [〜#〜] です。

メッセージを認証しないと、誰でもデータをサービスに送ることができます。攻撃者は復号化後の結果を完全に制御できない可能性がありますが、それでも非常に危険な場合があります。たとえば、CBC(使用している)と最も一般的なパディングスキーム(AESはブロック暗号であり、128ビットのデータブロックしか暗号化できない)を使用し、攻撃者がパディングエラーと他のエラーを区別できる場合すべてのメッセージは攻撃者によって復号化される可能性があります。これは パディングOracle攻撃 と呼ばれ、farが一般的です。

このクラスの攻撃から保護するには、認証された暗号化スキームを使用できます。たとえば、 [〜#〜] gcm [〜#〜 ] ブロック暗号モード。

また、リプレイ攻撃から保護する必要があります。銀行のアプリケーションを考えてみましょう。送信するデータは銀行振込の注文です。 TANがなければ、攻撃者は以前のトランザクションを記録し、このトランザクションをサービスに何度も繰り返して、顧客が当初望んでいた複数の金額を送金する可能性があります。

HTTPSを介して送信されるデータを取得しているフォームですか?そうでない場合:キーは攻撃者によって盗聴される可能性がありますか?ユーザーは、フォームをあなたからではなく他の誰からも入手したことをどのようにして知るのですか(SSL/TLSは、機密性だけでなく認証も重要です)。

たぶん、私は単純なCBC暗号化が提供する他の攻撃ベクトルをいくつか忘れてしまいました。

代替案

おそらくこれらの攻撃から保護する最も簡単な方法は、フォームデータをHTTPS経由で送信することです。 SSL/TLSは、上記のすべての攻撃を防ぐように設計されており、クライアントおよびサーバー側の実装は成熟するまでに長い時間がかかりました。

8
Perseids