Как обнаружить входящее рукопожатие SSL (HTTPS) (формат провода SSL)?

StackOverflow https://stackoverflow.com/questions/3897883

  •  29-09-2019
  •  | 
  •  

Вопрос

Я пишу сервер, который принимает входящие соединения TCP. Предположим, сервер принял соединение TCP и уже получил 16 (или около того) байтов от клиента. Знание этих 16 байтов, как сервер определяет, хочет ли клиент инициировать рукопожатие SSL?

Я прошел эксперимент, который показал, что в моей системе Linux, подключенной к Localhost (либо 127.0.0.1, либо AF_UNIX) через SSL заставляет клиента отправлять следующее рукопожатие (шестигранник), за которым следует 16 кажущихся случайными байтами:

8064010301004b0000001000003900003800003500001600001300000a07
00c000003300003200002f03008000000500000401008000001500001200
0009060040000014000011000008000006040080000003020080

Как должен процитировать сервер этих первых нескольких байтов, просто чтобы иметь возможность определить, отправляет ли клиент рукопожатие SSL? Зонд должен вернуть true для всех действительных рукопожатий SSL, и он должен вернуть ложь с высокой вероятностью для сообщения, отправленного клиентом, который не является рукопожатием SSL. Не может использовать какие-либо библиотеки (например, OpenSSL) для зонда. Зонд должен быть простым кодом (например, несколько десятков линий в C или Python).

Это было полезно?

Решение 2

Я мог бы выяснить это на основе реализации ClientHello.parse метод внутриhttp://tlslite.cv.sourceforge.net/viewvc/tlslite/tlslite/tlslite/messages.py?view=markup.

Я даю два решения здесь в Python. 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

Другие советы

Клиент всегда отправляет так называемое сообщение Helloclient в первую очередь. Он может быть в формате SSL 2 или формате SSL 3.0 (тот же формат, что и в TLS 1.0, 1.1 и 1,2).

И есть также возможность, что клиенты SSL 3.0/TLS 1.0/1.1/1.2 отправляют Helloclient с более старым форматом (SSL 2), только с более высоким номером версии в данных. Таким образом, обнаружение 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 (это menas 7 бит B [0] вместе с B [1] - длина данных. Вы можете иметь меньше в своем буфере , поэтому вы не можете их проверить. Но из формата сообщений мы знаем, что есть 3 поля из 2 байта (поля длины) и, по крайней мере, один элемент в поле списка шифров (размера 3). Так что должно быть не менее 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 Handshake")

  • B [1] должен быть 0x03 (в настоящее время новейшая основная версия, но кто знает в будущем?)

  • b [5] должен быть 0x01 (сообщение протокола рукопожатия "helloclient")

Для справки, вы можете увидеть http://www.mozilla.org/projects/security/pki/nss/ssl/draft02.html. и http://tools.ietf.org/html/rfc4346.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top