19-bo‘lim
Ma'lumotlar bazasi xavfsizligi
SQL injection va parametrli so'rovlar, foydalanuvchi huquqlari, parol saqlash, maxfiy ma'lumot va zaxira nusxa.
Ushbu bo‘lim mundarijasi
Ma'lumotlar bazasi - ilovaning eng qimmatli qismi. Kod yo'qolsa qayta yozish mumkin, ma'lumot yo'qolsa - yo'q.
SQL injection #
CREATE TABLE foydalanuvchilar (
id INT PRIMARY KEY AUTO_INCREMENT,
ism VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
rol ENUM('foydalanuvchi', 'admin') NOT NULL DEFAULT 'foydalanuvchi'
);
INSERT INTO foydalanuvchilar (ism, email, rol) VALUES
('Husanboy', '[email protected]', 'foydalanuvchi'),
('Malika', '[email protected]', 'foydalanuvchi'),
('Admin', '[email protected]', 'admin');
-- Normal so'rov
SELECT id, ism, rol FROM foydalanuvchilar WHERE ism = 'Husanboy';
+----+----------+---------------+
| id | ism | rol |
+----+----------+---------------+
| 1 | Husanboy | foydalanuvchi |
+----+----------+---------------+
-- Hujum natijasida hosil bo'lgan so'rov
SELECT id, ism, rol FROM foydalanuvchilar WHERE ism = '' OR '1'='1';
+----+----------+---------------+
| id | ism | rol |
+----+----------+---------------+
| 1 | Husanboy | foydalanuvchi |
| 2 | Malika | foydalanuvchi |
| 3 | Admin | admin |
+----+----------+---------------+
Bitta belgi bilan hujumchi butun jadvalni oldi.
Yagona to'g'ri yechim - parametrli so'rov #
-- Tayyorlangan so'rov: SQL va MA'LUMOT ajratilgan
PREPARE qidirish FROM 'SELECT id, ism, rol FROM foydalanuvchilar WHERE ism = ?';
SET @kirish = 'Husanboy';
EXECUTE qidirish USING @kirish;
+----+----------+---------------+
| id | ism | rol |
+----+----------+---------------+
| 1 | Husanboy | foydalanuvchi |
+----+----------+---------------+
PREPARE sanash FROM
'SELECT COUNT(*) AS topildi FROM foydalanuvchilar WHERE ism = ?';
-- Hujum urinishi - endi u shunchaki MATN
SET @kirish = ''' OR ''1''=''1';
EXECUTE sanash USING @kirish;
DEALLOCATE PREPARE sanash;
+---------+
| topildi |
+---------+
| 0 |
+---------+
Hech qanday qator topilmadi - hujumchi kiritgan matn shunchaki ism sifatida qidirildi, kod sifatida emas.
Parametrli so'rovda SQL oldindan tahlil qilinadi, keyin ma'lumot qo'yiladi. Ma'lumot hech qachon kodga aylanmaydi.
PHP (PDO):
// XAVFLI
$db->query("SELECT * FROM users WHERE ism = '$ism'");
// TO'G'RI
$stmt = $db->prepare("SELECT * FROM users WHERE ism = ?");
$stmt->execute([$ism]);
Bu loyihada ham shu naqsh ishlatiladi - bootstrap.php da
PDO ATTR_EMULATE_PREPARES => false bilan sozlangan, ya'ni
so'rovlar haqiqatan serverda tayyorlanadi.
Ekranlash (escaping) yetarli emas. mysqli_real_escape_string
kabi funksiyalar kodlash sozlamalariga bog'liq va chetlab
o'tish usullari ma'lum. Parametrli so'rov esa strukturaviy
himoya beradi.
Parametr qo'yib bo'lmaydigan joylar:
SELECT * FROM ? WHERE ... -- jadval nomi - MUMKIN EMAS
ORDER BY ? -- ustun nomi - MUMKIN EMAS
Bunday joylarda oq ro'yxat ishlating:
$ruxsat = ['ism', 'sana', 'narx'];
$ustun = in_array($kirish, $ruxsat, true) ? $kirish : 'id';
$sql = "SELECT * FROM mahsulotlar ORDER BY $ustun";
Foydalanuvchi kiritgan qiymat hech qachon to'g'ridan-to'g'ri so'rovga tushmasin.
Eng kam imtiyoz #
-- Ilova foydalanuvchisi uchun huquqlar
SELECT
'CREATE USER ilova@localhost IDENTIFIED BY ...' AS qadam
UNION ALL SELECT 'GRANT SELECT, INSERT, UPDATE, DELETE ON dukon.* TO ilova@localhost'
UNION ALL SELECT '-- DROP, ALTER, CREATE BERILMAYDI'
UNION ALL SELECT '-- Migratsiya uchun ALOHIDA foydalanuvchi';
+--------------------------------------------------------------------+
| qadam |
+--------------------------------------------------------------------+
| CREATE USER ilova@localhost IDENTIFIED BY ... |
| GRANT SELECT, INSERT, UPDATE, DELETE ON dukon.* TO ilova@localhost |
| -- DROP, ALTER, CREATE BERILMAYDI |
| -- Migratsiya uchun ALOHIDA foydalanuvchi |
+--------------------------------------------------------------------+
root bilan ulanmasinKo'p loyiha root yoki to'liq huquqli foydalanuvchi bilan
ulanadi. Bu SQL injection ning zararini keskin oshiradi.
| Huquq | Injection bilan nima mumkin |
|---|---|
Faqat SELECT | Ma'lumot o'qish |
+ INSERT/UPDATE | Ma'lumot buzish |
+ DROP | Jadvallarni o'chirish |
+ FILE | Serverdagi fayllarni o'qish |
root | Butun server |
Har vazifa uchun alohida foydalanuvchi:
-- Ilova: kundalik ish
GRANT SELECT, INSERT, UPDATE, DELETE ON dukon.* TO 'ilova'@'10.0.%';
-- Migratsiya: faqat joylashtirish paytida
GRANT ALL ON dukon.* TO 'migratsiya'@'10.0.0.5';
-- Hisobot: faqat o'qish
GRANT SELECT ON dukon.* TO 'hisobot'@'10.0.%';
-- Zaxira nusxa
GRANT SELECT, LOCK TABLES, SHOW VIEW ON dukon.* TO 'zaxira'@'localhost';
Xost qismiga ham e'tibor bering: 'ilova'@'%' istalgan
joydan ulanishga ruxsat beradi. Aniq IP yoki tarmoqni
ko'rsating.
Huquqlarni ko'rish:
SHOW GRANTS FOR 'ilova'@'10.0.%';
Parol saqlash #
-- NOTO'G'RI: ochiq matn
SELECT 'parol123' AS ochiq_parol,
MD5('parol123') AS md5_xesh,
SHA1('parol123') AS sha1_xesh;
+-------------+----------------------------------+------------------------------------------+
| ochiq_parol | md5_xesh | sha1_xesh |
+-------------+----------------------------------+------------------------------------------+
| parol123 | e9aba73de1a9715fd100fe111585cebc | b4844d172402510660f33b6e12d310e69a4c6631 |
+-------------+----------------------------------+------------------------------------------+
MD5 va SHA1 parol uchun yaramaydiYuqoridagi MD5('parol123') qiymatini qidiruv tizimiga
kiritsangiz, darhol asl parolni topasiz - bunday
jadvallar internetda tayyor turibdi.
Muammo algoritm buzilganida emas, tezligida: zamonaviy
GPU sekundiga milliardlab MD5 hisoblaydi.
Parol uchun ataylab sekin algoritm kerak:
| Algoritm | Baho |
|---|---|
MD5, SHA1, SHA256 | Yaramaydi - juda tez |
bcrypt | Yaxshi, keng qo'llanadi |
scrypt | Yaxshi, xotira talab qiladi |
argon2id | Eng yaxshi - zamonaviy standart |
Ular ikki narsani beradi:
- Tuz (salt) - har parolga tasodifiy qo'shimcha, shuning uchun bir xil parollar turli xesh beradi;
- Sekinlik - minglab iteratsiya, brute-force qimmatlashadi.
PHP da:
$xesh = password_hash($parol, PASSWORD_DEFAULT); // bcrypt yoki argon2
password_verify($kiritilgan, $xesh); // tekshirish
Xeshlashni ma'lumotlar bazasida qilmang - u ilova darajasida bajarilishi kerak. Aks holda parol so'rov jurnalida va tarmoqda ochiq ko'rinadi.
Ustun turi: CHAR(60) bcrypt uchun, VARCHAR(255) universal.
Maxfiy ma'lumotni ajratish #
CREATE TABLE mijozlar (
id INT PRIMARY KEY AUTO_INCREMENT,
ism VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL,
telefon VARCHAR(20) NOT NULL,
karta_oxiri CHAR(4) NULL,
yaratilgan DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO mijozlar (ism, email, telefon, karta_oxiri, yaratilgan) VALUES
('Husanboy Qodirov', '[email protected]', '901234567', '4242', '2026-01-10 10:00:00'),
('Malika Yusupova', '[email protected]', '911112233', NULL, '2026-02-15 11:00:00');
-- Qo'llab-quvvatlash xodimi uchun ko'rinish
CREATE VIEW mijozlar_qisqa AS
SELECT
id,
ism,
CONCAT(LEFT(email, 2), '***@', SUBSTRING_INDEX(email, '@', -1)) AS email,
CONCAT('***', RIGHT(telefon, 4)) AS telefon,
yaratilgan
FROM mijozlar;
SELECT id, ism, email, telefon FROM mijozlar_qisqa ORDER BY id;
+----+------------------+--------------+---------+
| id | ism | email | telefon |
+----+------------------+--------------+---------+
| 1 | Husanboy Qodirov | hu***@edu.uz | ***4567 |
| 2 | Malika Yusupova | ma***@edu.uz | ***2233 |
+----+------------------+--------------+---------+
Xodimga butun jadvalni ko'rsatish o'rniga ko'rinish yarating va faqat unga huquq bering:
GRANT SELECT ON dukon.mijozlar_qisqa TO 'qollab_quvvatlash'@'%';
-- mijozlar jadvaliga huquq BERILMAYDI
Endi xodim to'liq email va telefonni ko'ra olmaydi.
Bu ma'lumotni minimallashtirish prinsipi: har kishi faqat o'z ishi uchun kerakli ma'lumotni ko'rsin.
Umumiy qoidalar:
| Ma'lumot | Qanday saqlash |
|---|---|
| Parol | Xesh (argon2id) |
| Karta raqami | Saqlamang - to'lov tizimiga qoldiring |
| Karta oxirgi 4 raqami | Saqlash mumkin |
| Pasport, JSHSHIR | Shifrlangan holda, kerak bo'lsagina |
| Email, telefon | Oddiy, lekin huquqlar bilan cheklangan |
| Sog'liq, moliya | Shifrlangan, audit bilan |
Umuman saqlamaslik - eng xavfsiz variant. Yo'q ma'lumot o'g'irlanmaydi.
Audit izi #
CREATE TABLE audit (
id INT PRIMARY KEY AUTO_INCREMENT,
jadval VARCHAR(64) NOT NULL,
qator_id INT NOT NULL,
amal ENUM('INSERT', 'UPDATE', 'DELETE') NOT NULL,
kim VARCHAR(100) NOT NULL,
qachon DATETIME NOT NULL,
eski_qiymat JSON NULL,
yangi_qiymat JSON NULL
);
INSERT INTO audit (jadval, qator_id, amal, kim, qachon, eski_qiymat, yangi_qiymat) VALUES
('mijozlar', 1, 'UPDATE', '[email protected]', '2026-03-01 09:15:00',
'{"telefon":"901234567"}', '{"telefon":"999999999"}'),
('mijozlar', 2, 'DELETE', '[email protected]', '2026-03-02 14:20:00',
'{"ism":"Test"}', NULL);
SELECT jadval, qator_id, amal, kim, qachon,
JSON_VALUE(eski_qiymat, '$.telefon') AS eski_telefon
FROM audit
ORDER BY id;
+----------+----------+--------+--------------+---------------------+--------------+
| jadval | qator_id | amal | kim | qachon | eski_telefon |
+----------+----------+--------+--------------+---------------------+--------------+
| mijozlar | 1 | UPDATE | [email protected] | 2026-03-01 09:15:00 | 901234567 |
| mijozlar | 2 | DELETE | [email protected] | 2026-03-02 14:20:00 | NULL |
+----------+----------+--------+--------------+---------------------+--------------+
Muhim ma'lumot ustidagi har o'zgarishni yozib borish - qoidalar talabi bo'lishi ham mumkin.
| Ustun | Nima uchun |
|---|---|
| Kim | Foydalanuvchi identifikatori |
| Qachon | Aniq vaqt |
| Nima | Jadval va qator |
| Eski qiymat | Nima o'zgargani |
| Yangi qiymat | Nimaga o'zgargani |
| IP manzil | Qayerdan |
Amalga oshirish usullari:
| Usul | Afzalligi | Kamchiligi |
|---|---|---|
| Trigger | Hech narsa o'tkazib yubormaydi | "Kim" ni bilmaydi |
| Ilova kodi | "Kim" ni biladi | To'g'ridan-to'g'ri UPDATE ni ko'rmaydi |
| Binlog tahlili | Hammasi | Murakkab |
Trigger kim ni bilmasligi asosiy muammo - u faqat
CURRENT_USER() (ma'lumotlar bazasi foydalanuvchisi) ni
ko'radi, ilova foydalanuvchisini emas.
Yechim - ilova sessiya o'zgaruvchisiga yozadi:
SET @ilova_foydalanuvchi = '[email protected]';
UPDATE mijozlar SET ...; -- trigger @ilova_foydalanuvchi ni o'qiydi
Audit jadvaliga DELETE va UPDATE huquqini bermang -
faqat INSERT. Aks holda hujumchi izini o'chira oladi.
Zaxira nusxa #
SELECT
'mysqldump --single-transaction --routines --triggers' AS buyruq,
'InnoDB uchun jadvalni qulflamaydi' AS izoh
UNION ALL SELECT '--single-transaction', 'Izchil surat oladi'
UNION ALL SELECT '--routines --triggers', 'Protsedura va triggerlarni ham oladi'
UNION ALL SELECT '--set-gtid-purged=OFF', 'Replikatsiya bilan muammo bo''lmasin';
+------------------------------------------------------+--------------------------------------+
| buyruq | izoh |
+------------------------------------------------------+--------------------------------------+
| mysqldump --single-transaction --routines --triggers | InnoDB uchun jadvalni qulflamaydi |
| --single-transaction | Izchil surat oladi |
| --routines --triggers | Protsedura va triggerlarni ham oladi |
| --set-gtid-purged=OFF | Replikatsiya bilan muammo bo'lmasin |
+------------------------------------------------------+--------------------------------------+
Eng ko'p uchraydigan halokat naqshi:
- Zaxira nusxa har kuni olinadi;
- Hech kim uni tiklab ko'rmaydi;
- Halokat kuni ma'lum bo'ladiki, fayllar bo'sh yoki buzilgan.
3-2-1 qoidasi:
| Raqam | Ma'nosi |
|---|---|
| 3 | Ma'lumotning uchta nusxasi |
| 2 | Ikki xil saqlash muhitida |
| 1 | Bittasi boshqa joyda (offsite) |
Tekshirish ro'yxati:
- Zaxira muntazam olinadimi?
- U boshqa serverda saqlanadimi?
- Shifrlanganmi? (maxfiy ma'lumot bor)
- Tiklash sinab ko'rilganmi? Qachon oxirgi marta?
- Tiklash qancha vaqt oladi? (RTO)
- Qancha ma'lumot yo'qolishi mumkin? (RPO)
Oxirgi ikkitasi biznes qarori: "1 soatlik ma'lumot yo'qolishi maqbulmi?" degan savolga javob zaxira chastotasini belgilaydi.
Nuqtaviy tiklash (point-in-time recovery) uchun binlog ham saqlanishi kerak:
SHOW VARIABLES LIKE 'log_bin';
Bu to'liq zaxira + binlog bilan istalgan lahzaga qaytish imkonini beradi.
Ulanish xavfsizligi #
| Chora | Izoh |
|---|---|
| Ma'lumotlar bazasi internetga ochiq emas | Faqat ichki tarmoq |
| TLS bilan ulanish | REQUIRE SSL |
| Kuchli parollar | Har muhit uchun boshqa |
Parollar .env da, kodda emas | Git ga tushmasin |
root uzoqdan ulanmaydi | 'root'@'localhost' |
| Anonim foydalanuvchilar o'chirilgan | mysql_secure_installation |
| Sinov bazasi o'chirilgan | DROP DATABASE test |
| Yangilanishlar o'rnatiladi | Xavfsizlik yamoqlari |
Tekshirish:
-- Kim qayerdan ulana oladi?
SELECT user, host FROM mysql.user ORDER BY user, host;
-- Parolsiz foydalanuvchi bormi?
SELECT user, host FROM mysql.user WHERE authentication_string = '';
Bu loyihaning .env fayli .gitignore da - ma'lumotlar
bazasi paroli hech qachon Git ga tushmasligi kerak.
Agar parol tasodifan commit qilingan bo'lsa: uni darhol almashtiring. Git tarixidan o'chirish yetarli emas - u allaqachon nusxalangan bo'lishi mumkin.
- SQL injection hujumini simulyatsiya qiling.
PREPAREbilan xuddi shu hujumni to'xtating.- Jadval nomini parametr qilib bo'lmasligini tushuntiring.
- Ilova uchun cheklangan huquqli foydalanuvchi yarating.
rootbilan ulanish nima uchun xavfli ekanini ayting.MD5parol xeshini qidiruv tizimida sinab ko'ring.- Parol uchun to'g'ri algoritmlarni sanang.
- Maxfiy ma'lumotni yashiruvchi ko'rinish yarating.
- Audit jadvalini loyihalang.
- 3-2-1 zaxira qoidasini tushuntiring.
Xulosa #
- SQL injection - satr birlashtirishdan kelib chiqadi.
- Parametrli so'rov - yagona ishonchli yechim.
- Ekranlash yetarli emas.
- Jadval va ustun nomi uchun oq ro'yxat ishlating.
- Ilova
rootbilan ulanmasin - eng kam imtiyoz. - Har vazifa uchun alohida foydalanuvchi.
MD5/SHA1parol uchun yaramaydi - juda tez.argon2idyokibcryptishlating, ilova darajasida.- Maxfiy ma'lumotni ko'rinish orqali cheklang.
- Karta raqamini umuman saqlamang.
- Audit jadvaliga faqat
INSERThuquqi. - Sinab ko'rilmagan zaxira - zaxira emas; 3-2-1 qoidasi.
Keyingi va oxirgi bo'limda to'liq tizim sxemasini loyihalaymiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
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.