← Back to list

Небольшая история про tls.Dial и Qrator

При работе с https в Go обычно используется стандартный пакет net/http, но иногда нужно поработать с tls.Conn.

KIV in KIV — блог · 2021-12-06 21:14 · 1 claps · 2.0 min read
#golang-tls #проблема-с-ip #avito #golang #alpn
Open on Medium ↗

Небольшая история про tls.Dial и Qrator

При работе с https в Go обычно используется стандартный пакет net/http, но иногда нужно поработать с tls.Conn.

В одном из своих проектов я использую прямой конекшн и отправляю сырой текст HTTP запроса в него. Реализация простая и выглядит примерно так:

package main
import (
 "crypto/tls"
 "fmt"
)
func main() {
 req := "GET / HTTP/1.1\r\nHost: www.google.com\r\nUser-Agent: Firefox\r\n\r\n"
 config := tls.Config{InsecureSkipVerify: true}
 c, _ := tls.Dial("tcp", "www.google.com:443", &config)
 c.Write([]byte(req))
 buf := make([]byte, 1024)
 n, _ := c.Read(buf)
 fmt.Printf("%s", buf[:n])
}

Всё это работало прекрасно, до одного момента.

Успешный ответ от сервера google

Успешный ответ от сервера google

Появилась необходимость общаться в таком режиме с avito и тут появилась проблема.

Код ответа — 403. Плохо!

Начал разбираться в чём дело. Авито работает в связке с Qrator. Это платформа для защиты от DDoS. Похоже, что нас не пускают на сайт по каким-то критериям.

Для эксперимента отправил запрос через net/http. И получил 200 код и нормальное тело ответа.

Становится понятнее… В чём могут быть различия? Переносы строк с \r\n, \n, какие-то заголовки добавляются?. Поднял быстренько Vikingo Satellite и проинспектировал запросы. Никаких дополнительных заголовков не нашлось. Значит нужно копать в сторону TLS.

TLS

Отснифал через Wireshark TLS хендшейки в двух разных сценариях и начал изучать различия.

Размер рукопожатий

Размер рукопожатий

По размеру можно понять, что они всё-таки есть. Листаю дальше и нахожу поле, которое есть при работе с net/http, но которое не выставляется при tls.Dial:

Слева net/http, справа tls

Слева net/http, справа tls

Расширение Application-Layer Protocol Negotiation (ALPN). Вот он, виновник.

Выбор протокола внутри TLS соединения

Выбор протокола внутри TLS соединения

При использовании стандартного tls-конфига, который будет использоваться в tls.Dial нет списка протоколов, которые будут использоваться в рамках соединения. Похоже, Qrator не очень любит гостей, которые пользуются не браузерами =)

Для исправления ситуации достаточно заполнить поле NextProtos нужным списком протоколов.

config := tls.Config{InsecureSkipVerify: true, NextProtos: []string{"http/1.1"}}
 c, _ := tls.Dial("tcp", "host", &config)

В результате всё заработает.

Успешный код и тело ответа

Успешный код и тело ответа


메타데이터
post_id
511bec86e666
slug
небольшая-история-про-tls-dial-и-qrator-511bec86e666
url
https://medium.com/kiv-blog/%D0%BD%D0%B5%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B0%D1%8F-%D0%B8%D1%81%D1%82%D0%BE%D1%80%D0%B8%D1%8F-%D0%BF%D1%80%D0%BE-tls-dial-%D0%B8-qrator-511bec86e666
canonical_url
https://medium.com/kiv-blog/%D0%BD%D0%B5%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B0%D1%8F-%D0%B8%D1%81%D1%82%D0%BE%D1%80%D0%B8%D1%8F-%D0%BF%D1%80%D0%BE-tls-dial-%D0%B8-qrator-511bec86e666
author_url
https://medium.com/@kivim
status
ok
fetched_at
2026-07-27 18:16:53