10-bo‘lim
Ma'lumot xavfsizligi
Ma'lumot tasnifi, shifrlash, maskalash, ma'lumot chiqishining oldini olish va shaxsiy ma'lumotlar himoyasi.
Ushbu bo‘lim mundarijasi
- Ma'lumot tasnifi
- Ma'lumot holatlari
- Ustun darajasida shifrlash
- Maskalash
- Sinov ma'lumotini anonimlashtirish
- Ma'lumotni minimallashtirish
- Saqlash muddati
- Ma'lumot chiqishining oldini olish
- Shaxsiy ma'lumotlar himoyasi
- Rozilikni boshqarish
- Jurnalda ma'lumot himoyasi
- Ma'lumot buzilishida javob
- Ma'lumot xavfsizligi ro'yxati
- Xulosa
Ma'lumot - hujumchilarning asosiy nishoni. Uning butun hayotiy tsikli davomida himoyalanishi kerak.
Ma'lumot tasnifi #
"Bizda qanday ma'lumot bor?" degan savolga javob bermasdan turib uni himoyalab bo'lmaydi.
| Ma'lumot | Daraja | Qayerda | Kim kiradi | Saqlash muddati |
|----------|--------|---------|-----------|----------------|
| Parol xeshlari | Maxfiy | MySQL | Faqat ilova | Doimiy |
| Karta ma'lumoti | Maxfiy | Saqlanmaydi | — | — |
| Mijoz telefoni | Shaxsiy | MySQL | Operator, admin | 3 yil |
| Buyurtma tarixi | Ichki | MySQL | Operator | 5 yil |
| Mahsulot katalogi | Ochiq | MySQL, kesh | Hamma | Doimiy |
Ma'lumot holatlari #
| Holat | Tavsifi | Himoya |
|---|---|---|
| Saqlanayotgan | Diskda, bazada | Diskni shifrlash, ustunni shifrlash |
| Uzatilayotgan | Tarmoqda | TLS, VPN |
| Ishlatilayotgan | Xotirada | Xotirani tozalash, izolyatsiya |
# Diskni shifrlash - LUKS
sudo cryptsetup luksFormat /dev/sdb1
sudo cryptsetup open /dev/sdb1 malumot
sudo mkfs.ext4 /dev/mapper/malumot
-- MySQL da jadval darajasida shifrlash
CREATE TABLE maxfiy_malumot (
id INT PRIMARY KEY,
mazmun TEXT
) ENGINE=InnoDB ENCRYPTION='Y';
Faqat jismoniy o'g'irlikdan: server o'g'irlansa, disk o'qilmaydi.
Lekin server ishlab turganda ma'lumot ochiq holatda - operatsion tizim uni avtomatik ochadi.
Shuning uchun ilova darajasida shifrlash ham kerak.
Ustun darajasida shifrlash #
final class ShifrlanganMaydon
{
public function __construct(
private Shifrlovchi $shifrlovchi,
) {}
public function saqlash(string $qiymat): array
{
return [
// Qidiruv uchun - deterministik xesh
'xesh' => hash_hmac('sha256', mb_strtolower($qiymat), $this->qidiruvKaliti),
// Ko'rsatish uchun - shifrlangan
'shifr' => $this->shifrlovchi->shifrlang($qiymat),
];
}
public function oqish(string $shifr): string
{
return $this->shifrlovchi->ochiring($shifr);
}
public function qidiruvXeshi(string $qiymat): string
{
return hash_hmac('sha256', mb_strtolower($qiymat), $this->qidiruvKaliti);
}
}
CREATE TABLE mijozlar (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
ism VARCHAR(80) NOT NULL,
email_xesh CHAR(64) NOT NULL, -- qidiruv uchun
email_shifr VARBINARY(512) NOT NULL, -- ko'rsatish uchun
telefon_xesh CHAR(64) NULL,
telefon_shifr VARBINARY(512) NULL,
UNIQUE KEY uq_email (email_xesh),
KEY idx_telefon (telefon_xesh)
) ENGINE=InnoDB;
// Qidiruv
$stmt = $pdo->prepare('SELECT * FROM mijozlar WHERE email_xesh = ?');
$stmt->execute([$maydon->qidiruvXeshi($email)]);
$qator = $stmt->fetch();
$email = $maydon->oqish($qator['email_shifr']);
Shifrlangan qiymat har safar boshqacha bo'ladi (nonce tufayli), shuning uchun uni bo'yicha qidirib bo'lmaydi.
Deterministik xesh esa har doim bir xil - qidiruv uchun ishlatiladi.
Xeshni HMAC bilan yarating (oddiy SHA-256 emas) - shunda hujumchi lug'at hujumi qila olmaydi.
Maskalash #
final class Maskalovchi
{
public function email(string $email): string
{
[$nom, $domen] = explode('@', $email, 2);
$korinadigan = min(2, mb_strlen($nom));
return mb_substr($nom, 0, $korinadigan)
. str_repeat('*', max(3, mb_strlen($nom) - $korinadigan))
. '@' . $domen;
}
public function telefon(string $telefon): string
{
$raqamlar = preg_replace('/\D/', '', $telefon);
return '+998 ** *** ** ' . mb_substr($raqamlar, -2);
}
public function karta(string $raqam): string
{
return '**** **** **** ' . mb_substr($raqam, -4);
}
public function ism(string $ism, string $familiya): string
{
return $ism . ' ' . mb_substr($familiya, 0, 1) . '.';
}
}
echo $maskalovchi->email('[email protected]');
echo $maskalovchi->telefon('+998901234567');
di***************@example.uz
+998 ** *** ** 67
| Joy | Nima uchun |
|---|---|
| Qo'llab-quvvatlash paneli | Operator to'liq ma'lumotni ko'rmasin |
| Jurnal fayllari | Sirlar jurnalga tushmasin |
| Sinov muhiti | Haqiqiy ma'lumot ishlatilmasin |
| Hisobotlar | Faqat kerakli qism ko'rinsin |
| Xato xabarlari | Ma'lumot oshkor bo'lmasin |
Sinov ma'lumotini anonimlashtirish #
-- Ishlab chiqarish nusxasini sinov uchun tayyorlash
UPDATE mijozlar SET
ism = CONCAT('Sinov', id),
familiya = CONCAT('Familiya', id),
email = CONCAT('sinov', id, '@example.test'),
telefon = CONCAT('+99890', LPAD(id, 7, '0')),
manzil = 'Sinov manzili',
tugilgan_sana = '2000-01-01',
parol_xesh = '$2y$12$sinovParoliUchunXesh...';
UPDATE buyurtmalar SET izoh = NULL WHERE izoh IS NOT NULL;
DELETE FROM tolov_malumotlari;
DELETE FROM audit_jurnali;
Bu keng tarqalgan, lekin juda xavfli amaliyot:
- Sinov muhiti kamroq himoyalangan
- Ko'proq odam kirish huquqiga ega
- Tasodifan haqiqiy mijozlarga email yuborilishi mumkin
- Qonun buzilishi (shaxsiy ma'lumotlar)
Har doim anonimlashtiring va tashqi xizmatlarni o'chirib qo'ying.
// Sinov muhitida tashqi xizmatlarni to'sish
final class XavfsizPochtaYuboruvchi implements PochtaInterfeysi
{
public function yuboring(string $qabuluvchi, Xat $xat): void
{
if ($this->muhit !== 'production') {
$this->jurnal->info('Xat yuborilmadi (sinov muhiti)', [
'qabuluvchi' => $qabuluvchi,
'mavzu' => $xat->mavzu(),
]);
// Barcha xatlarni bitta test manziliga
$qabuluvchi = $this->sinovManzili;
}
$this->haqiqiyYuboruvchi->yuboring($qabuluvchi, $xat);
}
}
Ma'lumotni minimallashtirish #
Har bir maydon uchun so'rang:
- Bu ma'lumot haqiqatan kerakmi?
- Qancha muddat kerak?
- Kim ko'rishi kerak?
- Uni anonimlashtirish mumkinmi?
Misollar:
- Tug'ilgan sana o'rniga - faqat yosh yoki "18+" bayrog'i
- To'liq manzil o'rniga - faqat shahar
- Karta raqami o'rniga - token (to'lov provayderidan)
- IP manzil o'rniga - mamlakat
// Karta ma'lumotini saqlamaslik
final class TolovServisi
{
public function tolang(TolovSorovi $sorov): Tolov
{
// Karta ma'lumoti to'g'ridan-to'g'ri provayderga
// Bizning serverimizdan o'tmaydi
$natija = $this->provayder->tolang([
'token' => $sorov->kartaTokeni, // provayderdan olingan
'summa' => $sorov->summa->tiyin,
'buyurtma' => $sorov->buyurtmaId,
]);
// Faqat oxirgi 4 raqam va tur saqlanadi
return $this->repozitoriy->saqlang(new Tolov(
buyurtmaId: $sorov->buyurtmaId,
oxirgi4: $natija->oxirgi4,
kartaTuri: $natija->kartaTuri,
tranzaksiyaId: $natija->id,
));
}
}
Saqlash muddati #
-- Muddati o'tgan ma'lumotni o'chirish
DELETE FROM sessiyalar WHERE oxirgi_faollik < DATE_SUB(NOW(), INTERVAL 30 DAY);
DELETE FROM parol_tiklash_tokenlari WHERE muddat < NOW();
DELETE FROM kirish_urinishlari WHERE vaqt < DATE_SUB(NOW(), INTERVAL 90 DAY);
-- Anonimlashtirilgan arxiv
UPDATE buyurtmalar
SET izoh = NULL, telefon = NULL
WHERE yaratilgan < DATE_SUB(NOW(), INTERVAL 3 YEAR);
// Muntazam tozalash vazifasi
final class MalumotTozalash
{
private const QOIDALAR = [
['jadval' => 'sessiyalar', 'maydon' => 'oxirgi_faollik', 'kun' => 30],
['jadval' => 'kirish_urinishlari', 'maydon' => 'vaqt', 'kun' => 90],
['jadval' => 'vaqtinchalik_fayllar','maydon' => 'yaratilgan', 'kun' => 7],
['jadval' => 'xabarnomalar', 'maydon' => 'yaratilgan', 'kun' => 180],
];
public function ishlang(): void
{
foreach (self::QOIDALAR as $qoida) {
$stmt = $this->pdo->prepare(sprintf(
'DELETE FROM %s WHERE %s < DATE_SUB(NOW(), INTERVAL ? DAY) LIMIT 10000',
$qoida['jadval'],
$qoida['maydon'],
));
do {
$stmt->execute([$qoida['kun']]);
$ochirilgan = $stmt->rowCount();
$this->jurnal->info('Ma\'lumot tozalandi', [
'jadval' => $qoida['jadval'],
'ochirilgan' => $ochirilgan,
]);
} while ($ochirilgan === 10000);
}
}
}
DELETE FROM ... WHERE ... bir vaqtda million qatorni o'chirsa,
jadval uzoq vaqt bloklanadi.
LIMIT 10000 bilan bo'lib o'chiring - sayt ishlashda davom etadi.
Ma'lumot chiqishining oldini olish #
// Katta hajmdagi yuklab olishni kuzatish
final class MalumotChiqishiNazorati
{
private const KUNLIK_CHEGARA = 1000;
public function tekshiring(Foydalanuvchi $f, int $qatorlarSoni): void
{
$bugun = $this->hisoblagich->oshiring(
"eksport:{$f->id}:" . date('Y-m-d'),
$qatorlarSoni,
86400,
);
if ($bugun > self::KUNLIK_CHEGARA) {
$this->xavfsizlik->ogohlantiring('katta_hajmdagi_eksport', [
'foydalanuvchi_id' => $f->id,
'email' => $f->email,
'bugungi_qatorlar' => $bugun,
'ip' => $this->sorov->ip(),
]);
throw new EksportChegarasiOshdi(self::KUNLIK_CHEGARA);
}
}
}
| Belgi | Nima anglatishi mumkin |
|---|---|
| Katta hajmdagi eksport | Ma'lumot o'g'irlash |
| Ish vaqtidan tashqari faollik | Buzilgan hisob |
| Ketma-ket ID bo'yicha so'rovlar | Avtomatlashtirilgan yig'ish |
| Yangi joylashuvdan kirish | Hisob egallangan |
| Bir vaqtda ko'p sessiya | Hisob ulashilgan yoki o'g'irlangan |
// Ketma-ket so'rovlarni aniqlash
final class SkanerlashDetektori
{
public function tekshiring(Foydalanuvchi $f, int $resursId): void
{
$kalit = "korilgan:{$f->id}";
$oxirgi = (int) $this->kesh->oling("{$kalit}:oxirgi");
if ($oxirgi > 0 && abs($resursId - $oxirgi) === 1) {
$ketmaKet = $this->kesh->oshiring("{$kalit}:ketma", 300);
if ($ketmaKet > 20) {
$this->xavfsizlik->ogohlantiring('ketma_ket_sorovlar', [
'foydalanuvchi_id' => $f->id,
'soni' => $ketmaKet,
]);
}
} else {
$this->kesh->ochiring("{$kalit}:ketma");
}
$this->kesh->saqlang("{$kalit}:oxirgi", $resursId, 300);
}
}
Shaxsiy ma'lumotlar himoyasi #
final class ShaxsiyMalumotXizmati
{
public function eksport(int $foydalanuvchiId): array
{
return [
'profil' => $this->profil->oling($foydalanuvchiId),
'buyurtmalar' => $this->buyurtmalar->foydalanuvchiniki($foydalanuvchiId),
'manzillar' => $this->manzillar->foydalanuvchiniki($foydalanuvchiId),
'sharhlar' => $this->sharhlar->foydalanuvchiniki($foydalanuvchiId),
'sessiyalar' => $this->sessiyalar->foydalanuvchiniki($foydalanuvchiId),
'roziliklar' => $this->roziliklar->foydalanuvchiniki($foydalanuvchiId),
];
}
public function ochiring(int $foydalanuvchiId): void
{
$this->pdo->beginTransaction();
try {
// Qonun talab qiladigan ma'lumot anonimlashtiriladi
$this->buyurtmalar->anonimlashtiring($foydalanuvchiId);
// Qolganlari o'chiriladi
$this->manzillar->ochiring($foydalanuvchiId);
$this->sharhlar->anonimlashtiring($foydalanuvchiId);
$this->sessiyalar->hammasiniOchiring($foydalanuvchiId);
$this->profil->ochiring($foydalanuvchiId);
$this->audit->yozing('shaxsiy_malumot_ochirildi', [
'foydalanuvchi_id' => $foydalanuvchiId,
'sana' => date('c'),
]);
$this->pdo->commit();
} catch (Throwable $x) {
$this->pdo->rollBack();
throw $x;
}
}
}
Ba'zi ma'lumotlar qonun bo'yicha saqlanishi kerak:
- Moliyaviy hujjatlar (soliq talabi)
- Buyurtma tarixi (kafolat)
- Audit jurnali
Bunday holatda ma'lumot anonimlashtiriladi: shaxs bilan bog'liqlik uziladi, lekin yozuv qoladi.
UPDATE buyurtmalar
SET mijoz_id = NULL, telefon = NULL, manzil = 'O''chirilgan'
WHERE mijoz_id = ?;
Rozilikni boshqarish #
CREATE TABLE roziliklar (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
foydalanuvchi_id BIGINT UNSIGNED NOT NULL,
turi VARCHAR(50) NOT NULL,
berilgan BOOLEAN NOT NULL,
versiya VARCHAR(20) NOT NULL,
ip VARCHAR(45),
vaqt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
KEY idx_foydalanuvchi (foydalanuvchi_id, turi, vaqt DESC)
) ENGINE=InnoDB;
final class RozilikXizmati
{
public function yozing(
int $foydalanuvchiId,
RozilikTuri $turi,
bool $berilgan,
): void {
// Eski yozuvlar o'chirilmaydi - tarix saqlanadi
$this->repozitoriy->qoshing([
'foydalanuvchi_id' => $foydalanuvchiId,
'turi' => $turi->value,
'berilgan' => $berilgan,
'versiya' => $this->joriySiyosatVersiyasi,
'ip' => $this->sorov->ip(),
]);
}
public function bormi(int $foydalanuvchiId, RozilikTuri $turi): bool
{
return $this->repozitoriy->oxirgi($foydalanuvchiId, $turi)?->berilgan ?? false;
}
}
// Marketing xati yuborishdan oldin
if (!$this->rozilik->bormi($mijoz->id, RozilikTuri::Marketing)) {
return; // xat yuborilmaydi
}
"Foydalanuvchi rozilik bergan edimi?" degan savolga javob berish uchun tarix kerak.
Har bir o'zgarish yangi yozuv sifatida saqlanadi: qachon, qanday versiyaga, qaysi IP dan.
Jurnalda ma'lumot himoyasi #
final class MaxfiylikFiltri
{
private const MAXFIY_KALITLAR = [
'parol', 'password', 'token', 'sir', 'secret',
'karta', 'cvv', 'pin', 'api_kalit', 'authorization',
];
public function tozalang(array $malumot): array
{
$natija = [];
foreach ($malumot as $kalit => $qiymat) {
if ($this->maxfiymi((string) $kalit)) {
$natija[$kalit] = '***';
continue;
}
$natija[$kalit] = is_array($qiymat)
? $this->tozalang($qiymat)
: $this->qiymatniTozalang($qiymat);
}
return $natija;
}
private function maxfiymi(string $kalit): bool
{
$past = mb_strtolower($kalit);
foreach (self::MAXFIY_KALITLAR as $maxfiy) {
if (str_contains($past, $maxfiy)) {
return true;
}
}
return false;
}
private function qiymatniTozalang(mixed $qiymat): mixed
{
if (!is_string($qiymat)) {
return $qiymat;
}
// Karta raqamini maskalash
$qiymat = preg_replace('/\b\d{13,19}\b/', '****', $qiymat);
// Emailni maskalash
return preg_replace_callback(
'/([\w.+-]{1,2})[\w.+-]*(@[\w.-]+)/',
fn (array $m) => $m[1] . '***' . $m[2],
$qiymat,
);
}
}
Jurnal fayllari:
- Ko'p odam ko'radi
- Uzoq saqlanadi
- Markazlashtirilgan tizimga yuboriladi
- Zaxira nusxaga tushadi
Bir marta jurnalga tushgan parolni "qaytarib olib bo'lmaydi".
Filtrni jurnalga yozishdan oldin qo'llang.
Ma'lumot buzilishida javob #
# Ma'lumot buzilishi rejasi
## 1. Aniqlash va cheklash (0-4 soat)
- [ ] Buzilish faktini tasdiqlash
- [ ] Kirish yo'lini yopish
- [ ] Buzilgan hisoblarni bloklash
- [ ] Dalillarni saqlash (jurnal, xotira nusxasi)
## 2. Baholash (4-24 soat)
- [ ] Qanday ma'lumot oshkor bo'ldi?
- [ ] Nechta foydalanuvchi ta'sirlandi?
- [ ] Qachon boshlandi va qancha davom etdi?
- [ ] Ma'lumot ko'chirildimi yoki faqat ko'rildimi?
## 3. Xabar berish (24-72 soat)
- [ ] Rahbariyat
- [ ] Yuridik xizmat
- [ ] Nazorat organi (qonun talabiga ko'ra)
- [ ] Ta'sirlangan foydalanuvchilar
- [ ] Ommaviy bayonot (kerak bo'lsa)
## 4. Tiklash
- [ ] Zaiflikni tuzatish
- [ ] Parollarni majburiy almashtirish
- [ ] Sessiyalarni bekor qilish
- [ ] Qo'shimcha monitoring
## 5. Xulosa
- [ ] Hodisadan keyingi tahlil
- [ ] Jarayonlarni yangilash
- [ ] Xodimlarni o'qitish
Ko'pchilik mamlakatlarda ma'lumot buzilishi haqida nazorat organiga belgilangan muddatda (odatda 72 soat) xabar berish talab qilinadi.
Bu muddat "buzilishni bilgan paytdan" hisoblanadi.
Rejani oldindan tayyorlang - hodisa paytida uni yozishga vaqt bo'lmaydi.
// Foydalanuvchiga xabar shabloni
final class BuzilishXabari
{
public function matn(array $tafsilotlar): string
{
return <<<XAT
Hurmatli {$tafsilotlar['ism']},
Biz {$tafsilotlar['sana']} kuni tizimimizda xavfsizlik hodisasi
yuz berganini aniqladik.
NIMA BO'LDI
{$tafsilotlar['tavsif']}
QANDAY MA'LUMOT TA'SIRLANDI
{$tafsilotlar['malumot']}
BIZ NIMA QILDIK
- Zaiflik darhol yopildi
- Barcha sessiyalar bekor qilindi
- Qo'shimcha monitoring o'rnatildi
SIZ NIMA QILISHINGIZ KERAK
1. Parolingizni almashtiring
2. Ikki bosqichli autentifikatsiyani yoqing
3. Boshqa saytlarda bir xil parol ishlatgan bo'lsangiz,
ularni ham almashtiring
4. Shubhali faollikni kuzating
SAVOLLAR
xavfsizlik@dokon.uz | +998 71 xxx-xx-xx
Uzr so'raymiz va sizning ishonchingizni qadrlaymiz.
XAT;
}
}
Yashirishga urinish har doim yomonroq oqibatga olib keladi:
- Ishonch butunlay yo'qoladi
- Jarima ortadi
- Ommaviy axborot vositalarida salbiy yoritiladi
Aniq, tushunarli va tez xabar - eng yaxshi strategiya.
Ma'lumot xavfsizligi ro'yxati #
| Talab | Bajarildi |
|---|---|
| Ma'lumot inventarizatsiyasi tuzilgan | |
| Tasnif belgilangan | |
| Maxfiy ma'lumot shifrlangan | |
| Kalitlar alohida saqlanadi | |
| TLS barcha ulanishlarda | |
| Jurnalda sirlar yo'q | |
| Sinov muhitida anonimlashtirilgan ma'lumot | |
| Saqlash muddati belgilangan va bajariladi | |
| Ma'lumot minimallashtirilgan | |
| Katta hajmdagi eksport kuzatiladi | |
| Foydalanuvchi huquqlari amalga oshirilgan | |
| Rozilik yozib boriladi | |
| Buzilish rejasi tayyor | |
| Zaxira nusxa shifrlangan |
- Loyihangizdagi ma'lumot inventarizatsiyasini tuzing.
- Har birini tasniflang va himoya darajasini belgilang.
- Bitta maydonni shifrlang (qidiruv xeshi bilan).
Maskalovchiklassini yozing.- Sinov ma'lumotini anonimlashtiruvchi skript yozing.
- Jurnal filtrini yozing va sinang.
- Saqlash muddati qoidalarini belgilang va tozalash vazifasini yozing.
- Katta hajmdagi eksportni kuzatuvchi kod yozing.
- Shaxsiy ma'lumotni eksport qilish funksiyasini yozing.
- Ma'lumot buzilishi rejasini yozing.
Xulosa #
- Avval ma'lumot inventarizatsiyasi, keyin himoya.
- Uch holat: saqlanayotgan, uzatilayotgan, ishlatilayotgan.
- Disk shifrlash faqat jismoniy o'g'irlikdan himoya qiladi.
- Qidiruv uchun deterministik HMAC xesh, ko'rsatish uchun shifrlangan qiymat.
- Maskalash qo'llab-quvvatlash, jurnal va hisobotlarda.
- Sinov muhitida ma'lumot anonimlashtirilsin, tashqi xizmatlar o'chirilsin.
- Yig'maganingizni yo'qotolmaysiz - ma'lumotni minimallashtiring.
- Saqlash muddatini belgilang va avtomatik tozalang.
- Katta hajmdagi eksportni kuzating.
- Jurnalga tushgan sir - oshkor sir.
- Buzilish rejasini oldindan tayyorlang.
Keyingi bo'limda zaifliklarni boshqarishni 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.