着信SSL(HTTPS)ハンドシェイク(SSLワイヤ形式)を検出する方法は?
質問
受信TCP接続を受け入れているサーバーを書いています。サーバーがTCP接続を受け入れており、すでにクライアントから16バイト(または)を受け取っているとしましょう。これらの16バイトを知って、サーバーはクライアントがSSLの握手を開始したいかどうかをどのように検出できますか?
SSLを介してLocalHost(127.0.0.1またはAF_UNIXのいずれか)に接続するLinuxシステムで、クライアントに次のハンドシェイク(HexDump)を送信することを示した実験を行いました。
8064010301004b0000001000003900003800003500001600001300000a07
00c000003300003200002f03008000000500000401008000001500001200
0009060040000014000011000008000006040080000003020080
クライアントがSSLハンドシェイクを送信しているかどうかを判断できるように、サーバーはこれらの最初の数バイトをどのようにプローブする必要がありますか?プローブは、すべての有効なSSLハンドシェイクに対して真で返す必要があり、SSLハンドシェイクではないクライアントが送信したメッセージに対して高い確率でfalseを返す必要があります。プローブにライブラリ(OpenSSLなど)を使用することは許可されていません。プローブは、単純なコード(CまたはPythonの数十行など)でなければなりません。
解決 2
の実装に基づいてこれを理解できました ClientHello.parse
メソッドインhttp://tlslite.cvs.sourceforge.net/viewvc/tlslite/tlslite/tlslite/messages.py?view=markup
ここPythonで2つのソリューションを提供しています。 IsSSlClientHandshakeSimple
単純なregexpであり、いくつかの誤検知を非常に簡単に生成できます。 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
他のヒント
クライアントは常に、いわゆる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」がバッファーだとしましょう。メッセージ形式を図式化しようとしました。
SSL 2
+-----------------+------+-------
| 2 byte header | 0x01 | etc.
+-----------------|------+-------
b [0]&0x80 == 0x80(これは、B [0]の最も重要なビットが「1」であることを意味します)
((b [0]&0x7f)<< 8 | b [1])> 9(b [0]とb [1]の低い7ビットはデータの長さです。バッファには少ないことができます。 、したがって、それらを確認することはできません。しかし、メッセージ形式から2つのフィールド(長さフィールド)の3つのフィールドがあり、少なくとも1つのアイテム(サイズ3)があります。データの長さ> = 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 と http://tools.ietf.org/html/rfc4346