18-bo‘lim

Foydalanuvchilar va xavfsizlik

Foydalanuvchi yaratish, huquqlar berish, SQL inyeksiya va undan himoyalanish, parollarni to'g'ri saqlash.

🕑 15 daqiqa o‘qish 📄 729 so‘z 👁 5 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Foydalanuvchilarni boshqarish
  2. Huquqlar (privileges)
  3. SQL inyeksiya
  4. Zaif kod
  5. Hujum turlari
  6. Himoya: tayyorlangan so'rovlar
  7. Parollarni saqlash
  8. Maxfiy ma'lumotni himoyalash
  9. Ma'lumotni yashirish
  10. Ulanish xavfsizligi
  11. Xavfsizlik tekshiruv ro'yxati
  12. Xato xabarlari
  13. Ommaviy hujumdan himoya
  14. Xulosa

Ma'lumotlar bazasi - loyihaning eng qimmatli qismi. Uni himoya qilish alohida e'tibor talab qiladi.

Foydalanuvchilarni boshqarish #

SQL
-- 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:

SQL
CREATE USER 'app'@'localhost' ...;         -- faqat shu serverdan
CREATE USER 'app'@'10.0.1.%' ...;          -- faqat ichki tarmoqdan

Huquqlar (privileges) #

SQL
-- 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';
HuquqNima beradi
SELECTO'qish
INSERTQo'shish
UPDATEO'zgartirish
DELETEO'chirish
CREATEJadval yaratish
DROPJadval o'chirish
ALTERTuzilmani o'zgartirish
INDEXIndeks boshqarish
EXECUTEProtsedura chaqirish
ALL PRIVILEGESHammasi
Eng kam huquq prinsipi

Ilova foydalanuvchisiga DROP, ALTER, CREATE huquqlarini bermang.

Agar SQL inyeksiya topilsa, hujumchi faqat ma'lumot o'qiy oladi - butun bazani o'chirib tashlay olmaydi.

SQL
-- 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 #

PHP
// XAVFLI - hech qachon shunday yozmang
$ism = $_GET['ism'];
$sql = "SELECT * FROM talabalar WHERE ism = '$ism'";
$natija = $pdo->query($sql);

Oddiy foydalanuvchi Husanboy deb yozadi:

SQL
SELECT * FROM talabalar WHERE ism = 'Husanboy'

Hujumchi esa ' OR '1'='1 deb yozadi:

SQL
SELECT * FROM talabalar WHERE ism = '' OR '1'='1'

Natija - butun jadval qaytadi.

Formaga kiritilgan matn so'rovga qo'shilib ketadi Oddiy foydalanuvchi: SELECT * FROM talabalar WHERE ism = ' Husanboy ' Hujumchi: SELECT * FROM talabalar WHERE ism = ' ' OR '1'='1 ' Shart har doim ROST bo'ladi → butun jadval qaytadi DROP TABLE yoki UNION SELECT bilan yanada yomonroq oqibat mumkin
Ma'lumot va buyruq aralashib ketishi - inyeksiyaning mohiyati

Hujum turlari #

SQL
-- 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 #

PHP
// 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,
]);
Nima uchun bu ishlaydi?

Tayyorlangan so'rovda so'rov tuzilmasi va ma'lumot alohida yuboriladi:

  1. Baza avval so'rov shablonini oladi va tahlil qiladi
  2. Keyin qiymatlar alohida yuboriladi
  3. 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
# Python
kursor.execute("SELECT * FROM talabalar WHERE ism = %s", (ism,))

# XATO - f-satr bilan
kursor.execute(f"SELECT * FROM talabalar WHERE ism = '{ism}'")
JavaScript
// Node.js (mysql2)
const [qatorlar] = await ulanish.execute(
    'SELECT * FROM talabalar WHERE ism = ?', [ism]
);
Parametrlash ishlamaydigan joylar
PHP
// 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:

PHP
$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 #

Parolni hech qachon ochiq saqlamang
SQL
-- 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.

PHP
// 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
}
SQL
-- 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
);
Nima uchun 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 #

SQL
-- 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
);
Shifrlash kalitini bazada saqlamang

Kalit muhit o'zgaruvchisida yoki maxsus kalit boshqaruv tizimida saqlanishi kerak. Agar kalit ham bazada bo'lsa, shifrlashning ma'nosi yo'qoladi.

PHP
$kalit = getenv('DB_ENCRYPTION_KEY');

Ma'lumotni yashirish #

SQL
-- 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;
Natija
+----+----------+------------------+-------------------+
| id | ism      | email            | telefon           |
+----+----------+------------------+-------------------+
|  1 | Husanboy | hu***@example.uz | +998 ** *** ** 67 |
+----+----------+------------------+-------------------+

Ulanish xavfsizligi #

PHP
// 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
Natija
# .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 => false

Standart 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 #

Ishlab chiqarishga chiqarishdan oldin
TalabBajarildi
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
INI
# my.cnf - faqat localhost dan ulanish
[mysqld]
bind-address = 127.0.0.1

Xato xabarlari #

PHP
// 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 #

SQL
-- 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.

Amaliy topshiriq
  1. kutubxona_app foydalanuvchisini yarating va faqat SELECT, INSERT, UPDATE huquqlarini bering.
  2. SHOW GRANTS bilan tekshiring.
  3. DELETE qilishga urinib ko'ring - xatoni ko'ring.
  4. PHP da parametrlanmagan so'rov yozing va ' OR '1'='1 bilan sinab ko'ring.
  5. Uni tayyorlangan so'rovga aylantiring va yana sinang.
  6. password_hash bilan foydalanuvchi ro'yxatdan o'tkazish kodini yozing.
  7. VIEW bilan email va telefonni maskalangan ko'rinishda chiqaring.
  8. Saralash ustuni uchun oq ro'yxat funksiyasini yozing.

Xulosa #

  • Ilova uchun alohida foydalanuvchi yarating, root ni ishlatmang.
  • Eng kam huquq prinsipi: DROP, ALTER huquqlarini 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_hash bilan xeshlang, MD5 bilan emas.
  • Ulanish ma'lumotlarini .env da saqlang va Git ga qo'shmang.
  • Xato xabarlarini foydalanuvchiga ko'rsatmang - jurnalga yozing.

Keyingi bo'limda zaxira nusxa va PHP bilan ishlashni ko'ramiz.

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.