Wie ein eingehende SSL (https) Handshake (SSL Draht-Format) zu erkennen?
Frage
Ich schreibe einen Server, der eingehende TCP-Verbindungen akzeptiert. Nehmen wir an, der Server eine TCP-Verbindung angenommen hat, und hat bereits 16 empfangen (oder so) von der Client-Bytes. diese 16 Bytes zu wissen, wie kann der Server erkennt, ob der Client einen SSL-Handshake zu initiieren?
Ich habe ein Experiment gemacht, die das zu localhost Anschluss auf meinem Linux-System zeigte (entweder 127.0.0.1 oder AF_UNIX) über SSL der Client macht die folgenden Handshake (hexdump) senden, gefolgt von 16 scheinbar zufälligen Bytes:
8064010301004b0000001000003900003800003500001600001300000a07
00c000003300003200002f03008000000500000401008000001500001200
0009060040000014000011000008000006040080000003020080
Wie sollte der Server diese ersten paar Bytes Sonde, nur in der Lage sein, zu bestimmen, ob der Client einen SSL-Handshake sendet? Die Sonde muss return true für all gültiges SSL-Handshakes, und es muss mit hohen Wahrscheinlichkeit für eine Nachricht durch den Client gesendet, die kein SSL-Handshake return false ist. Es wird keine Bibliotheken (wie OpenSSL) für die Sonde benutzen. Die Sonde muss ein einfacher Code sein (wie ein paar Dutzend Zeilen in C oder Python).
Lösung 2
Das konnte ich herausfinden, auf der Grundlage der Umsetzung des ClientHello.parse
Verfahrens in
http://tlslite.cvs.sourceforge.net /viewvc/tlslite/tlslite/tlslite/messages.py?view=markup
Ich gebe zwei Lösungen hier in Python. IsSSlClientHandshakeSimple
ist ein einfaches regexp, die ganz leicht einige Fehlalarme ergeben können; IsSslClientHandshake
ist komplizierter. es die Konsistenz der Längen überprüft, und die Reichweite von einigen anderen Zahlen
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
Andere Tipps
Der Client sendet immer so HelloClient Nachricht zuerst genannt. Es kann in SSL-2-Format oder SSL 3.0-Format (wie in TLS 1.0, 1.1 und 1.2 das gleichen Format) sein.
Und es gibt auch die Möglichkeit, dass SSL 3.0 / TLS 1.0 / 1.1 / 1.2-Clients HelloClient mit dem älteren Format (SSL 2), nur mit der höheren Versionsnummer in den Daten zu senden. So, Erkennung von SSL 2 HelloClient ist notwendig für neue Kunden auch. (Zum Beispiel Java SSL-Implementierung funktioniert so)
Lassen Sie uns sagen ‚b‘ ist Ihr Puffer. Ich habe versucht, das Nachrichtenformat, um Diagramm.
SSL 2
+-----------------+------+-------
| 2 byte header | 0x01 | etc.
+-----------------|------+-------
-
b [0] & 0x80 == 0x80 (es bedeutet höchstwertigen Bits von b [0] ist '1')
-
((b [0] & 0x7f) << 8 | b [1])> 9 (Es Menas die niedrigen 7 Bits von b [0] zusammen mit b [1] sind Datenlänge Sie. weniger in Ihrem Puffer kann, so dass Sie sie nicht überprüfen können. Aber von dem Nachrichtenformat wir wissen, gibt es 3 Feld von 2 Bytes (Länge Feldern), und mindestens ein Element in Chiffre-Listenfeld (Größe 3). so sollte es sein, mindestens 9 Bytes (Datenlänge> = 9).
-
b [2] muss 0x01 sein (Nachrichtentyp "Client")
SSL 3.0 oder TLS 1.0, 1.1 und 1.2
+-------+------------------+------------------+--------+------
| 0x16 | 2 bytes version | 2 bytes length | 0x01 | etc.
+-------+------------------+------------------+--------+------
-
b [0] == 0x16 (Nachrichtentyp "SSL-Handshake")
-
b [1] sollte 0x03 (derzeit neueste Hauptversion, aber wer weiß, in Zukunft?) Sein
-
b [5] muss 0x01 sein (Handshake-Protokoll-Nachricht "HelloClient")
Als Referenz können Sie sehen http://www.mozilla.org/projects /security/pki/nss/ssl/draft02.html und http://tools.ietf.org/html/rfc4346