13

着信 TCP 接続を受け入れるサーバーを作成しています。サーバーが TCP 接続を受け入れ、クライアントから既に 16 (またはそれくらい) バイトを受信して​​いるとします。これらの 16 バイトを知っている場合、サーバーはクライアントが SSL ハンドシェイクを開始する必要があるかどうかをどのように検出できますか?

実験を行ったところ、SSL 経由で localhost (127.0.0.1 または AF_UNIX) に接続している Linux システムで、クライアントが次のハンドシェイク (hexdump) を送信し、その後に一見ランダムな 16 バイトが送信されることが示されました。

8064010301004b0000001000003900003800003500001600001300000a07
00c000003300003200002f03008000000500000401008000001500001200
0009060040000014000011000008000006040080000003020080

クライアントが SSL ハンドシェイクを送信しているかどうかを判断できるようにするために、サーバーはこれらの最初の数バイトをどのようにプローブする必要がありますか? プローブは、すべての有効な SSL ハンドシェイクに対して true を返す必要があり、SSL ハンドシェイクではないクライアントによって送信されたメッセージに対して高い確率で false を返す必要があります。プローブにライブラリ (OpenSSL など) を使用することは許可されていません。プローブは単純なコードでなければなりません (C または Python の数十行のような)。

4

2 に答える 2

18

クライアントは常に、いわゆる HelloClient メッセージを最初に送信します。SSL 2 形式または SSL 3.0 形式 (TLS 1.0、1.1、および 1.2 と同じ形式) にすることができます。

また、SSL 3.0/TLS 1.0/1.1/1.2 クライアントが古い形式 (SSL 2) で HelloClient を送信し、データのバージョン番号が高くなる可能性もあります。したがって、SSL 2 HelloClient の検出は、新しいクライアントにも必要です。(たとえば、Java SSL 実装はそうします)

「b」がバッファだとしましょう。メッセージフォーマットを図にしてみました。

SSL2

+-----------------+------+-------
| 2 byte header   | 0x01 | etc.
+-----------------|------+-------
  • b[0] & 0x80 == 0x80 (b[0] の最上位ビットが「1」であることを意味します)

  • ((b[0] & 0x7f) << 8 | b[1]) > 9 (b[0] の下位 7 ビットと b[1] を合わせたものがデータの長さです。バッファ内のデータの長さを減らすことができます。 , したがって、それらをチェックすることはできません. しかし、メッセージ形式から、2 バイトの 3 つのフィールド (長さフィールド) と、暗号リストフィールド (サイズ 3) に少なくとも 1 つの項目があることがわかっています. したがって、少なくとも 9 バイト (データ長 >= 9)。

  • b[2] は 0x01 でなければなりません (メッセージ タイプ「ClientHello」)

SSL 3.0 または TLS 1.0、1.1、および 1.2

+-------+------------------+------------------+--------+------
| 0x16  | 2 bytes version  |  2 bytes length  |  0x01  |  etc.
+-------+------------------+------------------+--------+------
  • b[0] == 0x16 (メッセージ タイプ「SSL ハンドシェイク」)

  • b[1] は 0x03 である必要があります (現在は最新のメジャー バージョンですが、将来は誰にもわかりません)。

  • b[5] は 0x01 でなければなりません (ハンドシェイク プロトコル メッセージ "HelloClient")

参考までに、http://www.mozilla.org/projects/security/pki/nss/ssl/draft02.htmlおよびhttps://www.rfc-editor.org/rfc/rfc4346を参照してください。

于 2012-04-27T18:16:43.357 に答える
7

http://tlslite.cvs.sourceforge.net/viewvc/tlslite/tlslite/tlslite/messages.py?view=markupClientHello.parseのメソッド の実装に基づいてこれを理解できました

ここでは、Python で 2 つのソリューションを提供しています。IsSSlClientHandshakeSimple単純な正規表現であり、誤検知が非常に簡単に発生する可能性があります。IsSslClientHandshakeはより複雑です。長さの一貫性と、その他の数値の範囲をチェックします。

import re

def IsSslClientHandshakeSimple(buf):
  return bool(re.match(r'(?s)\A(?:\x80[\x0f-\xff]\x01[\x00-\x09][\x00-\x1f]'
                       r'[\x00-\x05].\x00.\x00.|'
                       r'\x16[\x2c-\xff]\x01\x00[\x00-\x05].'
                       r'[\x00-\x09][\x00-\x1f])', buf))

def IsSslClientHandshake(buf):
  if len(buf) < 2:  # Missing record header.
    return False
  if len(buf) < 2 + ord(buf[1]):  # Incomplete record body.
    return False
  # TODO(pts): Support two-byte lengths in buf[1].
  if ord(buf[0]) == 0x80:  # SSL v2.
    if ord(buf[1]) < 9:  # Message body too short.
      return False
    if ord(buf[2]) != 0x01:  # Not client_hello.
      return False
    if ord(buf[3]) > 9:  # Client major version too large. (Good: 0x03)
      return False
    if ord(buf[4]) > 31:  # Client minor version too large. (Good: 0x01)
      return False
    cipher_specs_size = ord(buf[5]) << 8 | ord(buf[6])
    session_id_size = ord(buf[7]) << 8 | ord(buf[8])
    random_size = ord(buf[9]) << 8 | ord(buf[10])
    if ord(buf[1]) < 9 + cipher_specs_size + session_id_size + random_size:
      return False
    if cipher_specs_size % 3 != 0:  # Cipher specs not a multiple of 3 bytes.
      return False
  elif ord(buf[0]) == 0x16:  # SSL v1.
    # TODO(pts): Test this.
    if ord(buf[1]) < 39:  # Message body too short.
      return False
    if ord(buf[2]) != 0x01:  # Not client_hello.
      return False
    head_size = ord(buf[3]) << 16 | ord(buf[4]) << 8 | ord(buf[5])
    if ord(buf[1]) < head_size + 4:  # Head doesn't fit in message body.
      return False
    if ord(buf[6]) > 9:  # Client major version too large. (Good: 0x03)
      return False
    if ord(buf[7]) > 31:  # Client minor version too large. (Good: 0x01)
      return False
    # random is at buf[8 : 40]
    session_id_size = ord(buf[40])
    i = 41 + session_id_size
    if ord(buf[1]) < i + 2:  # session_id + cipher_suites_size doesn't fit.
      return False
    cipher_specs_size = ord(buf[i]) << 8 | ord(buf[i + 1])
    if cipher_specs_size % 2 != 0:
      return False
    i += 2 + cipher_specs_size
    if ord(buf[1]) < i + 1: # cipher_specs + c..._methods_size doesn't fit.
      return False
    if ord(buf[1]) < i + 1 + ord(buf[i]): # compression_methods doesn't fit.
      return False
  else:  # Not SSL v1 or SSL v2.
    return False
return True
于 2010-10-09T22:18:09.657 に答える