HTTP → HTTPS majburlash
Server HTTPS’da ishlayapti. Lekin foydalanuvchi brauzerga api.example.uz deb yozsa, brauzer ko‘pincha avval http:// orqali ulanishga urinadi. Eski havolalar, xatcho‘plar va boshqa saytlardagi linklar ham http:// bilan boshlanishi mumkin.
Bunday so‘rovlarni ikki xil usulda qabul qilish mumkin:
- Rad etish — 80-port umuman ochilmaydi. Foydalanuvchi "sayt ishlamayapti" degan xabarni ko‘radi.
- Yo‘naltirish — 80-portda kichik server ishlaydi va har bir so‘rovni HTTPS manziliga yo‘naltiradi.
Ikkinchi usul foydalanuvchi uchun qulayroq. Lekin uning ham zaif joyi bor: birinchi so‘rov baribir ochiq HTTP orqali ketadi. Bu darsda yo‘naltirishni to‘g‘ri yozishni va HSTS yordamida zaif joyni yopishni o‘rganamiz.
Yo‘naltirish handleri
func redirectToHTTPS(host string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet && r.Method != http.MethodHead {
http.Error(w, "HTTPS ishlating", http.StatusBadRequest)
return
}
target := url.URL{
Scheme: "https",
Host: host,
Path: r.URL.Path,
RawQuery: r.URL.RawQuery,
}
http.Redirect(w, r, target.String(), http.StatusMovedPermanently)
})
}
Bu kodda uchta muhim qaror bor.
1. Host so‘rovdan emas, sozlamadan olinadi
// ❌ Xavfli
target := "https://" + r.Host + r.URL.RequestURI()
// ✅ Xavfsiz
target := url.URL{Scheme: "https", Host: host, Path: r.URL.Path, RawQuery: r.URL.RawQuery}
r.Host — klient yuborgan Host sarlavhasi. Uni klient istalgan qiymatga o‘zgartira oladi:
Server foydalanuvchini hujumchi saytiga yo‘naltirib yubordi. Bu ochiq yo‘naltirish (open redirect) zaifligi. U fishing hujumlarida ishlatiladi: havola ishonchli domendan boshlanadi, lekin oxir-oqibat soxta saytga olib boradi. Shu sababli manzil domeni sozlamadan olinadi. Bir nechta domen bo‘lsa, r.Host ruxsat ro‘yxati bilan tekshiriladi.
2. Faqat GET va HEAD yo‘naltiriladi
POST /auth/login so‘rovi HTTP orqali kelgan bo‘lsa, parol allaqachon ochiq tarmoq orqali uzatilgan. Uni yo‘naltirish muammoni yashiradi, lekin hal qilmaydi. Bundan tashqari, klient buni sezmay qoladi va keyingi safar ham xato manzilga yuboradi.
Shu sababli tanasi bo‘lgan so‘rovlar yo‘naltirilmaydi, ular xato bilan rad etiladi. Bu klient dasturchisiga API manzilini https:// ga to‘g‘rilash kerakligini darhol ko‘rsatadi.
3. 301 yoki 308?
| Kod | Nomi | Metod saqlanadimi |
|---|---|---|
301 |
Moved Permanently | Yo‘q. Ko‘p klientlar POSTni GETga aylantiradi |
302 |
Found | Yo‘q. Vaqtinchalik |
307 |
Temporary Redirect | Ha. Vaqtinchalik |
308 |
Permanent Redirect | Ha. Doimiy |
Faqat GET va HEAD yo‘naltirilgani uchun 301 yetarli. U barcha klientlarda ishlaydi. Tanali so‘rovlarni ham metodini saqlagan holda yo‘naltirish kerak bo‘lsa, 308 ishlatiladi.
301 va 308 doimiy yo‘naltirish hisoblanadi. Brauzerlar ularni keshlaydi va keyingi safar serverga murojaat qilmasdan to‘g‘ridan-to‘g‘ri HTTPS manzilini ochadi.
HSTS
Yo‘naltirish bitta muammoni hal qilmaydi: birinchi so‘rov ochiq HTTP orqali ketadi. Kafedagi umumiy Wi-Fi’da hujumchi aynan shu so‘rovni ushlab, foydalanuvchini HTTPS’ga emas, o‘zining soxta HTTP sahifasiga yo‘naltirishi mumkin. Bu hujum SSL stripping deb ataladi.
HSTS (HTTP Strict Transport Security) bu muammoni hal qiladi. Server HTTPS javobiga maxsus sarlavha qo‘shadi:
Brauzer bu sarlavhani ko‘rgach, ko‘rsatilgan muddat davomida:
- shu domenga faqat HTTPS orqali ulanadi.
http://havolalari ham ichki ravishdahttps://ga aylantiriladi va HTTP so‘rov umuman yuborilmaydi; - sertifikat xatosida foydalanuvchiga "baribir davom etish" tugmasini ko‘rsatmaydi.
| Parametr | Ma’nosi |
|---|---|
max-age=63072000 |
Qoida 2 yil davomida eslab qolinadi (soniyalarda) |
includeSubDomains |
Barcha subdomenlarga ham qo‘llanadi |
preload |
Domenni brauzerlarning oldindan o‘rnatilgan ro‘yxatiga qo‘shishga rozilik |
Warning
HSTS’ni bosqichma-bosqich yoqing. Avval max-age=300 (5 daqiqa) bilan sinang, keyin asta-sekin oshiring. includeSubDomains qo‘shishdan oldin barcha subdomenlar HTTPS’da ishlashiga ishonch hosil qiling. Masalan, old.example.uz HTTP’da qolgan bo‘lsa, u foydalanuvchilar uchun ochilmay qoladi. Brauzer qoidani eslab qolgani uchun uni tezda bekor qilishning imkoni yo‘q.
preload esa faqat to‘liq ishonch hosil qilgandan keyin qo‘shiladi. Domen hstspreload.org orqali brauzerlarning ichki ro‘yxatiga kiritiladi. Shundan keyin brauzer bu domenga birinchi ulanishdan boshlab faqat HTTPS orqali ulanadi. Ro‘yxatdan chiqarish esa oylab davom etishi mumkin.
HSTS sarlavhasi faqat HTTPS javoblarida yuboriladi. HTTP javobidagi HSTS sarlavhasini brauzer e’tiborsiz qoldiradi, chunki uni yo‘lda soxtalashtirish mumkin.
HSTS middleware
func hsts(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
next.ServeHTTP(w, r)
})
}
To‘liq misol
Lokal kompyuterda sinash uchun HTTP 8080-portda, HTTPS esa 8443-portda ishlaydi. Production’da ular 80 va 443 bo‘ladi. Sertifikat Self-signed sertifikat darsida yaratilgan cert.pem va key.pem.
Sinab ko‘ramiz:
HTTP/1.1 301 Moved Permanently
Content-Type: text/html; charset=utf-8
Location: https://localhost:8443/books?page=2
...
Yo‘l va query parametrlar saqlandi.
Soxta Host sarlavhasi e’tiborsiz qoldirildi.
HTTP/2 200
strict-transport-security: max-age=63072000; includeSubDomains
...
{"data":["O‘tkan kunlar","Sarob"]}
HTTPS javobida HSTS sarlavhasi bor. HTTP/2 da sarlavha nomlari kichik harflarda yoziladi.
curl -L bayrog‘i bilan curl yo‘naltirishga o‘zi amal qiladi:
Proxy ortida
TLS nginx, Caddy yoki load balancer’da tugasa, Go ilovasiga barcha so‘rovlar oddiy HTTP bo‘lib keladi. Bu holatda r.TLS har doim nil bo‘ladi. Klient qaysi protokol bilan kelganini proxy qo‘shadigan X-Forwarded-Proto sarlavhasidan bilish mumkin:
func requireHTTPS(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-Forwarded-Proto") == "http" {
target := url.URL{Scheme: "https", Host: httpsHost, Path: r.URL.Path, RawQuery: r.URL.RawQuery}
http.Redirect(w, r, target.String(), http.StatusMovedPermanently)
return
}
next.ServeHTTP(w, r)
})
}
Warning
X-Forwarded-Proto sarlavhasiga faqat so‘rov o‘z proxy’ingizdan kelganda ishonish mumkin. Go ilovasi internetdan to‘g‘ridan-to‘g‘ri ochiq bo‘lsa, istalgan klient bu sarlavhani o‘zi qo‘sha oladi. Shuning uchun ilova porti faqat proxy’ga ochiq bo‘lishi kerak: localhost yoki ichki tarmoq.
Ko‘pincha HTTP→HTTPS yo‘naltirishni proxy’ning o‘zi bajaradi. Masalan, Caddy buni standart holatda qiladi. Bu holatda ilova faqat HSTS sarlavhasini qo‘shadi yoki uni ham proxy zimmasiga qoldiradi.
Xulosa
- 80-portda faqat yo‘naltirish serveri ishlaydi. U
GETvaHEADso‘rovlarini HTTPS’ga yo‘naltiradi. - Manzil domeni
r.Hostdan emas, sozlamadan olinadi. Aks holda ochiq yo‘naltirish zaifligi paydo bo‘ladi. - Tanali so‘rovlar yo‘naltirilmaydi, ular xato bilan rad etiladi.
- HSTS brauzerni domenga faqat HTTPS orqali ulanishga majbur qiladi va birinchi so‘rovdagi zaiflikni yopadi.
- HSTS’ni kichik
max-agebilan boshlab, bosqichma-bosqich kuchaytiring.preloadni oxirgi bosqichda qo‘shing. - Proxy ortida protokol
X-Forwarded-Protodan olinadi, lekin faqat ishonchli proxy’dan kelganda.
Shu bilan HTTPS bo‘limi tugadi. Keyingi bo‘limda API’ga qarshi keng tarqalgan hujumlar va ulardan himoyalanish usullarini ko‘rib chiqamiz.