Государственная цензура есть многогранный монстр, закрывающий путь не только к зарубежной информации посредством блокировки ресурсов и их методов обхода, в лице Proxy, VPN, Tor, но также и постоянно пытающийся подавлять неподконтрольные безопасные и анонимные коммуникации внутри самого себя. Гос.структуры ради этого готовы пойти буквально на любые ухищрения и запугивания, включая штрафы (дело Threema, отказ передачи ключей шифрования), уголовные преследования (дело Дмитрия Богатова, дело по VIPole), регулирование неконтролируемой криптографии (история запретов до 2016, запрет шифрования доменных имён), а также возможные бэкдоры в собственных криптоалгоритмах (Кузнечик и Стрибог). Рэкет становится для государства нормой жизни.
При этом гос.структур не смущает реальное отсутствие безопасности у них же под носом в лице неоднократных и массовых утечек персональных данных с Яндекс, Delivery Club, Сбербанка, Альфа-банка, Ростелекома, Почты России и множества других компаний работающих на российском рынке (список1, список2, список3). С учётом существования пакета Яровой, собирающим огромное количество данных, такие случаи должны были бы стать скорее исключением, чем правилом. Не только же ради борьбы с терроризмом создавался такой закон, правда ведь? Но мы видим явное противоречие, где с одной стороны вводятся всё более репрессивные меры к гражданам в целях их же защиты (по-видимому /s), с другой стороны их настоящая защита постоянно ставится всё под больший вопрос самой реальностью, как и накладываемые меры безопасности. Да и что уж греха таить, сам пакет Яровой приводит к небыли ранее известной концентрации и централизации персональных данных в ограниченном количестве мест, не исключающим при этом реплицирования, что также ставит под вопрос рациональность такой безопасности.
Вследствие всего этого наплыва новостей разработчикам клиент-безопасных приложений и анонимных сетей ставятся дополнительные задачи со звёздочкой, заключающиеся не только в реализации стойких приложений к действиям всевозможных злоумышленников, но и в реализации скрывающих механизмов самого факта исполнения, как в целях предотвращения блокировок, так и в целях сокрытия явной активности от государства. Таким образом, стеганография (как наука / искусство о сокрытии самого факта существования информации) становится последним рубежом защиты криптографии.
Цели и задачи
Нашей основной задачей станет вживление анонимизированного / паразитного трафика в централизованный сервис с целью последующего его сокрытия под обычный HTTPS трафик. При этом, усложняя для себя ситуацию и делая её одновременно интереснее, мы попытаемся использовать сторонний сервер, не принадлежащий нам. Такое условие поможет не только усложнить среду в которой будет развёрнута анонимная сеть, но и позволит лучше разобраться в самой специфике подобного "вживления" на реальных условиях, где одним из важных моментов будет являться адаптация нашей сети в роли паразита на теле неподозревающего централизованного сервера.
Также интересной особенностью данного сценария будет являться и своеобразный обход NAT для P2P-архитектуры, как бы это забавно не звучало. Сервер в таком случае станет лишь ретранслятором (TURN-сервером) сообщений от отправителя ко всем получателям, без возможности расшифрования анонимизированного трафика. Плюсы такого подхода лежат в плоскости отсутствия вложений в аренду собственных VPS-ретрансляторов, либо собственных мощностей железа.
Для задачи подобного рода к сожалению подойдёт не всякая анонимная сеть, потому как таковая должна иметь возможность абстрагироваться от самой среды коммуникации. Иными словами, уровень анонимности подобной системы не должен зависеть: ни от количества узлов в сети, ни от их расположения, ни от их связей, ни от уровня централизации. Популярные анонимные сети, такие как Tor или I2P, становятся непригодными для подобной своеобразной задачи лишь по причине того, что они не способны противостоять глобальному наблюдателю.
К сожалению, также и не все теоретически доказуемые анонимные сети, способные противостоять глобальному наблюдателю, имеют свойство абстрагирования. Например, DC-сети, в их классическом исполнении, имеют чётко заданную связь все-ко-всем, где становится непредусмотренной возможность создания каких-либо промежуточных узлов. В любом случае, DC-сети всё равно можно проапгрейдить и привести в вид абстрактных анонимных сетей, но в таком случае придётся добавлять сквозное шифрование (E2EE), а также по причине отсутствия живых проектов, попутно писать с нуля механизм анонимизации. Следовательно, наиболее рациональным решением для нас будет являться использование уже готовой анонимной сети, которая бы являлась также и абстрактной. Выбирать, к счастью или к сожалению особо не приходится, потому как под такие специфичные требования, на данный момент времени, существует лишь сеть Hidden Lake.
Hidden Lake и QB-сети
Анонимная сеть Hidden Lake (HL) относится к QB-сетям, иными словами, её алгоритм анонимизации принадлежит к задаче на базе очередей. Суть данной задачи может быть описана крайне просто следующим алгоритмом:
Каждое сообщение шифруется ключом получателя,
Сообщение отправляется в период = T всем участникам сети,
Период T одного участника независим от периодов T1, T2, ..., Tn других участников,
Если на период T сообщения не существует, то в сеть отправляется ложное сообщение без получателя,
Каждый участник пытается расшифровать принятое им сообщение из сети.
Таким образом, со стороны глобального наблюдателя мы будем видеть лишь факт генерации сообщений в определённые периоды времени = T, не выявляя случаи отправления / получения сообщений, или банального сетевого бездействия. При этом отделить ложный трафик от истинного мы будем не в состоянии, потому как QB-сети, в отличие от других анонимных сетей, не накладывают ложный трафик на истинный, а заменяют ложный трафик истинным.
Плюс ко всему этому, и сами статичные периоды T не являются обязательным условием существования анонимности в QB-сетях. И действительно, если мы сделаем периоды T случайной величиной, то сути дела такое условие не поменяет. Вследствие подобной фичи, анонимные сети базируемые на QB-сетях могут быть более гибкими в задачах сокрытия трафика, чем собратья в лице DC-сетей.
Сеть Hidden Lake имеет приличное количество настроек для всевозможных адаптаций к различным условиям сетевой среды. HL может настраивать размер генерируемого сообщения, количество случайно добавляемых ложных байт, растягивать периоды и делать их динамичными. Все сообщения генерируемые сетью HL имеют вид случайных байт, что в определённой степени снижает риски однозначного детектирования сети по структуре данных и, как следствие, снижает риски успешного блокирования.
Плюс ко всему вышеописанному, в исходном коде Hidden Lake существует такая сущность как - адаптеры. Их основная роль заключается как раз в том, чтобы подстраиваться под конкретную систему с дальнейшей возможностью пробрасывать и принимать анонимизированный трафик. В неком роде, сама сеть HL на уровне своего исходного кода становится фреймворком для разработки таковых адаптеров.
Более подробно об абстрактных анонимных сетях, и в частности о QB-сетях можно почитать тут. Более подробно ознакомиться с теорией анонимных сетей можно тут и здесь. Более подробно почитать о сети Hidden Lake можно здесь.
Централизованный сервис
Исходя из теории абстрактных анонимных сетей, чисто технически мы можем выбирать совершенно любой централизованный сервис в котором имеется возможность писать групповые сообщения и читать их соответственно. Но к сожалению для нас некоторые сервера создают дополнительные неудобства при автоматическом исполнении сценариев, как например - регистрация, авторизация, каптчи, баны и т.д. Вследствие этого, необходимо выбрать централизованный сервис, который не был бы так навязчив к автоматике и имел возможность группового общения. Для подобных сценариев хорошо подойдут групповые анонимные чаты или анонимные треды.
Одним из таких сервисов с достаточно низким уровнем банов и с существованием анонимных групповых тредов является сайт chatingar.com. У него существует возможность как писать в групповые чаты, так и писать в треды. Групповых чатов ограниченное количество и их нельзя создавать, что нельзя сказать о тредах. Таким образом, чтобы не производить лишний спам явно, чтобы не портить жизнь другим людям в общении, а также чтобы быть менее заметными, мы можем писать либо в давно забытый и старый тред, активность которого максимум увидят только сами админы, либо создать новый тред и писать уже в него.
Также ещё одним из возможных кандидатов на выбор жертвы был телеграм. У него достаточно хорошее API, что могло позволить быстро развернуть групповой канал через который бы прогонялся весь анонимизированный трафик. Тем не менее выбор пал на chatingar по причине более основополагающего понимания того, как будут работать адаптеры в HL для среды у которой нет явной документации. Если вам будет интересно после прочтения данной статьи самим реализовать какой-нибудь адаптер, то в качестве "домашнего задания" вполне себе может выступить телеграм. Плюс к этому вы можете скинуть Pull Request на его добавление сюда, тем самым дополнительно поучаствовав в open-source разработке анонимной сети.
Вынюхиваем API
В первую очередь нам необходимо будет попытаться получить содержимое самого треда, а именно - количество комментариев в этом треде. Данное условие поможет нам однозначно определить в каком конкретно месте закончилась генерация старого трафика, чтобы не подтягивать все ранее сгенерированные сообщения при старте приложения.
В запросах браузера можно увидеть запрос типа https://api.chatingar.com/api/post/65f121625b65dcbdedcbed7d
, где строка 65f121625b65dcbdedcbed7d
- это есть ID поста, что будет очень удобно при конкретном указании места генерации всего последующего трафика.
Сам запрос крайне простой, в нём отсутствуют какие-либо идентификаторы со стороны самого сайта. Единственные остающиеся идентификаторы являются для нас классическими, а именно: User-Agent и наш IP. Первый легко будет изменить в нашем коде, второй легко будет обойти при помощи общедоступных прокси, включая российские. Тем не менее, в целях облегчения кода, данные параметры я оставлю без изменения.
И в ответе действительно нас ожидает количество комментариев в поле commentCount
.
Далее, следующее, что нам необходимо сделать - это найти запрос на получение самих комментариев. Благо он схож со старым запросом, просто добавился дополнительный аргумент ?per_page=5
и вместо строки post
- строка comment
. Полный URL: https://api.chatingar.com/api/comment/65f121625b65dcbdedcbed7d?per_page=5
.
Мы получили очень даже релевантный ответ, в котором помимо самого сообщения (body
) существует также и время его получения сервером (timestamp
). Это будет полезным в условиях запоминания ранее прочитанных сообщений.
Теперь нам необходимо проверить, как поведёт себя API, когда количество комментариев будет больше пяти.
И повело себя API сомнительно, удваивая каждый раз параметр per_page
при загрузке новой странице. Для потребления анонимизированного трафика - это является крайне плохим свойством, потому как будет постоянно сжирать всё больше ресурсов самого сервера. Если так продолжить, то сервер начнёт более активные попытки стремительного бана такого рода паразитного трафика.
И тут интуиция вполне даёт ответ - а что если помимо per_page
существует также и указание page
? Давайте проверим.
Получилось. Таким образом, мы можем игнорировать вовсе параметр per_page
(по умолчанию сайт всегда выставляет значение равное пяти) и использовать в своей реализации преимущественно параметр page
.
Ну и последнее, что нам необходимо сделать - это проверить API на создание нового комментария. Благо и данный запрос является крайне простым и сводится просто к методу POST на адрес https://api.chatingar.com/api/comment
с указанием сообщения (body
) и поста (postId
).
Одним из немаловажных условий здесь также является проверка ввода большой строки текста, а именно здесь нас интересует действие самого сервиса - обрежет ли он вводимое сообщение на несколько маленьких (как это например делают ВК, Телеграм), или зальёт полностью одним сообщением. Для этого я подготовил вполне себе большую строку, которая является одним сообщением сети Hidden Lake (8KiB) закодированным в HEX (16KiB).
4a61831da281bbd06c010fb02ad9b79f64fd47a01377c4805427658f4f6dab3415a3bbb0a3fe0983e11d8c6a01700ff45ebfed9acea4129c966db5e82d0e2ae07c78cc8fc164d5172cf9faaca4e869a761f2706f24c355833da1a25adbb6da2fd91fe82f9a433803134071a965f39abbe6f8540bf39a5cadcfba50c719bd50a09af74abacc4dcdbd32fd71e85f7573742e30febf934b533debd109d014caace9e9bf06f51935cf7b2e64ffcb867b47b3ca02a5ae48a864a7ddc255531193c323f8b616096cb33486bb9a1df73c412e19d7f21fc4adddbc1b7996e31cd24ac3ec0309dfb9486154d3143d189f0b01af7ecdd2417dfb10e6226e247fdc67a8fe0ba74733649993fec335f91c68b8f7f4b5fd88f078fd0503da282d6888ddedebdd609cf0e6a1305b31d3f51c325e6da2276b20ec04c45167ebefe8c9d8b446d662bd262500e0a5cc1dddfb654ce0bc644ca381b068c20f7773ed3d0ffeb457377f7bd2858e7d0b57b0998a0a58a3c3b571d324fff3d67182ca4762b193d596a29eba95aa5154bec9fc0d1b4ff6d5cd367dcc9d8757463fdd5475c068a77e6da8ce3db4227e39657838a6e67a7b1ad139c9ae231a7e8bd90f16ae548fed8e516632907bc9fd9937d176318b24ed45964914fe5d634bef1a1c8b69ff517f9e65bac8a22cfd4acf57c46d8e22bf5eec8cf037b74afaecf317a2cb0f965b0c654e2a69dd6729cdd7d4d9b094d3a2c24a1b86578d8d6e86bc656d6f0bfb9575e8523fc83720658c341f24016ec4872a3d04b368a7e990b55ac171e147fed4a1964c590cb1f814507e49af4f0e35384f4161f9ab56b13bd0b902ef5f61c6ea46128998a835fb923fe552bdc3a0640b48cda6c61ba984583c4ea1090399fdbaa1d9bedca18b50605deac3632b7cec8a0ebae6845d0c37a0e7c0ce411c601ff3f6e4da0b4bdefbaff4775432ff7b024ba47bba110e366a7a89390079ccfb0990300121ec05ffbb9907f6347bafe675c6f10e5f468e74730c93634003c20fe63c112d5f4aac52ded926c181b6965c7801b9b3107126096afe53256b69ecb3855f13e55c3221d2f85ba74f90b0f29727048802f6c215418209d66abbd8c615f298df130c6e0bdc514c85a77b6fd35bcf65aa9c0f18550db732d34755fbda752985e566772e24b308d19984a1cf2c4b614c15016182691a0b76e43490adb0ce6a0bdd2718051b46ba7b5a88d606f4c72e806a45691db9821164141419514bd2cc91a5ac8f40f906f88a3db0597d90e067e754a4c005a76e54c112d83805b53a07128723fd3bfee2d01fc89cfbd90f4677bbdacecd30e94589de0e7261c70f88631e306c1abb156e97f80f9f5cb1511855afd00e91f2aa918ddfcbbe34c3e8f0474c04924894b856a4b164f0301e6a17a435e06334926f7e8764be2dc42446317fd131678c4c58e9cf155a63bdee03916ee327d49270bbe4809d3d01da62bae4f37c548708af216f107f446ff8be558252e81c922b8bbf45e095efeb87c15bc52bb47d10cc116b04ae52bcfc0c432beff4430255dc601ccf17b0da32d978e5e6ba6d9b5614f445a8577f3c55369a500211097b073ea376f28191923aacfe5dbd72c1e9b60031622bfa8e56c86d039028e1c690e32d4efd18078e53955ba67c6b51e55cd7f70ca0359f02b7fb1da164f2bc1aa30e918724de7e72babfcb899b4a360fbcd85b9e4f312b02f8a8ca7497f91b01727e9701a91e97e629583427610448c6a82c9a7f1b865eeaf6a35b57f10f88ad1ce2032a4a2242ac77c24df98001cb7322a081cfb01b602bccbb6f7856f4fbd5a8e1423908391da4b18c749e9869c94a7307c0cf1c726f0a650531171bb782a747752e8c0fd0153d60b6ded5a0427f739e16ed21b18d51a78b26d941ae1c748da8b27fe5d1c54c4e5d04c14e092d96ccd03ecd2e7c54c0c61159bf7c5d3eaab19ba6987f63ff0654b6a8b8ae7f9cd08693ac7c902a6062405187988edb38b6da5d8ce921354d99eb59d55da1830c02bc95d5c90152c211e48bf562d72e46139bf73b11c2d2e6841053a86f45dadfe8017d186ba01bfa8dcbddd9af05ed3d6349b7d666e347fbc2116f187084d93164a731ca503baf7f31f579a7d390194bf3d1e61b5fa38da3bacb5f1ae6e7b393f0f68d80132cdb747ec89260d2a9ea6b3e32127430a93d2d99bc670ba84cfd9275fd14d70938221a7cf62902fb0bbcfd786f9c6ec13efe0a88d8356f4b4f5a263c41168e5040de34bc95984ff29db100abf9dd488c8132010f0bd0223038dbccc179e636376b8102dc8d379e1abba993e40cb41e46223b9cb67c3c278bd730dfc1e251bc80e90160b854026b85bbc4a2835d9505aac055f33d8c47883b9980ab49a4326f423d95c0bd2e9601645a053dd6aae059025924559b9ca871e4c123fb6a3a0385c2c631d0f5c8de4c55d9cf76041a4eedf08be76742584f726faa60a31a6ff2f9450565a3a65d4e07d005bae58af03ebde0a7fa0ca3ef1647043055f31e03be40415257e2fcbe9220cd5ce381134042e85e5fc70c39e4c05cf2abc4a893e725c71497f942b4901e195401745ce05fe8f1923ad4c7cd7810c601b9a5cb81e726b7cbaadda9b867e0a21386d0e1c3b9571d6a1c3bdac47c2b2640232ad07ae2abfddaa01fd1916699651e20fbaa7f0067befe83dd19600e8a51047075ef2295a68576a7b89d8087b30bc1d5d51258d82115bb7c6fdc673a9308a460212347b85b68e4bd7a5b3d5cb3fe4c55e3136d2b3745a5e65c921805ca58f91930ce8d7953c51b605e7caf684042ffbbefab7fc781ea9ca324864962d419f14c2a5abcbba2842bbdfe8038effefa08add2e398f00935798dfe01c8428c036b8839085c0b447fcb17e160b84048272d9a57da16d3351ea4f4501595566ab62c8d05aed55098d9c388a5ec8d7a7dc42f61f34b02e3a015302faaef930c506d57a4e5c94affdd0db41a8aa5323b8edc9e016287cd246e574e1fa480084a34939e61bcded3d068fb939de7e000f60b0194f6f66b1e08afa99523d7077e3223fe0aafce2428c9ed8dc089514543fcef23872b277753c31d3b3d1c5d06cad81aaff3ed486a3d01a74e86d6ef0b7b3a2e733315366ddc64c81baacd8753b2c3fa2e3b8710491f2d33abbe3d1f515a136def9b5fdd32a2a348106f2f21619c0338f986de968c15287c195aa045d288c983a74de46ed554f41d8b5eb579d24946fe5974627b9cc3c212de6afba6c9762dde9123d8a11e5083140d9401fe06d32e98d2990fb7588cbe4db823bba115da28e1e8be20d5bffc034fb01174206758a8b9e0bd4bd3f18540b47e5a4d014e7083fc73fd91b394203592266580f9179475c937f9edbce7ed1088de6d615af6a9d4af1c940ab5ff223e4c6adac7970083e9bbe30b7b350288417d4b80f86d3a955e1b8aef19eecc1dd717d80b03d75bf972352309474ad3ec47bec793691565ba3dec64a75e6e15a1bbe1b83d7a8d0e53d655d7226804791867c210db8d9762d20d4fd1d0ed268c0354ee300092e39250f6d31616439fc63219aaeff72db13c7a01852931dd7eeec007e58b11ebcbfb23c4cd8ca7772cbb463ab86740a8577b89b67cdeb2035e8cc24c01e96e170867f05b9a10beb10760549791660443a9cd7df0dffdb5842f5426b566e3dd7fb5080ebe171934381081ab710c091a0695f56d70386c7ef0c611143bf7ffbb9318ed204ddeb3b13cd846cbdf3702090147d371ead3e99f21c59649beb9e683dcd0fc1f10d98b8f3e8511309f25927f11c84f0ea0b55a1b80141ec8d64b48acdf26a89ff4c6c692df2d42682a795edfd88ee0af5fea8f4bb85e4ae4984cfae51c38e888ddfb616ca04526ac72b19deba3c3d8f516b670512e7dce82cdd6be463fba91e63a4599cef0bbe419d6530e3735779e9f3bd6935111a4b667f78a3ddd1c3b79ea34f1679de1f820e0772959c84c0b179a5ed618daf0270b7bd9300ead11e1bd38e7a42ba62ce329cd4e80b5792f4dfe7e804a488ec23f88b3a45cc5332c9bbe37800882a5cd0d00278c8972d02425e9799a497d0330050d757f26f8262b531cd284769b10a8f9d675fe73f6f3f30f7aaa612a223118bf03e919e61a9121c336a2ba661d9c7ba02bd8721550d7aa2718d0cbbf39dd284b5ab370282cbd191831e20f91c1d6b5ec786dac2332bf802681231522f303c0f1b646d4e45a53ff90778193a3edd6408ad9b89b808276612cdcc83ed4f0beb74fe585f3aaa42363fbb4d92ec5bb8420a9f11a0b64d743498b2c7a62a52738b17a61917aeb2e28dbf1b406ab5fcbb1bc74f9a9e961fc3f061b96f3e0ee794e0e796d97f051340d03c0145a6576a525fcc47decaeb3a13426abc7bdf036e92ba072d0c88831cb823babe27d80f3cec6134616593e7446a6af2ff65c989a43476f9bdf2e2bc98fb78392f47e15f21fcc3730e1db4803cc3de617980b35ded6874170b2e56f86f36e1797dc42f72ea192d62654f3982e2637253afe5196805e7f0098f851b5c08a03ddf682c5937ffe483daa130be95ebf0214472fcf4d2f06ff90c64ac109de91bd7768404c087fe21ffcd69bed5559482a9228944792ea205069e4eb2e15e555d740b95d221c9064557653ecfc6251806831345f166bcb477081c3492d4071fb740030b88f07b0e1e00ebda772727ff092ccceca23b491ed4663de35ac826ab522babe371c07d7171fc81d2a47af2af6d7906ea0ae462ee9e343939796c60abb48fa46389fc83b78f33417927c5444c45c47309d03a2ce5f27e6394250242cdd80fedc61c999f4051c2457fec2fff92eae7df1731a39d2362f3ddc47f2827d3c22f0f91fd4445de3aa1c66c962352f7d591f889e15af04f5509f4a1df0eca5458749e36befffc5913bf6f2ed4f213642cded3dbfdca3a4c3a19383f772b5ff97c889faaee819bdfaf1bbaf63c3867808babb46f6cf3146d71fa901cc38a4ee675ec4fe871971ede532906e55e025c45459b91725230930eb2529b053169398dcfe0b362d554b5443f888065fbe2345b26828a96b78136c644334817b231c6bc31a9c4039538f348a667285be85193e904c8637bcc70bb41538abbba87473de9092f2061f6675488cf9a41ce14501e08df6fc50be729172a75f0969671bda19c350392a172aa869704f0ea2cad9caad0437a21b606a669641b8409417ff1554833ef956f15d3ca02f9b808e04fb6920df4295fae5f8a9b59d854a70f9a4638c1143639d9e8d56802ada8001257885e2ad56b0a0dc403e0c6c7a415c2a34e3eec3313d0e179a72c4bf326f1013c0d63ccd275f98b59d14a6fd079b553a39bb09bafdab3c34f4f6e345961114f92332809c465167ef74a9259a3959a3130147d605b4e086e9ff5bb4c87c6fd7e185c9ca23bdc6130d3ad15a9ae75d2a1daaae84273209ae53f3795eca5199a91646a67ee2ca10b5cc2845324aa120e0d21164d39296b6a039e0174da9a0ccc0ce4070058363a88bc511419d6d82a6ecb1c04d753b57a7aa19d645c468de7dddc4daec308ce571454827d218ade8bfb338c0a73d9f9c6e0de8d81a14699162da41aa6ec219e073e2222558ad0e9a6bc263fe14137d4be2719a3770033143d62a898e113aab37db26389a80474a1a32038ca530ee773936de582c29d1a0f0ea29d16799a34fdab620042cbedf6e4215c53486bf096dfb289b7474944b5c14a2802105dfdbbd893298cfc15fedc25f1b92bfb24516d12039b9105cb4c487fe3fca717d05990ab46689ca080df9eabb562009ae6fe5bb4bf9f31389f0e9a86e185b9fa4ced6500491c569ecde12bedf7c2a0f57283d3c69077bb0dd598d77a48f3297eeadd1d95f7d31c326451937614e6c1fd82073b8e64449637f84eb3743243fa382dd6e4201c147390b66fc0ebb0a802f6dddacd673e7f6faf7be2bf92f0d995ade09c6c5677734bdaf94a2f18bf122227badd7eaa49e7e28ef97c6a46be270e2da038445f8b39bd4274b679bb14b915334f46622682455746369a18d594fa1e5eb6fc31fe5102574c76b1f110211cb33d63ddee0988e5d10afa1fa11972dbd07f383b3b09e2d518ad7aaf05ab7031f45ad84b69178ee02fe2139a2d4247034624636388e3c2a0522d77004e50f1c9e7a025e470c15572aadaac016fca9ba23ed5243c041840ed0b1536f3b713476df289776c88aa749d78fee99f10fae68b72b27e2c6a62fd528a5f95e153635fc521085923c6651e2d3ab27a1b733d7d7cd7684317f262cb509c27f11f3d5eb8b83a71ea16e88ff4d597cd7cb7fee47c7d1bd1037b5763f569fa57af84e032a3d2c600ee15522ef735977b5cd6a5811f7c64362c13d7376cd7bba7db6204ace2ffe0f94e7bfd77ed3150f03252b86b49874b026a90130a8cb75c8ce3699a7f8f35941d57af55b27ee22a05e385f1ae5926bd64207dd933aa941db8f512d08fd51971a6a4f96566b36ac8127c00cc110cbdf534ec6cb6e524b1f9d9b762bb24d9beaf520dfd6cd224f857ab46bd0ef939689d1137e0d88a434ac7ec8930e112eb06d420f831defdd0789531ebea4eb4c031d6149628c33d580ab2a7f87a015c7f779367c643a463c69cd4751ddb5e3d091cd4e15ddcefadbe1e3e2175f3a602d455c8aca59f8c60a5cd83787b1ab7ecd318f41ad3281624092e6ba7fe79e0fccd1047b164fa70c72d6ba930999878b7201554b30b88becb84e9be255c8832699675c2156bd334d95ca400c2038f8622d76c820ea644ba50c99f6e6a817f4d2d21ac40d83837ca9fde6d979e36b980d4d74a4e158c526b4e189087af5b011a73c9da4368a3e92d870a9f4a0ddc64a572733fd4b50d6313daebd70e52b5f05af4a543d61f91442ed9e9e70581428b9c27c684feb58820edd96285615efd2318973b28a5f01742391dead72cc8fe2acddb4b288ef64f6beed4d3eeba08ccd8d5c199b7dd6b23427b04899b64cbc17cd570cbe8d54bed8585cc2a9d3e8aaece1e796e0406d7f1aa827904a346782298a35e40833c31bd42a511257821a657c966f6c0024e5680c13df978da334b0a8830f1506b1b26c59c185e16466ffa13648e44696e4e6fdbdf6d76b750c4815f9e9d691cfb6c25e8bacb52146a0242a0f3fc84c2852c4e60d92c3d323f9594e7538170203eb5409251864bd4076fa59f162b58574ed004233679589f7cefadfaf3d93367008e3820da2b0867627f6d3949d76e0d90fe2f3f580794285745f5cb40740d27522cb593df780f666a9e2495d44fe436ae412476017d61771a496e21e9c3c6a00f4af88f77f5f38805bdda7e2de89265bd965ce040b4d654cfcc2066c40fa19f62fd7126986d7ffc39e29523e84fd5b36a65cdad5cd3604812df69a12dd8529084a87d5f8c7196999284a5ef59be3c539f2f15f7e4441c0c4aafdde0238bfb9c2c41bbaeae5efe595d4bc248c462c8c3edf2b62d55c4356d25c22d34c3658280675101e0ac0e149f781f7c1016e7cba49d62dbde4262d14ed5002329b6aaee8ba17f432b300227ae0b32cc35ceb030d17db40f300305aa6c8f00b32532356daf9c6374ff712dcdcc800c4277571b63f63605e5fbf54e97ade9348e710bd6d976365dec934ea3e502931649d5a6cbd0fa41a183ec9d8d8ba2b959cb670d1aae57d3212d0f6b5c6277d6984351f047f7e63ed0998af5d29df4ac4567047deed8a816cf6c3ddbf1b7d0a6c23536cf5cfd76c86fd61501eb5f0a779d590d4024fb20d001995b365884ba600cfa9c878ddcfd23f85ae81fd118d954096c7b92d6d25da1f8a74987d04c6ba8adf81239d1ada19aacf6d02c0b37f949b08b404bc309b95ca0f9b587e750d73b78393bae0fd9a24fba45aa3531fb9036ec0e08cc9171e7ef4676be82714182d9969d2851782295d84d2c1c3f978281877d5cdcb92f2c452852ad6794111e9c71c1314396d5c606d5f1b23203a0282f4e86565193295e1aa54f89203952e6fb4c4177cb63c77dc34baeb176e7cedb823d11671e076cf194dcd68ded0ccff82061c4b30198cd8ea928028c05d24a8bf509d1a1fe91c3a1bf55463a581fdec1bfcad07d2e5a6775459cfea7d281b9e405ab532c315a36cb84d23ab6120633ffeee8a7c2cd0c8ddfede3e10e057f1c4e71b483a55271828aeae972e238a3eb86a5e44c20823e978bf080d448efcf6c0380a8a69a5b33e300cca86504d5f96d972bad808d658dfec9cdc18f9b2d1f684cb0f7c6d1296073b12f507c71c15056ef9cfe86cb7e18515ef1179d63038851f450a40b5744193a559a5dbd2eb2e0c887d14e03a3971f07a966b2fa2dcd1ef41c60d908bcc5e073a2b038b69232c047e5a3b33414ce8eefc73652c8f8e791bbacd9a0f010a457bae5700edf5bb25ffd3743495ccffa711db346d2a1029021fa120a6c5b4915ce9082496c56e711dd59d8957bde80904f03638286807dd7d5ed64b8fd72284ebdd12cfa7a409ba9549685680b29d1db8c85c2c1eb4d3c202841bccb2d77e7e98ecd0b6cb41b2e4c83cc31558b319263943ffd4d7e9e6e5c39bcb42fe12ea3285e2723ccc3afd22d7b532576df62bfbc7451c8d6af71d7dff0a11c412c7f4cf2da8099351121ced3ef9ec1020aee055afe0ab769bb948199b8e13760f94b043506eeb5c3c9fff6e4b0fca9c2e9bf10ee19dc26340ff9153c9d9935f1ee54cd3dc2a21537239bec32e44e6efe713dcb643759258622ebd34d08700c49525688ad39c68636bbbd63628dc0bca6bb5bc68159237e1ff5cb7b7ca7608461027d23b632902b3e2defe10b36a2681b523fe88a0391843f633239c27657e0cedefb43aca7b2a783525c688bd3969d05ad82ae6c843de41a3baa187e0e4623d3b66ad76f370b0caaf099717bfdf9c8050504a46928e29af80ba549b1cbd605ecf54bc0e7a529082b9f3fb1e83c86c3945f4efb7f9a599f49e6e4420b675187838dde61eb05af97d6450298a6674bcb275a954b1df14c4792e5a81983e26c14de66fa34b6f1ec195251fc8c8343ea202696e4314c35023dca2d605245744f062a32d39747d8aa7060aefdb1fae22c839ef19a4a9624123901c91ad411c4c91f0ac653181950697e1051b5175b2515963aa5b40b5975b8fef48e8bde55822027fc2c70a1d448dcd1e8feff02dd3ab575f5263882d8fb88bb199840f14f375e3b715a4d2bedbc47de245a46b0284e85c0c0c3c07b2ee6f9886e1f89d043be1d89d7e9c82e8dad9813a6d5f8bd2256ee48d64dd3b04451a3a145f5f4cb445c4207fe8805c79561615d85e691c97b81d445c170943b2b0b5b846adbc2467abc70279b9b1a5465ccba6f2f22d9496ea65c4a40b122e1c704e5dfa01ddbcec59d25d19a400ee530d2218d1ac3213e4a0cf9e54797f015c1d56f1e09e113823663334c361a77c444adb367dcd77c9b64401b27a4fa87c5e1ffec6720630e77a88220fc19884e847d226753e0dd88e652a5ee0c4dc513eea5daf5b447f133d88407e1d8d72c3c166f57ea996110f5db07ac7a904d6c2a79c30dd18f350483d575522753f782c6d6c21f64763cdc0c38fddffac6ed1d66e8ffa28f94d4e8d55be7b6e75f1ad366ace514bba6307c3d8a9e6da98d066d7675c201457ce8fa8246af729e0c1bcc525ce5c1d895f86d72b29701b1c43ab33de5fe1641f9ed99281ce87feec4fd3eb41c1bd7856a5b36eecddb4aa0a419187987dbc2f953c0546a30762f2351dc1ec89a03657acb2bec039f0432e6d9a59a02f051f9b1bd828e5e0ae1e62219d5faaa809dab33b741e311f78464428557a63cf5673e55b70e4157ca8c1b1f1568cca51d203c2a49c547eb049def16cf0f3c6c9f5ff2af676efb1cdcd3951a93e16197bb3a5b403a9e18648be76deb3d25473fdab022cb0abeed6d42debf654c95304a344f1ecded02221a7645dfcbbf3da6ef66eb7ab24f0e7f5c399b3bd1a473e0fc053c254c69df539de7ba0d30cfe230845d3229f543840294a92af12f1d5e5d7d3920231caa0e9fc5e76072d17774d4ced330cbd3ed9a98c4f9443db703c5e5285ffe36e3ea63084198660101af6ba59e59c627ed3e1299a8b1b7a07da263e9b567ca5599c93786837444c4aee2f816057f7e5fc78368a0ce309236a59b6b9774726e6cfeff3778d447451d32a003fa128a18f4b3a64c5c99123fc4988bf4afc0878357d902e4a748b49ebfd50b34232dd650e5c90a43fc8f82edb821345e8ab7a49f864abab385723ca82fd012681337407b78d354a1f541cbd5f90c252b0dc54bc2aea1af5fe614deb093fb6aba2538882f8058c312f0d4102041fc63e8cd57a860fd07558611f78286f3b11f1ff46f77b7a257e430acabe5a1c2382d4daa1e7bbcd52082490fd92f62a0ea4223f33ecd2920c7f8b0a42500e9a09f9b4ce23696487bee94e275f7d635ddff157d81a0eecc9ceb262b7aace20b8f8b5101c4ed45bfb944936367d50f4e747f78fe30fc7f9cb7061e2a92221cc60622b90bb62213d88a956f5e2320bcad0a7b6e6bfd30a12edbd2250a9b9ea9e6b41ace8d2882c7c68683dd0ea77960c177bae9d3e3b54a8ff366132205b749ba76d8667b79c567421e878a512c8a451afa40666221ea6714c064135cf3d1f6a8a28ac10bf4b9ce8d5b0b1a926c80732592bbb22cac92cf107d4491b39274b79f08bba10bfda91199bf1325c6e13f56c468e958d28b438585b062a0b47aed99903ef596f78650175a05b90b3b5f08315e4e662ed4e98b8a1f04945bf566f6747ec1a2399e147f999a41fa846e8fa523e7522a36f1f08f6f31c52c59e68a1d8ae92b37bd63a3d8056167c601e2403cbc58f38c5db53527519615ab9c960ec12875abe5c5c28d36ac2fb47c1a3d7f94e147a66fb5711e49f56e89328ae45384317db64354b53e1c7ec74047a57f3247939ff1b8cf49793f8d5c3501ca98763e34f262ae62f94cab0d43671c96421c0d858acb880632e9cfd64a8b429ab2531d81706be6163269dff96fde9a3d6df212cc12fe57f051ce0b813f03ab3d597866eebb64695e05047926c2bc5e1282188bcc9f624cadfda328ce8d8a538045bac6a31ac4a5e238e79088b96f2aa72139b6bd3a6367b0f9e989886abb1b04120e39b446ab7a52ba65f39ebd1be66d1be8306c741a9c8017a0e86a3449191e23f27cf7869380dc4fbef476f8e8195e1ac7442df0d542fc038cfec11ae7887e651154114554250d8f15ecdc4b25d31f675d9429f0039d9d81bb82ea34dc2cc3e5ad90eb79c194c4c2c7bd95544e57ed8ee0aacaa8739d5a33704f2af29628abf8335a547efccb988b946c47a9df89ca15e1abf895d09ccb48cfbb06b45019ab8b700320c6c933c2068f147bd532aa289b04ad61a827676461f5f8794024d09875b0ee6b9cac9b6cafdad50fc2548e425e799780a3eb10bd2ca74e4731c4fd6e3adf9ee0736e5efc6611b2c290f11ce5c0117672f605a9a54f33ed36221ce15f02f25f5f8110ca35acc6e0ba460646574724ff5e83971785cd99a76ca86da77753d8a6f494cace4de49d2a3fe0467bd6193447c47fe508774dfbd4c14e6cdf866e66b84ca59e227722501acc2bbc215da4298aef93cbcbfb303d0c31313328d9c7af3554a8803ce5bf7798de75d6ebf8a7900c38ae4fe1aca1a40c24df7ba3a1a78a3c04270805da268673414e8a9ecbc71d42ee21fbd929b466bde7569126f0aebb4
После вставки данного сообщения в API создания комментария, сообщение было успешно и полностью вставлено. Это говорит о том, что для нас упрощается жизнь при чтении новых сообщений, без какой бы то ни было необходимости добавлять логику объединения нескольких частей одного сообщения.
Ну и всё, этого для нас более чем достаточно, чтобы со всем комфортом начать писать адаптеры под данный сервис.
Адаптация адаптеров
Адаптеры в сети Hidden Lake, для успешной своей реализации, должны придерживаться двух интерфейсов: IAdaptedConsumer
и IAdaptedProducer
. Первый должен уметь принимать новые сообщения из сети, в то время как второй должен уметь создавать новые сообщения в сети.
type IAdaptedConsumer interface {
Consume(context.Context) (net_message.IMessage, error)
}
type IAdaptedProducer interface {
Produce(context.Context, net_message.IMessage) error
}
Все основные запросы в chatingar осуществляются по большей мере с одними и теми же HTTP заголовками. Поэтому чтобы не сильно выделяться, лучше таковые хедеры вставлять при каждом новом запросе.
func EnrichRequest(pReq *http.Request) *http.Request {
pReq.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0")
pReq.Header.Set("Accept", "*/*")
pReq.Header.Set("Accept-Language", "en-US,en;q=0.5")
pReq.Header.Set("Accept-Encoding", "gzip, deflate, br")
pReq.Header.Set("Referer", "https://chatingar.com/")
pReq.Header["content-type"] = []string{"application/json"}
pReq.Header.Set("Origin", "https://chatingar.com")
pReq.Header.Set("Connection", "keep-alive")
pReq.Header.Set("Sec-Fetch-Dest", "empty")
pReq.Header.Set("Sec-Fetch-Mode", "cors")
pReq.Header.Set("Sec-Fetch-Site", "same-site")
return pReq
}
Здесь существует несколько подводных камней. Первый и самый понятный - это статичный User-Agent
. В долгосрочной перспективе лучше его всё же сделать как минимум случайным. Второй и менее явный момент - это реализация хедеров в самом языке Go. Тот порядок заголовков, которые я указал в коде, является правильным, но сама структура Header
является мапой map[string][]string
в Go, что автоматически будет сводить все заголовки к рандомному порядку. Если сервер за этим следит, то он нас крайне быстро сможет вычислить и забанить. Одним из возможных и наименее проблемных решений может являться использование нестандартного http пакета.
Producer
Начнём написание адаптеров пожалуй с IAdapterProducer
, потому как это будет самым простым адаптером в нашей реализации, где достаточно одного лишь запроса. Создаём для начала конструктор в который передадим ID поста.
func NewAdaptedProducer(pPostID string) adapters.IAdaptedProducer {
return &sAdaptedProducer{
fPostID: pPostID,
}
}
И далее, всё что нам нужно - это сформировать запрос с указанием идентификатора поста (postId
) и нашего сообщения (body
), отправив его на URL адрес комментариев /api/comment
методом POST. Сообщение pMsg.ToString()
представлено в виде hex кодировки, так что никакой коллизии с двойными кавычками в JSON формате не будет.
func (p *sAdaptedProducer) Produce(pCtx context.Context, pMsg net_message.IMessage) error {
reqStr := fmt.Sprintf(
`{"postId":"%s","body":"%s"}`,
p.fPostID,
pMsg.ToString(),
)
req, err := http.NewRequestWithContext(
pCtx,
http.MethodPost,
"https://api.chatingar.com/api/comment",
bytes.NewBuffer([]byte(reqStr)),
)
if err != nil {
return err
}
httpClient := &http.Client{Timeout: 30 * time.Second}
resp, err := httpClient.Do(chatingar.EnrichRequest(req))
if err != nil {
return err
}
defer resp.Body.Close()
if code := resp.StatusCode; code != http.StatusCreated {
return fmt.Errorf("got status code = %d", code)
}
return nil
}
Consumer
С IAdaptedConsumer
всё чуточку сложнее, т.к. здесь нам необходимо будет заботиться о количестве ранее прочтённых сообщений и двигаться постоянно по новым страницам, чтобы успевать читать актуальный трафик.
func NewAdaptedConsumer(
pPostID string,
pSettings net_message.ISettings,
pCacheSetter cache.ICacheSetter,
) adapters.IAdaptedConsumer {
return &sAdaptedConsumer{
fPostID: pPostID,
fSettings: pSettings,
fCacheSetter: pCacheSetter,
fMessages: make(chan net_message.IMessage, cPageOffet),
}
}
Конструктор также принимает ID поста, но помимо прочего он вбирает в себя Key-Value базу данных (для сохранения состояния прочитанных сообщений) и настройки сообщений, чтобы первично отделять сообщения сгенерированные анонимной сетью от других сообщений.
func (p *sAdaptedConsumer) Consume(pCtx context.Context) (net_message.IMessage, error) {
// Если потребитель запустился в первый раз, тогда...
if !p.fEnabled {
// Запросить у сервиса количество комментариев
countComments, err := p.loadCountComments(pCtx)
if err != nil {
return nil, err
}
// Вычислить количество страниц от количества комментариев
p.fCurrPage = (countComments / cPageOffet) + 1
p.fEnabled = true
}
// Загрузить с сайта комментарии со страницы currPage
return p.loadMessage(pCtx)
}
func (p *sAdaptedConsumer) loadMessage(pCtx context.Context) (net_message.IMessage, error) {
// Если в очереди существуют сообщения, тогда вернуть от туда
// одно сообщение в качестве результата
select {
case msg := <-p.fMessages:
return msg, nil
default:
// Иначе запросить у сервиса новые сообщения
}
// Сформировать запрос с текущей сохранённой страницей
req, err := http.NewRequestWithContext(
pCtx,
http.MethodGet,
fmt.Sprintf(
"https://api.chatingar.com/api/comment/%s?page=%d",
p.fPostID,
p.fCurrPage,
),
nil,
)
if err != nil {
return nil, fmt.Errorf("failed: build request")
}
httpClient := &http.Client{Timeout: 30 * time.Second}
resp, err := httpClient.Do(chatingar.EnrichRequest(req))
if err != nil {
return nil, fmt.Errorf("failed: bad request")
}
defer resp.Body.Close()
if code := resp.StatusCode; code != http.StatusOK {
return nil, fmt.Errorf("got status code = %d", code)
}
var messagesDTO sMessagesDTO
if err := json.NewDecoder(resp.Body).Decode(&messagesDTO); err != nil {
return nil, err
}
sizeComments := len(messagesDTO.Comments)
// Если мы получили в ответе количество комментариев больше чем может
// быть на странице (cPageOffset), тогда считаем,
// что это некорректное поведение сайта
if sizeComments > cPageOffet {
return nil, errors.New("has limit pages")
}
// Если количество комментариев равно cPageOffset, тогда надо двигаться
// к следующей странице (инкрементируем счётчик страниц)
if sizeComments == cPageOffet {
p.fCurrPage++
}
for _, v := range messagesDTO.Comments {
msg, err := net_message.LoadMessage(p.fSettings, v.Body)
if err != nil {
continue
}
// Запоминаем сообщение, чтобы его дважды не загружать в очередь
if ok := p.rememberMessage(msg); !ok {
continue
}
p.fMessages <- msg
}
// Вернуть сообщение, если такое существует в очереди
select {
case msg := <-p.fMessages:
return msg, nil
default:
return nil, nil
}
}
Оставшиеся методы
1.
func (p *sAdaptedConsumer) loadCountComments(pCtx context.Context) (uint64, error) {
req, err := http.NewRequestWithContext(
pCtx,
http.MethodGet,
fmt.Sprintf("https://api.chatingar.com/api/post/%s", p.fPostID),
nil,
)
if err != nil {
return 0, fmt.Errorf("failed: build request")
}
httpClient := &http.Client{Timeout: 30 * time.Second}
resp, err := httpClient.Do(chatingar.EnrichRequest(req))
if err != nil {
return 0, fmt.Errorf("failed: bad request")
}
defer resp.Body.Close()
if code := resp.StatusCode; code != http.StatusOK {
return 0, fmt.Errorf("got status code = %d", code)
}
var count sCountDTO
if err := json.NewDecoder(resp.Body).Decode(&count); err != nil {
return 0, err
}
result := count.Post.CommentCount
if result < 0 {
return 0, errors.New("got count < 0")
}
return uint64(result), nil
}
2.
func (p *sAdaptedConsumer) rememberMessage(pMsg net_message.IMessage) bool {
hash := hashing.NewSHA256Hasher(pMsg.GetHash()).ToBytes()
return p.fCacheSetter.Set(hash, []byte{})
}
Всё, мы написали последние штрихи и осталось лишь подключить сие творение к функциям процессинга. Это уже является стандартной процедурой, которую можно посмотреть здесь и здесь.
Тестирование и запуск
Для проверки работоспособности нашего кода я написал несколько docker-compose файлов, а также добавил все эти адаптеры в анонимную сеть Hidden Lake посредством приложения HLC (composite) для мессенджера (HLM) и файлообменника (HLF).
Заспамленность сервиса я поставил щадящую, а именно: каждые 5 секунд происходит запрос на количество комментариев, каждые 30 секунд происходит отправление шифрованного сообщения размером в 8KiB (с учётом HEX кодировки).
Чтобы запустить всё это творение - необходимо в первую очередь скачать репозиторий go-peer. Далее зайти в директорию примеров с конкретно данными адаптерами и запустить нужный.
$ git clone --depth=1 https://github.com/number571/go-peer.git
# Запуск HLM:
$ cd go-peer/examples/anon_messenger/docker/secret_channel/chatingar
$ make
# Запуск HLF:
$ cd go-peer/examples/anon_filesharing/docker/secret_channel/chatingar
$ make
Все прикладные приложения сети Hidden Lake продолжили успешно функционировать с трафиком прогоняемым через HTTPS-сервер.
Заключение
Таким образом, нами были написаны адаптеры сети Hidden Lake под централизованный сервис chatingar.com, позволившие внедрить в последний анонимизированный трафик. Такой ход действий дал возможность:
Скрывать специфичный для анонимных сетей трафик внутри HTTPS соединения на реальных централизованных сервисах, при этом сохраняя прежний уровень безопасности и анонимности,
Использовать сторонний сервис как один из способов обхода NAT для P2P коммуникаций без аренды VPS или использования собственных ресурсов.
Все нами написанные адаптеры находятся полностью в открытом доступе, исходный код которых можно посмотреть здесь. Запустить и протестировать работоспособность адаптеров можно через docker-compose тут (HLM) и здесь (HLF). Узнать более подробно о проекте go-peer и об анонимной сети Hidden Lake можно в документации по ссылке.