Русский
Русский
English
Статистика
Реклама

Mtproto

Почему я использую Matrix вместо Telegram

23.07.2020 02:15:38 | Автор: admin
Привет, меня зовут Михаил Подивилов. Я специализируюсь на сетевых технологиях и преподаю в Сетевой академии Cisco.

Matrix замечательный, но недооценённый децентрализованный протокол обмена сообщениями с прилагающимся к нему клиентом Element, который я считаю достойной и жизнеспособной альтернативой Telegram.

Рассказываю, почему я использую Matrix вместо Telegram, как присоединиться к сети Matrix и настроить свой сервер.



Почему я использую Matrix вместо Telegram


На Хабре уже писали про Matrix, но я считаю, что этому протоколу нужна большая огласка: чем больше людей о нём узнает и начнёт его использовать тем лучше как для протокола, так и для айти-тусовки.

Matrix это открытый и децентрализованный протокол мгновенного обмена сообщениями, разрабатываемый энтузиастами из Matrix.org Foundation.

Спецификации протокола, равно как и клиент-серверная часть (Synapse как сервер и Element как клиент) находятся в открытом доступе в отличие от Telegram, который в настоящее время в открытом доступе держит только свой клиент, что вызывает немало вопросов по поводу безопасности использования протокола MTProto.

У протокола существует полноценная реализация федерации, которая позволяет бесшовно общаться, организовывать общие комнаты для общения пользователям разных серверов, совершать видео- и аудиозвонки.

Клиент Element обладает всеми современными функциями, которые присущи другим популярным мессенджерам (за исключением голосовых сообщений).

Чтобы не перечислять все преимущества, выделю некоторые из них, которые склонили меня к использованию этого протокола и мессенджера:

  • Децентрализация возможность поднять свой сервер для подключения к сети Matrix
  • Приватность сквозное шифрование
  • Групповые аудио- и видеозвонки для пользователей с разных серверов
  • Отличное API для ботов
  • Полнофункциональный веб-клиент
  • Консольные клиенты

Почему не Jabber?


Потому что протокол XMPP переполнен многочисленными XEP-ами, которые так и не были никогда не реализованы во многих его клиентах. Например, до сих пор не существует ни одного Jabber-клиента c нормальной синхронизацией истории с сервером.

Почему не Signal?


Потому что Signal не поддерживает децентрализацию.

Почему не Telegram?


Потому что исходные коды протокола MTProto, который используется в Telegram, никогда не были опубликованы.

Конечно, Telegram больше любят за его UI/UX но Element на данный момент почти ничем не уступает Telegram: там есть и мосты, и комнаты, и боты, и даже стикеры а вот людей относительно мало. К сожалению, проекту в действительности не хватает хорошего пиара.

Как подключиться к Matrix через чужой сервер


Это очень просто просто используйте веб-клиент Element или его десктопную версию. Регистрация не займёт у вас больше минуты.

Как подключиться к Matrix через свой сервер


Для этого необходимо настроить свой HomeServer на Synapse официальном сервере Matrix. Ниже я привёл инструкции, как это можно сделать.

Процесс настройки сервера Synapse мало чем отличается на разных дистрибутивах Linux основное отличие заключается в том, какую команду необходимо отдать операционной системе, чтобы та поняла, какой пакетный менеджер нужно использовать для установки.

Я буду объяснять на примере дистрибутива Ubuntu Server 20.04.

Шаг 1. Установка сервера Synapse


Подготавливаем нашу рабочую среду к установке сервера Synapse:

sudo apt install -y lsb-release wget apt-transport-httpssudo wget -qO /usr/share/keyrings/matrix-org-archive-keyring.gpg https://packages.matrix.org/debian/matrix-org-archive-keyring.gpgecho "deb [signed-by=/usr/share/keyrings/matrix-org-archive-keyring.gpg] https://packages.matrix.org/debian/ $(lsb_release -cs) main" |    sudo tee /etc/apt/sources.list.d/matrix-org.list

Загружаем непосредственно сам Synapse:

sudo apt updatesudo apt install matrix-synapse-py3

В процессе установки пакета будет запрошено название доменного имени, на котором будет располагаться ваш сервер Synapse. Как правило, это домен вида matrix.example.com, где A-запись поддомена matrix указывает непосредственно на ваш сервер с запущенным Synapse.



После установщик спросит вас, хотите ли вы отправлять анонимную статистику разработчикам.



На этом установка сервера Synapse завершена. Теперь необходимо включить Synapse при загрузке операционной системы:

systemctl start matrix-synapsesystemctl enable matrix-synapse

Мы также можем убедиться, как идут дела у сервера и работает ли он в принципе:

systemctl status matrix-synapsess -plnt




Шаг 2. Настройка сервера Synapse


Используйте текстовый редактор (например, nano или vim) и откройте файл /etc/matrix-synapse/homeserver.yaml для редактирования.

Найдите строку listeners: и приведите секцию в следующий вид:

listeners:  - port: 8008    tls: false    type: http    x_forwarded: true    bind_addresses: ['127.0.0.1']    resources:      - names: [client, federation]        compress: false

Отключите возможность регистрации новых пользователей на вашем сервере, если сервером будете пользоваться только вы:

enable_registration: false

Сгенерируйте случайную строку и поместите её в раздел registration_shared_secret:

registration_shared_secret: ""

После сохранения файла с настройками не забудьте перезапустить Synapse:

systemctl restart matrix-synapse


Шаг 3. Настройка Let's Encrypt


Сертификат необходим в том случае, если ваш сервер не находится за Cloudflare или другим подобным сервисом.

Для начала необходимо установить certbot программу для выпуска сертификатов:

sudo apt install certbot -y


И выпустить сертификат для вашего доменного имени:
certbot certonly --rsa-key-size 2048 --standalone --agree-tos --no-eff-email --email example@example.com -d matrix.example.com


Готово. Ваш сертификат теперь находится по адресу /etc/letsencrypt/live/matrix.example.com/.

Шаг 4. Настройка Nginx в качестве Reverse Proxy



В этой конфигурации мы будем использовать 80 порт для незащищённого соединения, 443 для защищённого и 8448 для подключения к федерации Matrix.

server {    listen 80;    server_name example.com;    return 301 https://$host$request_uri;}server {    listen 443 ssl;    server_name example.com;    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;    location /_matrix {        proxy_pass http://localhost:8008;        proxy_set_header X-Forwarded-For $remote_addr;        # Nginx by default only allows file uploads up to 1M in size        # Increase client_max_body_size to match max_upload_size defined in homeserver.yaml        client_max_body_size 10M;    }}server {    listen 8448 ssl;    server_name example.com;    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;    location / {        proxy_pass http://localhost:8008;        proxy_set_header X-Forwarded-For $remote_addr;    }}


Не забудьте перезагрузить Nginx после внесения изменений в конфигурацию:

systemctl restart nginx


Шаг 5. Регистрация нового пользователя


Теперь зарегистрируйте нового пользователя, под которым вы будете заходить на сервер:

sudo register_new_matrix_user -c /etc/matrix-synapse/homeserver.yaml http://localhost:8008New user localpart [root]: usernamePassword: Confirm password: Make admin [no]: yesSending registration request...Success!

Готово.

Мои поздравления, теперь вы полноценный участник сети Matrix с собственным сервером.

Давайте поговорим


Вместе с моим коллегой по цеху мы организовали чат для пользователей Хабра, чтобы вы могли проверить протокол Matrix в действии.

Подключайтесь!

Подробнее..

Свой криптографический протокол опасная идея

01.06.2021 12:20:22 | Автор: admin

Разработка своей криптографии в чём-то сравнима с созданием собственного авиадвигателя, говорит эксперт по безопасности Руна Сандвик. Фото: Виталий Кузьмин

Предположим, заказчик требует разработать собственный сетевой протокол. Например, для передачи данных между сервером и мобильным приложением, для связи между микросервисами, модулями программной системы, поверх UDP или поверх TCP. С шифрованием.

Казалось бы, почему не использовать открытые стандарты типа WebSocket и JSON, зачем собственный закрытый протокол? На это обычно приводят ряд причин. Во-первых, для безопасности, чтобы его было сложнее отреверсить и никто не понял, какие данные вы передаёте. Во-вторых, для эффективности. У нас ведь уникальный случай использования, поэтому стандартные решения не самые оптимальные. Наш собственный протокол будет работать с меньшими задержками, потреблять меньше трафика и меньше расходовать батарею (на мобильных устройствах). И третья причина функции. В своём протоколе специально для нашего приложения мы реализуем уникальные возможности, каких нет ни в открытом стандарте, ни у конкурентов.

Это распространённые причины, из-за которых разрабатывают проприетарные протоколы.

Безопасность


Поговорим о безопасности.

Любой безопасник скажет, что протокол должен быть закрытым. Это важно для защиты от конкурентов. И от клиентов тоже. Чем меньше клиент знает о протоколе тем меньше он контролирует систему, приложение. Вся власть остаётся в руках разработчика. Система полностью под контролем производителя, то есть вас. Соответственно, клиент навечно привязан к вам по обслуживанию, он не сможет ничего поломать в аппаратуре или софте. Он вообще побоится туда сунуться, потому что там ничего не понятно. Всё обфусцировано, зашифровано и написано на непонятном языке. Классика.

На Хабре частенько упоминают реверс-инжиниринг протоколов в различных устройствах: автомобили, тракторы, даже машины для производства мороженого. Производители этой техники стараются скрыть технические данные ради безопасности. Чтобы глупые пользователи не совали шаловливые ручки внутрь.

Например, важной частью бизнеса многих компаний является сервис и техническое обслуживание. Поэтому некая компания, скажем, системный интегратор предпочтёт вместо стандартных Open Source решений внедрить клиенту проприетарный софт с собственным закрытым протоколом. И всё. После этого она будет доить клиента десятилетиями.

Доходит до того, что некоторые интеграторы вообще пишут уникальный софт для конкретного клиента и отправляют собственного сотрудника в штат компании-клиента. Потому что он единственный, кто умеет работать с этой программой.

В общем, проприетарный протокол кажется очень выгодным для фирмы.

Но жизнь иногда показывает обратное. А именно:

  1. Принцип безопасность через неясность не работает. Любой протокол можно отреверсить.
  2. Проприетарная реализация шифрования это очень рискованно.
  3. Никто не поможет закрыть критические дыры в закрытом софте.

Принцип безопасность через неясность не работает в криптографии


Принцип безопасность через неясность (security through obscurity) заключается в том, чтобы скрыть внутреннее устройство системы или реализацию для обеспечения безопасности.

Но в любой системе есть недостатки. В данном случае разработчик системы хочет спрятать эти недостатки, чтобы злоумышленник ими не воспользовался.

Сравните с опенсорсными системами, где разработчик специально открывает код, чтобы независимые сторонние эксперты помогли выявить и исправить эти недостатки.

В итоге получается, что собственный проприетарный протокол, максимально закрытый от посторонних глаз и обфусцированный, вовсе не увеличивает безопасность системы, а уменьшает её!


В криптографии одно из базовых правил использовать только открытые, публичные алгоритмы и протоколы. В такой системе есть лишь один секрет приватный ключ. Больше ничего. Это принцип Керкгоффса, который широко применяется в криптографии и считается практически неоспоримым.

Голландский криптограф Огюст Керкгоффс изложил правила проектирования военных шифров в книге Военная криптография (1883). Основная идея была в том, что попадание системы в руки врага не должно причинять неудобств. Поэтому ничего, кроме ключа, не должно быть секретным.

Собственная криптография


Вопрос Почему не разработать собственную криптографию? чем-то похож на вопрос Почему не спроектировать авиадвигатель?, говорит Руна Сандвик, исследователь по безопасности. Конечно, теоретически мы можем это сделать. Но это чрезвычайно сложно. Гораздо более простой и надёжный вариант выбрать готовое решение, проверенные и надёжные протоколы и алгоритмы.

Поэтому в сообществе информационной безопасности вызывает большое подозрение, если какая-то компания реализует собственный проприетарный протокол. Например, проприетарный протокол MTProto в Telegram поначалу вызвал массу критических отзывов. Взлом MTProto1.0 стал одной из самых популярных статей на Хабре в 2013 году: Безопасен ли Telegram? Или как я искал закладку в MTProto (спойлер: глупые ошибки в проприетарной криптографии).

Хотя баг быстро исправили, это наглядный пример, почему не нужно разрабатывать собственные авиадвигатели криптографические алгоритмы. Вы можете допустить ошибку а риски огромные. Люди полагаются на безопасное шифрование, от этого зависит их свобода, а иногда жизнь.

К тому же, в мессенджере запрещена анонимная регистрация без номера телефона вероятно, так удобнее коммерческой компании, чтобы блокировать спам и продвигать приложение по адресным книгам. Кто будет думать об анонимности, когда на кону миллиарды долларов прибыли? При этом Telegram изначально позиционировали именно как безопасный мессенджер (многие пользователи купились на такую рекламу).

В реальности для обеспечения анонимности нужно регистрироваться по анонимной сим-карте, но не все это понимают. См. также советы Как защитить свой аккаунт в Telegram и Практическое руководство по анонимности в онлайне.

Привязка к телефонному номеру делает пользователя уязвимым, потому что операторы сотовой связи внутри страны удобный объект для работы спецслужб.

Конечно, в опенсорсе есть свои специфические риски. Например, проблемы с сотнями зависимостей, которые вы не контролируете. Например, 20% багов в проектах на GitHub явно внесены в проекты специально, со злым умыслом. То есть вредоносными контрибуторами, которые действовали умышленно. Ещё не забыта история c мейнтейнером ESLint, который 12 июля 2018 года опубликовал вредоносные версии пакетов eslint-scope и eslint-config-eslint в репозитории npm.

Такое может случиться с каждым опенсорсным проектом, потому что мейнтейнеры работают бесплатно, на износ:


Поэтому иногда соглашаются продать свой проект (такая же история с расширениями для браузеров). А этот проект уже внедрён в качестве зависимости в тысячи других.

Но все проблемы безопасности решаемы. Независимый аудит открытого кода профессиональными экспертами лучшая гарантия его надёжности.

Кстати, в 2013 году проприетарный софт впервые обогнал опенсорсные проекты по среднему количеству багов на 1000 строк кода.


Источник: Coverity Scan Open Source Report

В прошлом веке считалось, что проприетарный софт пишут профессионалы, а Open Source любители. Сегодня же профессиональный уровень опенсорсных программ вовсе не уступает проприетарным. Может, даже превосходит их.

Возвращаясь к примеру Telegram. 5 декабря 2020 года двое итальянских математиков Марино Микулан и Никола Витоколонна опубликовали на сайте препринтов arXiv.org исследование Автоматическая символическая проверка протокола MTProto 2.0 в Telegram (вторая версия опубликована 30 апреля 2021 года, arXiv:2012.03141v1). Оно подтверждает безопасность обновлённой версии фирменного протокола MTProto 2.0.


Набор протоколов MTProto 2.0 (в голубой рамке) и область покрытия данной научной работы (светло-зелёным цветом). Схема из научной статьи Марино Микулана и Никола Витоколонны, arXiv:2012.03141v1

Статья содержит полностью автоматизированное доказательство надёжности аутентификации MTProto 2.0, обычного чата, зашифрованного end-to-end чата и механизмов повторного ввода ключей в отношении нескольких свойств безопасности, включая аутентификацию, целостность, конфиденциальность и совершенную прямую секретность, а также доказывает формальную корректность MTProto 2.0.


Протокол аутентификации MTProto 2.0. Здесь $\{m\}_{pk}$ означает асимметричное шифрование $m$ открытым ключом $pk$. В свою очередь, $\{m\}_{(k,iv)}$ означает симметричное шифрование общим ключом $k$ с вектором инициализации $iv$. Схема из научной статьи


Слегка упрощённая версия протокола MTProto 2.0 для секретных чатов. Все сообщения перенаправляются через сервер $S$: каждое сообщение между $X \in \{A, B \}$ и $S$ шифруется с использованием ключа авторизации $X$ (здесь не показан). Обратите внимание, что $g_{ab}$, $k$ и $iv$ не известны серверу $S$. Схема из научной статьи



Эта математическая работа чуть ослабила озабоченность экспертов по поводу проприетарного протокола MTProto2.0. Редкий случай, когда собственная нестандартная криптографическая система (пока) работает надёжно. В ней ещё не нашли таких фатальных уязвимостей, как в MTProto 1.0.

Неужели разработчики Telegram всё-таки научились и смогли сделать уникальный авиадвигатель без ошибок? Можно только порадоваться за них, ведь все пользователи Telegram участвовали в этом обучении как бета-тестеры. В каком-то смысле это наш общий успех.



На правах рекламы


Наша компания предлагает серверы с Linux или Windows. Не экономим на железе только современное оборудование и одни из лучших дата-центров в России и ЕС. Поспешите проверить!

Присоединяйтесь к нашему чату в Telegram.

Подробнее..

Telegram на go часть 1, парсим схему

26.12.2020 00:20:24 | Автор: admin

Желание написать качественный клиент для любимого мессенджера на go зрело давно, но только месяц назад я решил, что время пришло и у меня есть достаточная квалификация для этого.

Разработа все еще в процессе (и полностью open source), но уже пройден увлекательный путь от полного непонимания протокола до относительно стабильного клиента. В серии статей я расскажу, с какими сложностями я столкнулся и как с ними боролся. Приёмы, которые я применил, могут быть полезны при разработке клиента для любого бинарного протокола со схемой.


Type Language

Начнем с Type Language или TL, схемы описания протокола. Не буду углубляться в описание формата, на хабре уже есть его разбор, расскажу про него лишь кратко. Он чем-то похож на gRPC и описывает схему взаимодействия между клиентом и сервером: структуру данных и набор методов.

Вот пример описания типа:

error#1fbadfee code:int32 message:string = Error;

Тут 1fbadfee это id типа, error его имя, code и message поля, а Error это имя класса.

Аналогичным образом описываются методы, только вместо имени типа будет имя метода, а вместо класса тип результата:

sendPM#3faceff text:string habrauser:string = Error;

Это значит, что метод sendPM принимает аргументы text и habrauser, а возвращает Error, варианты (конструкторы) которого описаны ранее, например, error#1fbadfee.

Чтобы начать работать с протоколом, нужно как-то научиться парсить его схему. Есть два пути: использовать обобщенный парсер или писать ad-hoc, т.е. специализированный парсер для конкретного протокола. Для первого пути есть participle, на первый взгляд неплохой обобщенный парсер на го, через которого можно было бы описать грамматику. Я решил выбрать путь ad-hoc и этот выбор себя оправдал.

Тестовые данные

Нам придется тестировать наш парсер, подавая ему на вход данные и проверяя результат, а разнообразие тестовых данных позволит нам не беспокоиться во время рефакторинга и быть уверенным в том, что наш парсер стабильный. Первая сложность: написать парсер так, чтобы он не ломался от изменений, позволив нам быстрее итерироваться в его разработке.

Табличные тесты

Для небольшого набора данных вполне подойдёт их хранение прямо в тестах, рядом с ожидаемым резульатом парсинга. Вот пример для парсинга Definition, определения для конкретного типа:

func TestDefinition(t *testing.T) {for _, tt := range []struct {Case       stringInput      stringString     stringDefinition Definition}{{Case:  "inputPhoneCall",Input: "inputPhoneCall#1e36fded id:long access_hash:long = InputPhoneCall",Definition: Definition{ID:   0x1e36fded,Name: "inputPhoneCall",Params: []Parameter{{Name: "id",Type: bareLong,},{Name: "access_hash",Type: bareLong,},},Type: Type{Name: "InputPhoneCall"},},},    // ...  } {t.Run(tt.Case, func(t *testing.T) {var d Definitionif err := d.Parse(tt.Input); err != nil {t.Fatal(err)}require.Equal(t, tt.Definition, d)})  } }  

Такие тесты можно написать для каждой сущности, начиная от Flag (определение опциональности поля, в котором указывается имя битового поля и смещения в нём) заканчиая полной схемой.

Проверять необходимо не только положительный, но и негативный случай, ведь невалидный вход должен вызывать ошибку и это тоже нужно закрепить в тестах. Делается это аналогично:

t.Run("Error", func(t *testing.T) {for _, invalid := range []string{"=0","0 :{.0?InputFi00=0",} {t.Run(invalid, func(t *testing.T) {var d Definitionif err := d.Parse(invalid); err == nil {t.Error("should error")}})}})

Файлы в testdata

Объёмные входные данные уже проблематично хранить прямо в коде и удобнее вынести в отдельное место. Я использую поддиректорию _testdata: подчеркивание вначале нужно для того, чтобы отделить пакеты с кодом и просто данные, а тулинг go игнорирует директории с таким префиксом.

В этом тесте мы читаем Sample.tl из _testdata и пытаемся его распарсить:

func TestParseSample(t *testing.T) {data, err := ioutil.ReadFile(filepath.Join("_testdata", "Sample.tl"))if err != nil {t.Fatal(err)}schema, err := Parse(bytes.NewReader(data))if err != nil {t.Fatal(err)}  // ...}

Тесты в го запускаются в контексте директории модуля, так что нужный путь получить довольно легко, но важно не забыть использовать filepath.Join для кросс-платформенности.

Эталонные (golden) файлы

Более распространённое название для них это "golden files". Эталоном в нашем случае является результат парсинга, записанный в файл. Такие файлы обновляются автоматически, если тесты запущены в специальном режиме (обычно это просто флаг -update). Они позволят нам не набирать вручную ожидаемый результат парсинга, а генерировать его из тестов. Я использовал goldie в качестве утилиты для работы с подобными файлами.

func TestParser(t *testing.T) {for _, v := range []string{"td_api.tl","telegram_api.tl","telegram_api_header.tl","layer.tl",} {t.Run(v, func(t *testing.T) {data, err := ioutil.ReadFile(filepath.Join("_testdata", v))if err != nil {t.Fatal(err)}schema, err := Parse(bytes.NewReader(data))if err != nil {t.Fatal(err)}t.Run("JSON", func(t *testing.T) {g := goldie.New(t,goldie.WithFixtureDir(filepath.Join("_golden", "parser", "json")),goldie.WithDiffEngine(goldie.ColoredDiff),goldie.WithNameSuffix(".json"),)g.AssertJson(t, v, schema)})})}}

Этот пример парсит все файлы из списка, сериализует результат в json и сравнивает с эталонным (как json). Если передан флаг -update, то код перед сравнением обновляет эталонный файл, сохраняя его в папку _golden.

Сериализуйте выходные данные в текстовый формат (например, json с идентацией) и храните их в системе контроля версий, тогда будет легко отслеживать в них изменения.

Decode-Encode-Decode

Если научиться не только парсить, а генерировать схему, то можно применить принцип decode-encode-decode, сравнивая результат генерации с входом на парсинг.

Для этого я каждой сущости имплементировал метод String() string:

// Annotation represents an annotation comment, like //@name value.type Annotation struct {Name  string `json:"name"`Value string `json:"value"`}func (a Annotation) String() string {var b strings.Builderb.WriteString("//")b.WriteRune('@')b.WriteString(a.Name)b.WriteRune(' ')b.WriteString(a.Value)return b.String()}

Оказалось, что удобнеее всего для такой цели использовать strings.Builder, вызывая метод String() для сущностей уровнем ниже.

Добавив результат генерации схемы в эталонные файлы, мы еще больше стабилизируем наш парсер, но самый интересный способ еще впереди.

Fuzzing

Для обеспечения еще большей стабильности нашего парсера я применил технику (ниндзя) фаззинга. Это когда на вход парсеру подаётся случайный набор данных, автоматически изменяемый так, чтобы покрыть как можно больше строк кода (coverage-guided fuzzing). Для фаззинга в го есть замечательный проект go-fuzz от Дмитрия Вьюкова. Это фаззер очень помог мне (и не только мне) во множестве проектов, и я применил его и для парсера. Примечательно, что Дмитрий Вьюков также является автором syzkaller, утилиты на go, предназначенной для фаззинга ядра Linux и нашедшей в нём уже сотни багов.

Для того, чтобы начать фаззинг, необходимо определить для него входную точку, функцию, которую фаззер будет вызывать в попытках разломать наш код.

Например, вот функция фаззинга для Definition:

// +build fuzzpackage tlimport "fmt"func FuzzDefinition(data []byte) int {var d Definitionif err := d.Parse(string(data)); err != nil {return 0}var other Definitionif err := other.Parse(d.String()); err != nil {fmt.Printf("input: %s\n", string(data))fmt.Printf("parsed: %#v\n", d)panic(err)}return 1}

Она сначала парсит вход, потом генерирует схему и пытается распарсить полученный результат снова.

Decode-encode-decode-encode

We need to go deeper. Для фаззинга полной схемы я применил подход еще интересней:

  1. Парсинг входных данных

  2. Генерация схемы

  3. Парсинг данных из (2)

  4. Генерация схемы на основании (3)

  5. Сравнение (4) и (2)

В идеале межу (4) и (2) разницы не должно быть, т.к. мы сравниваем уже более-менее нормализованную схему без мусора. На практике фаззинг помог мне найти и исправить огромное количество багов, и мы его еще применим в других частях проекта.

Немного о go-fuzz

Фаззинг важен для защиты от Denial of Service атак, т.к. поможет найти потенциальные паники или даже OOM. Фаззинг довольно ресурсоёмкий процесс, поэтому у go-fuzz есть режим распределенной работы, где запускается один координатор и несколько воркеров к нему подключаются по сети, так что фаззить можно и на удалённом сервере.

Результатом фаззинга является corpus, набор входных данных, который был обнаружен фаззером (а также crashers, набор данных, вызывающих падение парсера, если таковые есть). Нужно исправлять crashers до тех пор, пока их останется ровно 0, а фаззер перестанет находить новые данные. Для того, чтобы помочь фаззеру, можно в corpus заранее добавить данных, тогда поиск будет выполняться быстрее.

Еще я бы хотел упомянуть, что ведется активная работа по внедрению фаззинга как части тулинга го, так что если вы читаете эту статью в будущем, проверьте, нет ли уже встроенного решения.

Вместо заключения

Подходы к тестированию, которые я описал, довольно универсальны и могут быть применены для любого проекта, где есть какие-то входные и выходные данные. Они были сформированны за время работы над множеством протоколов (STUN, TURN, SDP, MTProto, ...) и должны помочь писать парсеры или сериализаторы без страха и с удовольствием.

Надеюсь, статья будет кому-то полезна. Если будет интересно, я дальше продолжу рассказывать про то, как создавался клиент (а возможно и сервер) Telegram на go:

  • Генерация кода на основании схемы

  • Парсинг документации (и добавление её в сгенерированный код)

  • Криптография

  • Тестирование сетевого взаимодействия (unit, e2e)

  • Тестирование работы с сайд-эффектами (время, таймауты, ГПСЧ)

  • CI, или настраиваем пайплайн так, чтобы кнопку Merge не было страшно нажимать

А еще я хочу сказать больше спасибо подключившимся участникам проекта, без них было бы намного сложнее.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru