18-bo‘lim
Foydalanuvchilar va xavfsizlik
Foydalanuvchi yaratish, huquqlar berish, SQL inyeksiya va undan himoyalanish, parollarni to'g'ri saqlash.
Ushbu bo‘lim mundarijasi
Ma'lumotlar bazasi - loyihaning eng qimmatli qismi. Uni himoya qilish alohida e'tibor talab qiladi.
Foydalanuvchilarni boshqarish #
-- Yaratish
CREATE USER 'dokon_app'@'localhost' IDENTIFIED BY 'KuchliParol123!';
-- Istalgan xostdan ulanish uchun
CREATE USER 'admin'@'%' IDENTIFIED BY 'ParolBuYerda';
-- Parolni o'zgartirish
ALTER USER 'dokon_app'@'localhost' IDENTIFIED BY 'YangiParol456!';
-- O'chirish
DROP USER 'dokon_app'@'localhost';
-- Ro'yxat
SELECT user, host FROM mysql.user;
'%' xostdan ehtiyot bo'ling'user'@'%' - istalgan IP dan ulanish mumkin degani. Ishlab chiqarish
serverida bu xavfli.
To'g'ri yondashuv:
CREATE USER 'app'@'localhost' ...; -- faqat shu serverdan
CREATE USER 'app'@'10.0.1.%' ...; -- faqat ichki tarmoqdan
Huquqlar (privileges) #
-- Ma'lum bazaga to'liq huquq
GRANT ALL PRIVILEGES ON dokon.* TO 'dokon_admin'@'localhost';
-- Faqat kerakli amallar (tavsiya etiladi)
GRANT SELECT, INSERT, UPDATE, DELETE ON dokon.* TO 'dokon_app'@'localhost';
-- Faqat o'qish
GRANT SELECT ON dokon.* TO 'hisobotchi'@'localhost';
-- Bitta jadval
GRANT SELECT ON dokon.mahsulotlar TO 'katalog'@'localhost';
-- Bitta ustun
GRANT SELECT (id, nomi, narx) ON dokon.mahsulotlar TO 'narxlar'@'localhost';
-- Qo'llash
FLUSH PRIVILEGES;
-- Ko'rish
SHOW GRANTS FOR 'dokon_app'@'localhost';
-- Bekor qilish
REVOKE DELETE ON dokon.* FROM 'dokon_app'@'localhost';
| Huquq | Nima beradi |
|---|---|
SELECT | O'qish |
INSERT | Qo'shish |
UPDATE | O'zgartirish |
DELETE | O'chirish |
CREATE | Jadval yaratish |
DROP | Jadval o'chirish |
ALTER | Tuzilmani o'zgartirish |
INDEX | Indeks boshqarish |
EXECUTE | Protsedura chaqirish |
ALL PRIVILEGES | Hammasi |
Ilova foydalanuvchisiga DROP, ALTER, CREATE huquqlarini bermang.
Agar SQL inyeksiya topilsa, hujumchi faqat ma'lumot o'qiy oladi - butun bazani o'chirib tashlay olmaydi.
-- Ilova uchun
GRANT SELECT, INSERT, UPDATE, DELETE ON dokon.* TO 'dokon_app'@'localhost';
-- Migratsiya uchun alohida foydalanuvchi
GRANT ALL PRIVILEGES ON dokon.* TO 'dokon_migrate'@'localhost';
SQL inyeksiya #
Eng mashhur va eng xavfli veb-hujum turi.
Zaif kod #
// XAVFLI - hech qachon shunday yozmang
$ism = $_GET['ism'];
$sql = "SELECT * FROM talabalar WHERE ism = '$ism'";
$natija = $pdo->query($sql);
Oddiy foydalanuvchi Husanboy deb yozadi:
SELECT * FROM talabalar WHERE ism = 'Husanboy'
Hujumchi esa ' OR '1'='1 deb yozadi:
SELECT * FROM talabalar WHERE ism = '' OR '1'='1'
Natija - butun jadval qaytadi.
Hujum turlari #
-- 1. Autentifikatsiyani chetlab o'tish
-- Kiritilgan: admin'--
SELECT * FROM users WHERE login = 'admin'--' AND parol = '...'
-- `--` dan keyingi hammasi izohga aylanadi, parol tekshirilmaydi
-- 2. Boshqa jadvaldan ma'lumot o'g'irlash
-- Kiritilgan: ' UNION SELECT login, parol, 1 FROM users--
SELECT nomi, tavsif, narx FROM mahsulotlar WHERE id = ''
UNION SELECT login, parol, 1 FROM users--'
-- 3. Ma'lumotni yo'q qilish
-- Kiritilgan: '; DROP TABLE talabalar;--
Himoya: tayyorlangan so'rovlar #
// TO'G'RI: PDO bilan
$stmt = $pdo->prepare('SELECT * FROM talabalar WHERE ism = ?');
$stmt->execute([$ism]);
$talabalar = $stmt->fetchAll();
// Nomlangan parametrlar bilan (o'qishga qulay)
$stmt = $pdo->prepare(
'SELECT * FROM talabalar WHERE shahar = :shahar AND ortacha_baho >= :baho'
);
$stmt->execute([
'shahar' => $shahar,
'baho' => $baho,
]);
Tayyorlangan so'rovda so'rov tuzilmasi va ma'lumot alohida yuboriladi:
- Baza avval so'rov shablonini oladi va tahlil qiladi
- Keyin qiymatlar alohida yuboriladi
- Qiymat har doim ma'lumot sifatida qaraladi, hech qachon kod sifatida emas
' OR '1'='1 yozilsa, baza uni shunday nomli talaba deb qidiradi va
hech nima topmaydi. Hujum ishlamaydi.
# Python
kursor.execute("SELECT * FROM talabalar WHERE ism = %s", (ism,))
# XATO - f-satr bilan
kursor.execute(f"SELECT * FROM talabalar WHERE ism = '{ism}'")
// Node.js (mysql2)
const [qatorlar] = await ulanish.execute(
'SELECT * FROM talabalar WHERE ism = ?', [ism]
);
// ISHLAMAYDI - jadval va ustun nomlari parametr bo'lolmaydi
$stmt = $pdo->prepare('SELECT * FROM ? ORDER BY ?');
$stmt->execute([$jadval, $ustun]);
Bunday holatda oq ro'yxat (whitelist) ishlating:
$ruxsatEtilgan = ['ism', 'familiya', 'ortacha_baho', 'yaratilgan'];
if (!in_array($ustun, $ruxsatEtilgan, true)) {
$ustun = 'id';
}
$yonalish = $yonalish === 'desc' ? 'DESC' : 'ASC';
$sql = "SELECT * FROM talabalar ORDER BY {$ustun} {$yonalish}";
Hech qachon foydalanuvchi kiritgan matnni to'g'ridan-to'g'ri so'rovga qo'ymang.
Parollarni saqlash #
-- FALOKAT
INSERT INTO users (login, parol) VALUES ('husanboy', 'parol123');
-- HAM YOMON: MD5 va SHA1 buzilgan
INSERT INTO users (login, parol) VALUES ('husanboy', MD5('parol123'));
MD5 xeshini zamonaviy videokartada soniyasiga milliardlab urinish bilan
ochish mumkin. "Rainbow table" lar orqali oddiy parollar bir zumda topiladi.
// TO'G'RI: PHP da
$xesh = password_hash($parol, PASSWORD_DEFAULT);
$stmt = $pdo->prepare('INSERT INTO users (login, parol_xesh) VALUES (?, ?)');
$stmt->execute([$login, $xesh]);
// Tekshirish
$stmt = $pdo->prepare('SELECT parol_xesh FROM users WHERE login = ?');
$stmt->execute([$login]);
$user = $stmt->fetch();
if ($user && password_verify($parol, $user['parol_xesh'])) {
// kirish muvaffaqiyatli
}
-- Ustun uzunligi yetarli bo'lsin
CREATE TABLE users (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
login VARCHAR(60) NOT NULL UNIQUE,
parol_xesh VARCHAR(255) NOT NULL, -- 60 emas, 255!
yaratilgan TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
VARCHAR(255)?password_hash hozir 60 belgi qaytaradi, lekin PHP algoritmni yangilaganda
uzunlik o'zgarishi mumkin. 255 - kelajakka zaxira.
Bcrypt avtomatik "tuz" (salt) qo'shadi va ataylab sekin ishlaydi - bu ommaviy urinishlarni imkonsiz qiladi.
Maxfiy ma'lumotni himoyalash #
-- Shifrlash
INSERT INTO hujjatlar (raqam)
VALUES (AES_ENCRYPT('AA1234567', 'maxfiy_kalit'));
SELECT AES_DECRYPT(raqam, 'maxfiy_kalit') AS raqam FROM hujjatlar;
-- Qidiruv uchun xesh (deterministik)
CREATE TABLE mijozlar (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
email_xesh CHAR(64) NOT NULL UNIQUE, -- qidiruv uchun
email_shifr VARBINARY(255) NOT NULL -- ko'rsatish uchun
);
Kalit muhit o'zgaruvchisida yoki maxsus kalit boshqaruv tizimida saqlanishi kerak. Agar kalit ham bazada bo'lsa, shifrlashning ma'nosi yo'qoladi.
$kalit = getenv('DB_ENCRYPTION_KEY');
Ma'lumotni yashirish #
-- Hisobotlar uchun VIEW
CREATE VIEW mijozlar_maskalangan AS
SELECT
id,
ism,
CONCAT(LEFT(email, 2), '***@', SUBSTRING_INDEX(email, '@', -1)) AS email,
CONCAT('+998 ** *** ** ', RIGHT(telefon, 2)) AS telefon
FROM mijozlar;
SELECT * FROM mijozlar_maskalangan;
+----+----------+------------------+-------------------+
| id | ism | email | telefon |
+----+----------+------------------+-------------------+
| 1 | Husanboy | hu***@example.uz | +998 ** *** ** 67 |
+----+----------+------------------+-------------------+
Ulanish xavfsizligi #
// Ma'lumotlarni kodda saqlamang
$dsn = getenv('DB_DSN');
$user = getenv('DB_USER');
$password = getenv('DB_PASSWORD');
$pdo = new PDO($dsn, $user, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false, // haqiqiy tayyorlangan so'rov
]);
.env faylni Git ga qo'shmang# .gitignore
.env
config/database.php
*.sql
GitHub da minglab loyihalarda baza paroli ochiq turibdi - chunki kimdir sozlama faylini tasodifan yuklagan. Bu eng ko'p uchraydigan xavfsizlik xatosi.
PDO::ATTR_EMULATE_PREPARES => falseStandart holatda PDO tayyorlangan so'rovlarni emulyatsiya qiladi - ya'ni qiymatlarni o'zi so'rovga qo'yadi. Bu odatda xavfsiz, lekin haqiqiy server tomonidagi tayyorlash ancha ishonchliroq.
Bu sozlama ushbu saytning Database.php faylida ham yoqilgan.
Xavfsizlik tekshiruv ro'yxati #
| Talab | Bajarildi |
|---|---|
root paroli o'rnatilgan | |
| Ilova uchun alohida foydalanuvchi | |
Faqat kerakli huquqlar berilgan (DROP yo'q) | |
| Barcha so'rovlar parametrlangan | |
Parollar password_hash bilan xeshlangan | |
Ulanish ma'lumotlari .env da, Git da emas | |
| Xato xabarlari foydalanuvchiga ko'rsatilmaydi | |
| Zaxira nusxa avtomatlashtirilgan | |
Baza tashqi tarmoqdan yopiq (bind-address) | |
| Ma'lumot uzatishda TLS ishlatilgan |
# my.cnf - faqat localhost dan ulanish
[mysqld]
bind-address = 127.0.0.1
Xato xabarlari #
// XAVFLI: baza tuzilmasini oshkor qiladi
try {
$pdo->query($sql);
} catch (PDOException $e) {
echo $e->getMessage();
// "Table 'dokon.users' doesn't exist" - hujumchiga foydali ma'lumot
}
// TO'G'RI
try {
$pdo->query($sql);
} catch (PDOException $e) {
error_log($e->getMessage()); // jurnalga yozamiz
echo 'Serverda xatolik yuz berdi.'; // foydalanuvchiga umumiy xabar
}
Ommaviy hujumdan himoya #
-- Kirish urinishlarini yozish
CREATE TABLE kirish_urinishlari (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
ip VARCHAR(45) NOT NULL,
login VARCHAR(60),
muvaffaqiyat BOOLEAN NOT NULL DEFAULT FALSE,
vaqt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
KEY idx_ip_vaqt (ip, vaqt)
) ENGINE=InnoDB;
-- Oxirgi 15 daqiqadagi muvaffaqiyatsiz urinishlar
SELECT COUNT(*) FROM kirish_urinishlari
WHERE ip = ? AND muvaffaqiyat = FALSE
AND vaqt > DATE_SUB(NOW(), INTERVAL 15 MINUTE);
5 tadan ko'p bo'lsa - kirishni vaqtincha bloklang.
kutubxona_appfoydalanuvchisini yarating va faqatSELECT, INSERT, UPDATEhuquqlarini bering.SHOW GRANTSbilan tekshiring.DELETEqilishga urinib ko'ring - xatoni ko'ring.- PHP da parametrlanmagan so'rov yozing va
' OR '1'='1bilan sinab ko'ring. - Uni tayyorlangan so'rovga aylantiring va yana sinang.
password_hashbilan foydalanuvchi ro'yxatdan o'tkazish kodini yozing.VIEWbilan email va telefonni maskalangan ko'rinishda chiqaring.- Saralash ustuni uchun oq ro'yxat funksiyasini yozing.
Xulosa #
- Ilova uchun alohida foydalanuvchi yarating,
rootni ishlatmang. - Eng kam huquq prinsipi:
DROP,ALTERhuquqlarini bermang. - SQL inyeksiya ma'lumot va buyruq aralashganda yuzaga keladi.
- Yagona ishonchli himoya - tayyorlangan so'rovlar (
prepare+execute). - Jadval va ustun nomlari parametr bo'lolmaydi - oq ro'yxat ishlating.
- Parollarni
password_hashbilan xeshlang,MD5bilan emas. - Ulanish ma'lumotlarini
.envda saqlang va Git ga qo'shmang. - Xato xabarlarini foydalanuvchiga ko'rsatmang - jurnalga yozing.
Keyingi bo'limda zaxira nusxa va PHP bilan ishlashni ko'ramiz.
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.