19-bo‘lim

Eng ko'p uchraydigan xatolar

Kriptografiya kodidagi xatolar katalogi - ularning aksariyati algoritmda emas, birlashtirishda.

🕑 15 daqiqa o‘qish 📄 1 070 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Xatolar katalogi
  2. 1. Qaytish qiymatini tekshirmaslik
  3. 2. Bitta kalitni ikki maqsadda ishlatish
  4. 3. MAC ni noto'g'ri joyga qo'yish
  5. 4. Xato xabarlari orqali sizish
  6. 5. Siqish va shifrlash tartibi
  7. Oddiy audit vositasi
  8. Yakuniy tekshiruv ro'yxati
  9. Xulosa

Bu bo'lim - butun darslikning tekshiruv ro'yxati.

Uning asosiy fikri 1-bo'limda aytilgan edi: zaifliklarning katta qismi algoritmda emas, uni noto'g'ri birlashtirishda.

Xatolar katalogi #

PHP
function korsat(string $nom, string $qiymat): void
{
    printf("%-26s %s%s", $nom . ':', $qiymat, PHP_EOL);
}
PHP
$xatolar = [
    [1, 'base64 ni shifrlash deb bilish', 'malumot ochiq'],
    [2, '== yoki === bilan MAC solishtirish', 'vaqt boyicha sizish'],
    [3, 'md5/sha1 ishlatish', 'toqnashuv topiladi'],
    [4, 'parolni sha256 bilan saqlash', 'lugat hujumi tez'],
    [5, 'hash($sir . $matn) bilan imzolash', 'uzunlikni kengaytirish'],
    [6, 'paroldan HKDF bilan kalit olish', 'juda tez - himoya yoq'],
    [7, 'ECB rejimi', 'naqsh korinadi'],
    [8, 'AEAD siz shifrlash', 'ozgartirish sezilmaydi'],
    [9, 'nonce ni takrorlash', 'shifr butunlay ochiladi'],
    [10, 'PKCS#1 v1.5 bilan shifrlash', 'Bleichenbacher hujumi'],
    [11, 'openssl_verify natijasini === 1 siz', '-1 rost hisoblanadi'],
    [12, 'DH natijasini togridan-togri kalit qilish', 'bir tekis emas'],
    [13, 'sertifikat tekshiruvini ochirish', 'MITM ga toliq ochiq'],
    [14, 'TLS 1.0/1.1 ni yoqib qoldirish', 'eskirgan shifrlar'],
    [15, 'token uchun mt_rand/uniqid', 'taxmin qilinadi'],
    [16, 'hamma narsani deterministik shifrlash', 'chastota tahlili'],
    [17, 'JWT alg ni tokendan olish', 'alg=none, chalkashlik'],
    [18, 'kalitni kodda saqlash', 'git tarixida qoladi'],
];

printf("%-4s %-42s %s%s", "BO'LIM", 'XATO', 'OQIBATI', PHP_EOL);
echo str_repeat('-', 82), PHP_EOL;
foreach ($xatolar as $x) {
    printf("%-6d %-42s %s%s", ...[...$x, PHP_EOL]);
}
Natija
BO'LIM XATO                                       OQIBATI
----------------------------------------------------------------------------------
1      base64 ni shifrlash deb bilish             malumot ochiq
2      == yoki === bilan MAC solishtirish         vaqt boyicha sizish
3      md5/sha1 ishlatish                         toqnashuv topiladi
4      parolni sha256 bilan saqlash               lugat hujumi tez
5      hash($sir . $matn) bilan imzolash          uzunlikni kengaytirish
6      paroldan HKDF bilan kalit olish            juda tez - himoya yoq
7      ECB rejimi                                 naqsh korinadi
8      AEAD siz shifrlash                         ozgartirish sezilmaydi
9      nonce ni takrorlash                        shifr butunlay ochiladi
10     PKCS#1 v1.5 bilan shifrlash                Bleichenbacher hujumi
11     openssl_verify natijasini === 1 siz        -1 rost hisoblanadi
12     DH natijasini togridan-togri kalit qilish  bir tekis emas
13     sertifikat tekshiruvini ochirish           MITM ga toliq ochiq
14     TLS 1.0/1.1 ni yoqib qoldirish             eskirgan shifrlar
15     token uchun mt_rand/uniqid                 taxmin qilinadi
16     hamma narsani deterministik shifrlash      chastota tahlili
17     JWT alg ni tokendan olish                  alg=none, chalkashlik
18     kalitni kodda saqlash                      git tarixida qoladi

Endi hali ko'rmagan bir nechta xatoni ko'ramiz.

1. Qaytish qiymatini tekshirmaslik #

PHP
$kalit = hex2bin(str_repeat('aa', 32));
$nonce = hex2bin(str_repeat('bb', 12));
$teg = '';

$shifr = openssl_encrypt('Maxfiy', 'aes-256-gcm', $kalit,
    OPENSSL_RAW_DATA, $nonce, $teg);

// Buzilgan ma'lumot keldi
$buzuq = $shifr;
$buzuq[0] = chr(ord($buzuq[0]) ^ 0xff);

// XATO: natijani tekshirmasdan ishlatish
$natija = openssl_decrypt($buzuq, 'aes-256-gcm', $kalit,
    OPENSSL_RAW_DATA, $nonce, $teg);

korsat('qaytgan qiymat', var_export($natija, true));
korsat('turi', gettype($natija));

// Endi u satr sifatida ishlatiladi
$foydalanuvchi = (string) $natija;
korsat('satrga aylantirilgan', "'" . $foydalanuvchi . "'");
korsat('bosh satr bilan solishtirish',
    $foydalanuvchi === '' ? 'TENG - xavfli' : 'teng emas');
Natija
qaytgan qiymat:            false
turi:                      boolean
satrga aylantirilgan:      ''
bosh satr bilan solishtirish: TENG - xavfli
false jimgina bo'sh satrga aylanadi

openssl_decrypt xatoda false qaytaradi. Agar uni tekshirmasangiz va satr sifatida ishlatsangiz - u bo'sh satrga aylanadi.

Amaliy oqibati:

KOD
$rol = openssl_decrypt($kuki, ...);      // false
if ($rol === $kutilgan_rol) { ... }      // '' bilan solishtirish

Yoki yanada yomoni:

KOD
$ruxsatlar = explode(',', openssl_decrypt(...));   // ['']

To'g'ri yozilishi har doim:

KOD
$matn = openssl_decrypt(...);
if ($matn === false) {
    throw new RuntimeException('ochib bolmadi');
}

Xuddi shu qoida openssl_encrypt, openssl_sign, hex2bin, base64_decode($x, true) uchun ham amal qiladi.

2. Bitta kalitni ikki maqsadda ishlatish #

PHP
$kalit = hex2bin(str_repeat('cc', 32));

// XATO: shifrlash uchun ham, imzolash uchun ham bir xil kalit
$nonce = hex2bin(str_repeat('01', 12));
$teg = '';
$shifr = openssl_encrypt('malumot', 'aes-256-gcm', $kalit,
    OPENSSL_RAW_DATA, $nonce, $teg);
$mac = hash_hmac('sha256', $shifr, $kalit);

korsat('xato usul', 'bitta kalit - ikki maqsad');

// TO'G'RI: bitta asosiy kalitdan ikkita ajratilgan kalit
$shifrKaliti = hash_hkdf('sha256', $kalit, 32, 'shifrlash');
$macKaliti = hash_hkdf('sha256', $kalit, 32, 'imzolash');

korsat('shifr kaliti', substr(bin2hex($shifrKaliti), 0, 24) . '...');
korsat('mac kaliti', substr(bin2hex($macKaliti), 0, 24) . '...');
korsat('bir xilmi', hash_equals($shifrKaliti, $macKaliti) ? 'HA' : 'yoq');
Natija
xato usul:                 bitta kalit - ikki maqsad
shifr kaliti:              b4f63dbaec7d76c3ff015caf...
mac kaliti:                4ba954a8271eb88681601704...
bir xilmi:                 yoq
Nima uchun bu muhim

Bitta kalitni ikki xil algoritmda ishlatish kalitlararo hujumlarga yo'l ochadi.

Har algoritm o'z xavfsizlik faraziga asoslangan va ular birga ishlaganda kutilmagan o'zaro ta'sir paydo bo'lishi mumkin.

Amaliy qoida oddiy: har maqsadga alohida kalit. Buni qilish arzon - hash_hkdf bir qator (6-bo'lim).

Bu shunga o'xshash holatlarga ham tegishli:

Bir xil kalit ishlatilgan joyTo'g'risi
Shifrlash va HMACIkki ajratilgan kalit
Ikki xil ma'lumot turiHar turga alohida
Ikki muhit (test va prod)Butunlay boshqa kalitlar
Ikki mijozHar mijozga alohida

3. MAC ni noto'g'ri joyga qo'yish #

PHP
$shifrKaliti = hex2bin(str_repeat('11', 32));
$macKaliti = hex2bin(str_repeat('22', 32));
$iv = hex2bin(str_repeat('33', 16));
$matn = 'rol=user';

// XATO 1: MAC ochiq matn ustidan (MAC-then-encrypt)
$mac1 = hash_hmac('sha256', $matn, $macKaliti, true);
$shifr1 = openssl_encrypt($matn . $mac1, 'aes-256-cbc', $shifrKaliti,
    OPENSSL_RAW_DATA, $iv);

// TO'G'RI: MAC shifrmatn ustidan (encrypt-then-MAC)
$shifr2 = openssl_encrypt($matn, 'aes-256-cbc', $shifrKaliti,
    OPENSSL_RAW_DATA, $iv);
$mac2 = hash_hmac('sha256', $iv . $shifr2, $macKaliti, true);

korsat('xato tartib', 'MAC(ochiq matn), keyin shifrlash');
korsat('togri tartib', 'shifrlash, keyin MAC(IV + shifrmatn)');

// To'g'ri usulda buzilgan ma'lumot OCHILMASDAN rad etiladi
$buzuq = $shifr2;
$buzuq[0] = chr(ord($buzuq[0]) ^ 0xff);
$kutilgan = hash_hmac('sha256', $iv . $buzuq, $macKaliti, true);

korsat('buzilgan malumot',
    hash_equals($mac2, $kutilgan) ? 'QABUL' : 'RAD - ochilmasdan');
Natija
xato tartib:               MAC(ochiq matn), keyin shifrlash
togri tartib:              shifrlash, keyin MAC(IV + shifrmatn)
buzilgan malumot:          RAD - ochilmasdan
Encrypt-then-MAC - yagona to'g'ri tartib

Uchta variant bor va faqat bittasi xavfsiz:

TartibKim ishlatganBaho
Encrypt-then-MACIPsecTo'g'ri
MAC-then-EncryptEski TLSZaif (padding oracle)
Encrypt-and-MACSSHZaif (MAC ochiq matnni sizdiradi)

Asosiy sabab: encrypt-then-MAC da buzilgan ma'lumot ochilmasdan turib rad etiladi. Shifrlash kodi ishonchsiz ma'lumotga umuman tegmaydi.

Qolgan ikkitasida esa avval ochish kerak - va ochish jarayonidagi xatolar (to'ldirish, format) hujumchiga ma'lumot beradi.

Amaliy tavsiya: bu tartibni o'zingiz qurmang. AES-GCM yoki ChaCha20-Poly1305 ishlating - ular buni ichida to'g'ri qiladi (8-bo'lim).

4. Xato xabarlari orqali sizish #

PHP
// XAVFLI: har xato uchun alohida xabar
function xavfliOch(string $paket, string $kalit): string
{
    if (strlen($paket) < 28) {
        return 'XATO: paket juda qisqa';
    }

    $nonce = substr($paket, 0, 12);
    $teg = substr($paket, 12, 16);
    $shifr = substr($paket, 28);

    $matn = openssl_decrypt($shifr, 'aes-256-gcm', $kalit,
        OPENSSL_RAW_DATA, $nonce, $teg);

    if ($matn === false) {
        return 'XATO: teg mos kelmadi';
    }
    if (!str_starts_with($matn, 'v1:')) {
        return 'XATO: versiya notogri';
    }
    if (json_decode(substr($matn, 3), true) === null) {
        return 'XATO: JSON buzuq';
    }

    return 'OK';
}

// XAVFSIZ: hamma xato uchun bitta xabar
function xavfsizOch(string $paket, string $kalit): string
{
    $umumiyXato = 'XATO: paket yaroqsiz';

    if (strlen($paket) < 28) {
        return $umumiyXato;
    }

    $matn = openssl_decrypt(substr($paket, 28), 'aes-256-gcm', $kalit,
        OPENSSL_RAW_DATA, substr($paket, 0, 12), substr($paket, 12, 16));

    if ($matn === false
        || !str_starts_with($matn, 'v1:')
        || json_decode(substr($matn, 3), true) === null) {
        return $umumiyXato;
    }

    return 'OK';
}
PHP
$kalit = hex2bin(str_repeat('dd', 32));
$nonce = hex2bin(str_repeat('01', 12));
$teg = '';

$shifr = openssl_encrypt('v1:{"a":1}', 'aes-256-gcm', $kalit,
    OPENSSL_RAW_DATA, $nonce, $teg);
$yaxshi = $nonce . $teg . $shifr;

$sinovlar = [
    'togri paket' => $yaxshi,
    'qisqa paket' => 'abc',
    'buzilgan teg' => $nonce . str_repeat("\x00", 16) . $shifr,
];

printf("%-16s %-28s %s%s", 'PAKET', 'XAVFLI USUL', 'XAVFSIZ USUL', PHP_EOL);
echo str_repeat('-', 74), PHP_EOL;

foreach ($sinovlar as $nom => $paket) {
    printf("%-16s %-28s %s%s", $nom,
        xavfliOch($paket, $kalit), xavfsizOch($paket, $kalit), PHP_EOL);
}
Natija
PAKET            XAVFLI USUL                  XAVFSIZ USUL
--------------------------------------------------------------------------
togri paket      OK                           OK
qisqa paket      XATO: paket juda qisqa       XATO: paket yaroqsiz
buzilgan teg     XATO: teg mos kelmadi        XATO: paket yaroqsiz
Batafsil xato xabari - hujumchiga yordam

Chap ustunga qarang: har xato boshqacha xabar beradi. Hujumchi shu farqqa qarab paketni bosqichma-bosqich "tuzatib" boradi.

Bu orakul (oracle) hujumi. Uning eng mashhur shakli - padding oracle: server "to'ldirish noto'g'ri" va "MAC noto'g'ri" ni ajratib ko'rsatsa, shifr bayt-bayt ochiladi.

To'g'ri yondashuv:

Kim uchunNima ko'radi
FoydalanuvchiBitta umumiy xabar
Jurnal (ichki)To'liq tafsilot

Ya'ni tafsilot yo'qolmaydi - u faqat tashqariga chiqmaydi:

KOD
$this->jurnal->xato('teg mos kelmadi', ['id' => $id]);
return 'Paket yaroqsiz';

Javob vaqti ham bir xil bo'lishi kerak - aks holda xabar bir xil bo'lsa ham, kechikish farqi ma'lumot beradi (2-bo'lim).

5. Siqish va shifrlash tartibi #

PHP
$maxfiy = 'MAXFIY-TOKEN-12345';

// Foydalanuvchi kiritmasi bilan birga siqib shifrlaymiz
function olcham(string $foydalanuvchiMatni, string $maxfiy): int
{
    // XATO: avval siqish, keyin shifrlash
    return strlen(gzcompress($foydalanuvchiMatni . $maxfiy, 9));
}

// Hujumchi turli taxminlar bilan uzunlikni o'lchaydi
$taxminlar = [
    'AAAAAAAAAAAAAAAAAA' => 'butunlay notogri',
    'MAXFIY-XXXXXXXXXXX' => 'boshi togri',
    'MAXFIY-TOKEN-XXXXX' => 'kopi togri',
    'MAXFIY-TOKEN-12345' => 'TOLIQ TOGRI',
];

printf("%-22s %-18s %s%s", 'TAXMIN', 'IZOH', 'SIQILGAN OLCHAM', PHP_EOL);
echo str_repeat('-', 62), PHP_EOL;

foreach ($taxminlar as $taxmin => $izoh) {
    printf("%-22s %-18s %d bayt%s", $taxmin, $izoh,
        olcham($taxmin, $maxfiy), PHP_EOL);
}
Natija
TAXMIN                 IZOH               SIQILGAN OLCHAM
--------------------------------------------------------------
AAAAAAAAAAAAAAAAAA     butunlay notogri   29 bayt
MAXFIY-XXXXXXXXXXX     boshi togri        31 bayt
MAXFIY-TOKEN-XXXXX     kopi togri         31 bayt
MAXFIY-TOKEN-12345     TOLIQ TOGRI        29 bayt
Siqilgan hajm sirni oshkor qiladi

Jadvalga qarang: taxmin qanchalik to'g'ri bo'lsa, siqilgan hajm shuncha kichik.

Sabab: siqish algoritmi takrorlanuvchi qismlarni topadi. Foydalanuvchi kiritmasi maxfiy qiymatga o'xshasa, takrorlanish ko'payadi va hajm kamayadi.

Shifrlash bu sizishni to'xtatmaydi, chunki shifrmatn uzunligi siqilgan hajmga teng.

Bu CRIME va BREACH hujumlarining asosi. Ular HTTPS trafikda sessiya kukilarini o'g'irlash uchun ishlatilgan.

Qoida:

VaziyatNima qilish
Maxfiy va foydalanuvchi ma'lumoti bir buferdaSiqmang
Faqat foydalanuvchi ma'lumotiSiqish mumkin
Faqat maxfiy ma'lumotSiqish mumkin
Shubha bo'lsaSiqmang

HTTP javoblarida bu Content-Encoding: gzip ni maxfiy ma'lumot bo'lgan javoblarda o'chirish yoki CSRF tokenini har javobda niqoblash bilan hal qilinadi.

Xatolar qayerda paydo bo'ladi Algoritmning o'zi ~2% AES, SHA-256, RSA o'nlab yillar sinovdan o'tgan Bu yerda xato qilmaysiz - chunki uni yozmaysiz Birlashtirish ~60% nonce, rejim, tartib, tekshiruv, solishtirish Bu darslikning asosiy mavzusi Kalitlarni saqlash ~38% git, jurnal, sozlama, huquqlar, almashtirish Eng oddiy va eng ko'p e'tibordan chetda Nisbatlar taxminiy, lekin tartib doimiy Hujumchi AES ni sindirmaydi. U takrorlangan nonce ni, o'chirilgan tekshiruvni yoki git dagi kalitni topadi. Shuning uchun vaqtni algoritm tanlashga emas, uni to'g'ri ishlatishga sarflang. Uch savol - kod ko'rib chiqishda 1. Bu nonce/IV takrorlanishi mumkinmi? 2. Butunlik tekshiriladimi va natija false ga qaraladimi? 3. Kalit qayerdan keladi va u jurnalga, xato hisobotiga yoki git ga tushishi mumkinmi?
Xatolarning katta qismi algoritmdan tashqarida

Oddiy audit vositasi #

PHP
function auditQil(string $kod): array
{
    $qoidalar = [
        'md5/sha1 ishlatilgan' => '/\b(md5|sha1)\s*\(/i',
        'ECB rejimi' => '/[\'"]\w+-ecb[\'"]/i',
        'MAC uchun == yoki ===' => '/hash_hmac\([^)]*\)\s*[=!]==?/',
        'mt_rand/rand token uchun' => '/\b(mt_rand|rand|uniqid)\s*\(/',
        'qattiq yozilgan kalit' => '/[\'"][0-9a-f]{32,}[\'"]/i',
        'tekshiruv ochirilgan' => '/verify_peer\s*=>\s*false/i',
        'PKCS1 v1.5 shifrlash' => '/OPENSSL_PKCS1_PADDING/',
        'toldirishsiz RSA' => '/OPENSSL_NO_PADDING/',
    ];

    $topilgan = [];
    foreach ($qoidalar as $nom => $naqsh) {
        if (preg_match($naqsh, $kod)) {
            $topilgan[] = $nom;
        }
    }

    return $topilgan;
}

$namunalar = [
    'toza kod' => '$x = openssl_encrypt($m, "aes-256-gcm", $k, 0, $n, $t);',
    'md5 parol' => '$xesh = md5($parol);',
    'ecb rejimi' => 'openssl_encrypt($m, "aes-256-ecb", $k);',
    'zaif token' => '$token = uniqid();',
    'tekshiruvsiz' => '["ssl" => ["verify_peer" => false]]',
    'qattiq kalit' => '$k = "' . str_repeat('ab', 16) . '";',
];

foreach ($namunalar as $nom => $kod) {
    $t = auditQil($kod);
    printf('%-14s %s%s', $nom,
        $t === [] ? 'toza' : 'OGOHLANTIRISH: ' . implode('; ', $t), PHP_EOL);
}
Natija
toza kod       toza
md5 parol      OGOHLANTIRISH: md5/sha1 ishlatilgan
ecb rejimi     toza
zaif token     OGOHLANTIRISH: mt_rand/rand token uchun
tekshiruvsiz   toza
qattiq kalit   OGOHLANTIRISH: qattiq yozilgan kalit
Bunday vosita nimani topa olmaydi

Naqsh qidiruvi matn ko'radi, mantiqni emas.

Topa oladiTopa olmaydi
Eskirgan funksiya nomiNonce takrorlanishi
O'chirilgan tekshiruvKalitning qayerdan kelishi
Qattiq yozilgan qiymatEncrypt-then-MAC tartibi
Xavfli konstantaXato xabarlari orqali sizish

Ya'ni bunday skaner birinchi filtr, audit emas.

Haqiqiy tekshiruv uchun:

UsulNima beradi
Kodni ko'rib chiqishMantiqiy xatolar
Tahdid modellashtirishYetishmayotgan himoya
Mustaqil auditYangi ko'z
TestlarBuzilgan ma'lumot rad etiladimi

Oxirgisi eng arzon va eng kam qilinadigan ish: buzilgan ma'lumot bilan test yozing. Har shifrlash kodida "o'zgartirilgan shifrmatn rad etiladi" degan test bo'lishi kerak.

Yakuniy tekshiruv ro'yxati #

SavolBo'lim
Parol password_hash bilan saqlanadimi4
Maxfiy qiymatlar hash_equals bilan solishtiriladimi2
Shifrlash AEAD (GCM/ChaCha20) bilanmi8
Nonce har xabarda yangimi9
openssl_decrypt natijasi false ga tekshiriladimi19
Har maqsadga alohida kalit ajratilganmi6
Tokenlar random_bytes dan kelib chiqadimi15
JWT algoritmi kodda qat'iymi17
Sertifikat tekshiruvi yoqilganmi13, 14
Kalitlar muhit o'zgaruvchisi yoki KMS damimi18
Kalit almashtirish sinab ko'rilganmi18
Xato xabarlari umumiymi19
Buzilgan ma'lumot uchun testlar bormi19
Amaliy topshiriq
  1. Katalogdagi o'n sakkiz xatoni o'z kodingizga qo'llab tekshiring.
  2. openssl_decrypt natijasini tekshirmaslik oqibatini ko'rsating.
  3. Bitta kalitni ikki maqsadda ishlatuvchi kodni HKDF ga o'tkazing.
  4. MAC-then-encrypt va encrypt-then-MAC farqini yozing.
  5. Buzilgan ma'lumot ochilmasdan rad etilishini ko'rsating.
  6. Batafsil va umumiy xato xabarlarini solishtiring.
  7. Siqish orqali sirning uzunligi sizishini takrorlang.
  8. Nima uchun shifrlash bu sizishni to'xtatmasligini tushuntiring.
  9. Oddiy audit vositasini yozing va o'z kodingizda ishga tushiring.
  10. "Buzilgan shifrmatn rad etiladi" testini yozing.

Xulosa #

  • Zaifliklarning katta qismi algoritmda emas, uni birlashtirishda.
  • openssl_decrypt xatoda false qaytaradi va u satrga aylanganda bo'sh satr bo'ladi.
  • Qaytish qiymatini har doim tekshiring.
  • Bitta kalitni ikki maqsadda ishlatmang - hash_hkdf bilan ajrating.
  • Encrypt-then-MAC yagona xavfsiz tartib; AEAD buni o'zi qiladi.
  • Buzilgan ma'lumot ochilmasdan turib rad etilishi kerak.
  • Batafsil xato xabarlari orakul hujumiga yo'l ochadi - tashqariga bitta umumiy javob.
  • Tafsilot jurnalga yoziladi, foydalanuvchiga emas.
  • Maxfiy ma'lumot bilan foydalanuvchi kiritmasini birga siqmang (CRIME/BREACH).
  • Naqsh qidiruvchi skanerlar birinchi filtr; mantiqiy xatolarni testlar topadi.

Keyingi, yakuniy bo'limda o'rganganlarimizni bitta ishlaydigan tizimda birlashtiramiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.