8

私はRubyについてほとんど知らないので、これに対する答えが明らかである場合はご容赦ください。http://www.ruby-doc.org/stdlib-1.9.3/libdoc/securerandom/rdoc/SecureRandom.htmlで、random_bytes の呼び出し時に Ruby が pid と現在の時刻を使用して OpenSSL::Random をシードしていることに気付きました作られています。裏で何か別のことが起きない限り、これは Netscape が 90 年代半ばに最初の SSL 実装で使用した種ではないでしょうか? http://en.wikipedia.org/wiki/Random_number_generator_attack#Prominent_examples_of_random_number_generator_security_issues

確かに、Ruby は 18 年前のバグを復活させていません。ここで何が欠けていますか?

編集: random_bytes のソースは次のとおりです。ruby が OpenSSL でコンパイルされているかどうかを確認する最初のチェックに注意してください。この場合、pid と現在の時刻がシードされます。

def self.random_bytes(n=nil)
  n = n ? n.to_int : 16

  if defined? OpenSSL::Random
    @pid = 0 if !defined?(@pid)
    pid = $$
    if @pid != pid
      now = Time.now
      ary = [now.to_i, now.nsec, @pid, pid]
      OpenSSL::Random.seed(ary.to_s)
      @pid = pid
    end
    return OpenSSL::Random.random_bytes(n)
  end

  if !defined?(@has_urandom) || @has_urandom
    flags = File::RDONLY
    flags |= File::NONBLOCK if defined? File::NONBLOCK
    flags |= File::NOCTTY if defined? File::NOCTTY
    begin
      File.open("/dev/urandom", flags) {|f|
        unless f.stat.chardev?
          raise Errno::ENOENT
        end
        @has_urandom = true
        ret = f.readpartial(n)
        if ret.length != n
          raise NotImplementedError, "Unexpected partial read from random device: only #{ret.length} for #{n} bytes"
        end
        return ret
      }
    rescue Errno::ENOENT
      @has_urandom = false
    end
  end

  if !defined?(@has_win32)
    begin
      require 'Win32API'

      crypt_acquire_context = Win32API.new("advapi32", "CryptAcquireContext", 'PPPII', 'L')
      @crypt_gen_random = Win32API.new("advapi32", "CryptGenRandom", 'LIP', 'L')

      hProvStr = " " * 4
      prov_rsa_full = 1
      crypt_verifycontext = 0xF0000000

      if crypt_acquire_context.call(hProvStr, nil, nil, prov_rsa_full, crypt_verifycontext) == 0
        raise SystemCallError, "CryptAcquireContext failed: #{lastWin32ErrorMessage}"
      end
      @hProv, = hProvStr.unpack('L')

      @has_win32 = true
    rescue LoadError
      @has_win32 = false
    end
  end
  if @has_win32
    bytes = " ".force_encoding("ASCII-8BIT") * n
    if @crypt_gen_random.call(@hProv, bytes.size, bytes) == 0
      raise SystemCallError, "CryptGenRandom failed: #{lastWin32ErrorMessage}"
    end
    return bytes
  end

  raise NotImplementedError, "No random device"
end
4

3 に答える 3

6

使用されているシードは、SecureRandomPID がリサイクルされるたびに発生する予測可能な乱数を禁止します。で修正しないとSecureRandom、OpenSSL の乱数ジェネレーターは、同じ PID を持つ異なるプロセスでまったく同じ値を生成します。

#4579は、これがどのように発生するかを概説しており、OpenSSL のメーリング リストの対応するエントリは、多かれ少なかれ、クライアント コードでこれを処理する必要があることを示しています。これが、セキュリティの脅威を防ぐために Ruby でこのシードが選択された理由です。確信が持てない場合は、この修正の前に Ruby バージョンに添付されたスクリプトEric Wong を実行して、これが何であるかを確認してください。

owlstead の説明に加えて、この時点で OpenSSL の RNG をシードしてもセキュリティは損なわれませんRAND_poll。これは、値が以前にシード/追加されたかどうかに関係なく、初期化されていないランダム ジェネレーターが常に最初に呼び出され、十分なエントロピーが収集されるためです。

ただし、シード値SecureRandomは明らかに予測可能であるため、エントロピーが追加されると想定すべきではありません。OpenSSL の内部動作は、ある時点で変更される可能性があり、すでにシードされている値に十分なエントロピーが含まれていると見なされる場合、最初のエントロピー収集がスキップされる可能性があります。

したがって、私は#6928を開きました。これは、さまざまなプロセスを区別するエントロピー プールに追加された値にエントロピーがないと仮定する、より防御的なアプローチを選択します。これにより、OpenSSL はすべてのケースで十分なエントロピーを確実に収集するようになります。

結論として、値 (PID と時間) の選択は賢明なものであり、全体的なセキュリティを低下させるのではなく、(「リサイクルされた PID 攻撃」を防ぐことによって) さらに追加します。

于 2012-08-27T03:55:52.713 に答える
3

どの RNG が使用されているかは、Ruby の構成によって異なります。

安全な乱数ジェネレーター インターフェイス。

このライブラリは、HTTP Cookie などでセッション キーを生成するのに適したセキュアな乱数ジェネレーターのインターフェイスです。

以下の安全な乱数ジェネレータをサポートしています。

  • openssl

  • /dev/urandom

  • Win32

上記の 3 つすべてが一般的に安全であると見なされます。SecureRandomただし、実際に安全かどうかは、クラスの実装に依存します。これを知る唯一の方法は、実装に関する広範な調査です。

問題のコードを見ると、PIDを追加でシードした後、Ruby が OpenSSL によって生成されたバイトを直接使用していることは明らかです。

シード データが追加されるたびに、次のように「状態」に挿入されます。

入力は 20 バイト単位 (最後のブロックはそれ以下) に分割されます。これらのブロックのそれぞれは、次のようにハッシュ関数を介して実行されます。 'block'、新しいキー データ 'block'、および 'count' (使用するたびにインクリメントされます)。この結果は「md」に保持され、ハッシュ関数への入力として使用されたのと同じ場所で「状態」にも xor されます。このシステムは、ポイント 1 (ハッシュ関数、現在は SHA-1)、3 (「状態」)、4 (「md」経由)、5 (ハッシュ関数と xor の使用による) に対応していると思います。

于 2012-08-25T15:31:54.303 に答える
2

私の同僚がこれを調査したところ、シードの選択がこのバグへの対応として導入されたことがわかりました。

http://bugs.ruby-lang.org/issues/4579

幸いなことに、OpenSSL は、コンパイル方法に応じて、/dev/urandom (利用可能な場合) または egd (「エントロピー収集デーモン」 -- /dev/urandom の前身) から 256 ビットのエントロピーをシードします。シードは、RAND_status() または RAND_bytes() が初めて呼び出されたときに自動的に行われ、RAND_seed() が明示的に呼び出された場合は抑制されません。この決定について、OpenSSL 関係者に敬意を表します。特定の OpenSSL コードへのリンクは次のとおりです。

http://cvs.openssl.org/dir?d=openssl/crypto/rand

興味深いファイルは、md_rand.c、rand_lib.c、および rand_unix.c です。

于 2012-08-25T18:08:27.433 に答える