Tarkibga o'tish

Go’da heshlash va Base64 bilan ishlash

Heshlash (hashing) — ixtiyoriy uzunlikdagi ma’lumotdan belgilangan uzunlikdagi xesh qiymat, ya’ni digest hosil qilish jarayoni. Fayl yuklashda uning buzilmaganini tekshirish, cache kaliti yaratish va xabarni autentifikatsiya qilish kabi vazifalarda xesh funksiyalari ishlatiladi.

Xesh, kodlash va shifrlash bir xil tushuncha emas. Ularni aralashtirish xavfsizlik xatolariga olib keladi:

  • xesh — odatda bir tomonlama o‘zgartirish;
  • Base64 — qayta ochiladigan matnli kodlash;
  • shifrlash (encryption) — kalit yordamida qayta ochiladigan himoya;
  • HMAC — maxfiy kalit bilan xabarning yaxlitligi va haqiqiyligini tekshirish.

Xesh funksiyasining asosiy xususiyatlari

Yaxshi kriptografik xesh funksiyasi quyidagi xususiyatlarga ega:

  • bir xil kirish doim bir xil digest beradi;
  • kirishdagi kichik o‘zgarish digestni keskin o‘zgartiradi;
  • digestdan dastlabki ma’lumotni tiklash amalda qiyin;
  • turli kirishlar uchun bir xil digest topish, ya’ni kolliziya yaratish qiyin bo‘lishi kerak.

Digest uzunligi kirish hajmiga bog‘liq emas. Bo‘sh matn ham, katta fayl ham SHA-256 bilan 256 bit — 32 bayt natija beradi.

Warning

Xesh qiymati ma’lumotni yashirmaydi. Kirish qiymatlari taxmin qilinadigan bo‘lsa, hujumchi ularni xeshlab solishtirishi mumkin. Shu sababli oddiy SHA-256 parollarni saqlash uchun yaramaydi.

SHA-256 bilan birinchi misol

crypto/sha256 paketi SHA-256 hisoblaydi. Natija binary baytlar bo‘lgani uchun uni ko‘rsatishda ko‘pincha hexadecimal kodlash ishlatiladi.

package main

import (
    "crypto/sha256"
    "encoding/hex"
    "fmt"
)

func main() {
    data := []byte("salom dunyo")
    sum := sha256.Sum256(data)

    fmt.Println("Baytlar soni:", len(sum))
    fmt.Println("SHA-256:", hex.EncodeToString(sum[:]))
}

Natija:

Baytlar soni: 32
SHA-256: 3d241073bae411a45a85ce25b3415ae1f1b32915a80c7fd9e1b07546526f665c

sha256.Sum256() []byte qabul qiladi va [32]byte massiv qaytaradi. sum[:] massivdan slice ko‘rinishini yaratadi. hex.EncodeToString() har bir baytni ikkita hexadecimal belgi bilan yozgani uchun natija 64 belgidan iborat.

Bir xil matn qayta xeshlansa, natija o‘zgarmaydi. Hatto bitta harf yoki bo‘sh joy o‘zgarsa, boshqa digest chiqadi. Xesh matnning kodlangan baytlariga hisoblanadi; Salom va salom, shuningdek, Unicode yozilishining turli shakllari bir xil ma’no bersa ham turli baytlar bo‘lishi mumkin.

fmt hexadecimal ko‘rinishni bevosita ham chiqara oladi:

sum := sha256.Sum256([]byte("go-lang.uz"))
fmt.Printf("%x\n", sum)

Bu qisqa parcha encoding/hex importisiz digestni ko‘rsatadi. %x baytlarni kichik harfli hexadecimal formatda yozadi.

Katta ma’lumotni oqim sifatida heshlash

Katta faylni to‘liq RAMga yuklash shart emas. sha256.New() hash.Hash interfeysini qaytaradi va ma’lumotni bo‘laklab qabul qiladi.

package main

import (
    "crypto/sha256"
    "fmt"
    "io"
    "log"
    "strings"
)

func main() {
    source := strings.NewReader("katta fayldan kelayotgan ma’lumot")
    hasher := sha256.New()

    if _, err := io.Copy(hasher, source); err != nil {
        log.Fatal(err)
    }

    fmt.Printf("%x\n", hasher.Sum(nil))
}

Natija 64 belgili SHA-256 digest bo‘ladi:

e66486a5eee0875c1004804c143af03364fb6ca1c550fdffc1bf3f4953f00a1d

io.Copy() ma’lumotni sourcedan bo‘laklab hasherga yozadi. hasher.Sum(nil) shu paytgacha yozilgan barcha baytlar digestini qaytaradi. Real faylda strings.NewReader() o‘rniga os.Open()dan olingan fayl ishlatiladi. Oqimli usul xotira sarfini fayl hajmiga bog‘lab qo‘ymaydi.

hash.Hashga Write() chaqirish digestni darhol qaytarmaydi; ichki holatni yangilaydi. Sum(nil) hisoblangan natijani oladi, lekin hasher holatini reset qilmaydi. Yangi mustaqil hisob uchun Reset() yoki yangi hasher kerak.

MD5 va SHA-1 nega xavfsiz emas?

MD5 128 bit, SHA-1 esa 160 bit digest yaratadi. Ikkalasida ham amaliy kolliziya hujumlari mavjud. Hujumchi turli ikki faylga bir xil digest yasashi mumkin bo‘lgan xavfsizlik kontekstida bu algoritmlarni ishlatib bo‘lmaydi.

MD5 faqat legacy protokol bilan moslik yoki xavfsizlikka aloqasiz eski checksum formati talab qilinganda uchrashi mumkin:

package main

import (
    "crypto/md5"
    "fmt"
)

func main() {
    sum := md5.Sum([]byte("salom dunyo"))
    fmt.Printf("%x\n", sum)
}

Natija:

8f4b0e175285a09d3bff44f0fdec2e0f

Natija 16 bayt, hexadecimal ko‘rinishda 32 belgi. Kod texnik jihatdan ishlaydi, ammo MD5’ni parol, raqamli imzo, sertifikat yoki hujumchi o‘zgartira oladigan faylning xavfsizligini tekshirish uchun ishlatmang.

Oddiy xesh autentifikatsiya bermaydi

SHA-256 tasodifiy buzilishni aniqlashi mumkin. Ammo hujumchi xabarni ham, yonidagi digestni ham almashtira olsa, yangi digestni o‘zi hisoblaydi. Xabarning ishonchli manbadan kelganini tekshirish uchun HMAC kerak.

HMAC xesh funksiyasi va faqat ikki tomon biladigan maxfiy kalitdan foydalanadi:

package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
)

func sign(message, key []byte) []byte {
    mac := hmac.New(sha256.New, key)
    mac.Write(message)
    return mac.Sum(nil)
}

func main() {
    message := []byte("order_id=42&amount=150000")
    key := []byte("serverdagi-maxfiy-kalit")

    signature := sign(message, key)
    fmt.Println("Imzo:", hex.EncodeToString(signature))
    fmt.Println("Mos:", hmac.Equal(signature, sign(message, key)))
    fmt.Println("O‘zgargan xabar:", hmac.Equal(signature, sign([]byte("order_id=42&amount=1"), key)))
}

Natija:

Imzo: 12cc899b0604da2c317e5a83dabe7ea5d0daff236a6c33b3714cd33993ac5ea0
Mos: true
O‘zgargan xabar: false

hmac.New() SHA-256 asosidagi HMAC yaratadi. Xabar yoki kalit o‘zgarsa, imzo mos kelmaydi. hmac.Equal() qiymatlarni vaqt ma’lumotini sizdirmaslikka mos usulda solishtiradi. Xavfsizlik tekshiruvida oddiy bytes.Equal() yoki hexadecimal satrlar uchun == ishlatishdan ko‘ra hmac.Equal()ni tanlang.

Maxfiy kalitni kodga qattiq yozish real loyiha uchun to‘g‘ri emas. Misoldagi qiymat faqat ishlash prinsipini ko‘rsatadi. Production’da kalit secret manager yoki himoyalangan konfiguratsiyadan olinadi.

Base64 xesh emas

Base64 binary ma’lumotni matnda xavfsiz tashish uchun kodlaydi. U maxfiylik bermaydi va kalit talab qilmaydi. Kodlangan qiymatni istalgan kishi qayta dekodlay oladi.

package main

import (
    "encoding/base64"
    "fmt"
)

func main() {
    data := []byte("salom dunyo")
    encoded := base64.StdEncoding.EncodeToString(data)

    decoded, err := base64.StdEncoding.DecodeString(encoded)
    if err != nil {
        fmt.Println("Decode xatosi:", err)
        return
    }

    fmt.Println("Kodlangan:", encoded)
    fmt.Println("Tiklangan:", string(decoded))
}

Natija:

Kodlangan: c2Fsb20gZHVueW8=
Tiklangan: salom dunyo

Base64 har 3 baytni 4 ta matn belgisiga aylantiradi. Shu sababli natija odatda asl ma’lumotdan qariyb uchdan birga katta bo‘ladi. Oxirdagi = to‘ldiruvchi belgi (padding) hisoblanadi.

Standard va URL-safe Base64

base64.StdEncoding alifbosida + va / bor. URL, cookie yoki fayl nomida ko‘pincha base64.URLEncoding ishlatiladi; ularning o‘rniga - va _ yozadi. Padding kerak bo‘lmasa, base64.RawURLEncoding tanlanadi.

package main

import (
    "encoding/base64"
    "fmt"
)

func main() {
    data := []byte{251, 255, 239}

    fmt.Println(base64.StdEncoding.EncodeToString(data))
    fmt.Println(base64.RawURLEncoding.EncodeToString(data))
}

Natija:

+//v
-__v

Ikkinchi variant URL uchun xavfsiz alifbodan foydalanadi va padding yozmaydi. Encode va decode paytida bir xil variantni ishlatish kerak; aks holda DecodeString() xato qaytarishi mumkin.

Parollarni qanday saqlash kerak?

SHA-256 va SHA-512 tez ishlaydi. Bu fayl yaxlitligi uchun foydali, ammo parol uchun xavfli: hujumchi soniyasiga juda ko‘p taxminni tekshirishi mumkin.

Parol uchun maxsus sekin va sozlanadigan algoritm ishlatiladi:

  • Argon2id;
  • scrypt;
  • bcrypt;
  • tashkilot talabi bo‘lsa, PBKDF2.

Ular salt va xarajat parametridan foydalanadi. Salt har bir parol uchun alohida tasodifiy qiymat bo‘lib, bir xil parollar bir xil saqlangan natija bermasligini ta’minlaydi. Salt maxfiy bo‘lishi shart emas; u xesh bilan birga saqlanadi.

Bu algoritmlarning amaliy implementatsiyalari golang.org/x/crypto modulida mavjud. Algoritm va parametr tanlovi xavfsizlik talabi, server resursi va amaldagi tavsiyalarga bog‘liq. Parolni o‘zingiz yaratgan SHA-256(password + salt) sxemasi bilan saqlamang.

Fayl checksumini tekshirish

Fayl yuklab olinganda kutilgan digest ishonchli HTTPS sahifasi yoki imzolangan manifestdan olingan bo‘lsa, SHA-256 fayl buzilmaganini tekshiradi. Faqat fayl bilan bir joydan olingan digest hujumchiga qarshi himoya bermaydi: ikkalasi ham almashtirilishi mumkin.

Digest matnini solishtirishdan oldin formatni tekshiring. SHA-256 hexadecimal qiymati 64 belgidan iborat bo‘lishi kerak. Katta-kichik harf farqini yo‘qotish uchun matnni baytga dekodlab, kutilgan 32 bayt bilan solishtirish mumkin.

Keng tarqalgan xatolar

  • Base64’ni shifrlash deb hisoblash. U qayta ochiladigan kodlashdir.
  • Parolga oddiy SHA-256 qo‘llash. Tez xesh brute-force hujumini arzonlashtiradi.
  • MD5 yoki SHA-1’ni xavfsizlik qarori uchun ishlatish. Ular kolliziyaga chidamli emas.
  • Xabar autentifikatsiyasi uchun SHA-256(key || message) kabi qo‘lbola sxema yaratish. Standart HMAC ishlating.
  • Katta faylni to‘liq os.ReadFile() bilan RAMga olish. io.Copy() va hash.Hash bilan oqimli hisoblang.
  • Xavfsizlik imzosini oddiy == bilan solishtirish. hmac.Equal()dan foydalaning.
  • Matnning ko‘rinishi bir xil bo‘lsa, baytlari ham bir xil deb o‘ylash. Kodlash va Unicode normalizatsiyasini protokol darajasida aniq belgilang.

Interviewda nimalarga e’tibor beriladi?

  • Xesh, kodlash, shifrlash va HMAC o‘rtasidagi farq.
  • Kolliziya, preimage va belgilangan digest uzunligi tushunchalari.
  • SHA-256 nega parol saqlash uchun mos emasligi.
  • sha256.Sum256() qaytaradigan [32]byte va sha256.New() qaytaradigan hash.Hash farqi.
  • Base64 hajmi va StdEncoding, URLEncoding, RawURLEncoding variantlari.
  • HMAC imzosini hmac.Equal() bilan tekshirish sababi.

Xulosa

SHA-256 ma’lumot digestini hisoblaydi, ammo o‘zi xabarning kimdan kelganini isbotlamaydi. HMAC maxfiy kalit bilan yaxlitlik va autentifikatsiyani birlashtiradi. Base64 faqat binary ma’lumotni matnga kodlaydi. MD5 va SHA-1 yangi xavfsizlik yechimlari uchun yaramaydi, parollar esa Argon2id, scrypt yoki bcrypt kabi maxsus password hashing algoritmi bilan saqlanadi.