5-bo‘lim
Avtorizatsiya va kirish nazorati
RBAC, ABAC, eng kam huquq prinsipi, resurs darajasidagi tekshiruv va IDOR zaifligiga qarshi himoya.
Ushbu bo‘lim mundarijasi
Autentifikatsiya "siz kimsiz?" savoliga javob beradi. Avtorizatsiya esa "sizga nima qilishga ruxsat bor?" savoliga.
Ikki tushuncha #
Kirish nazorati modellari #
| Model | Asosi | Qachon |
|---|---|---|
| DAC | Resurs egasi qaror qiladi | Fayl tizimlari |
| MAC | Markaziy siyosat, o'zgartirib bo'lmaydi | Harbiy, davlat |
| RBAC | Rollar | Ko'pchilik biznes ilovalari |
| ABAC | Atributlar va shartlar | Murakkab qoidalar |
| ReBAC | Munosabatlar | Ijtimoiy tarmoq, hujjatlar |
RBAC - rollarga asoslangan #
enum Huquq: string
{
case FoydalanuvchiKorish = 'foydalanuvchi.korish';
case FoydalanuvchiTahrirlash = 'foydalanuvchi.tahrirlash';
case FoydalanuvchiOchirish = 'foydalanuvchi.ochirish';
case BuyurtmaKorish = 'buyurtma.korish';
case BuyurtmaTahrirlash = 'buyurtma.tahrirlash';
case BuyurtmaBekorQilish = 'buyurtma.bekor';
case HisobotKorish = 'hisobot.korish';
case SozlamaOzgartirish = 'sozlama.ozgartirish';
}
enum Rol: string
{
case Administrator = 'administrator';
case Operator = 'operator';
case Koruvchi = 'koruvchi';
/** @return Huquq[] */
public function huquqlar(): array
{
return match ($this) {
self::Administrator => Huquq::cases(),
self::Operator => [
Huquq::FoydalanuvchiKorish,
Huquq::BuyurtmaKorish,
Huquq::BuyurtmaTahrirlash,
Huquq::BuyurtmaBekorQilish,
Huquq::HisobotKorish,
],
self::Koruvchi => [
Huquq::BuyurtmaKorish,
Huquq::HisobotKorish,
],
};
}
public function huquqBormi(Huquq $huquq): bool
{
return in_array($huquq, $this->huquqlar(), true);
}
}
final class Foydalanuvchi
{
/** @param Rol[] $rollar */
public function __construct(
public readonly int $id,
public readonly string $email,
private array $rollar,
) {}
public function huquqBormi(Huquq $huquq): bool
{
foreach ($this->rollar as $rol) {
if ($rol->huquqBormi($huquq)) {
return true;
}
}
return false;
}
public function rolBormi(Rol $rol): bool
{
return in_array($rol, $this->rollar, true);
}
}
// MO'RT: yangi rol qo'shilsa, hamma joyni tuzatish kerak
if ($foydalanuvchi->rolBormi(Rol::Administrator)
|| $foydalanuvchi->rolBormi(Rol::Operator)) { }
// MOSLASHUVCHAN
if ($foydalanuvchi->huquqBormi(Huquq::BuyurtmaTahrirlash)) { }
Ikkinchi variantda yangi rol qo'shish uchun faqat rol ta'rifi o'zgaradi.
Resurs darajasidagi tekshiruv #
RBAC "buyurtmalarni ko'rish mumkinmi?" degan savolga javob beradi. Lekin qaysi buyurtmalarni?
final class BuyurtmaSiyosati
{
public function korishiMumkinmi(Foydalanuvchi $f, Buyurtma $b): bool
{
// Xodimlar hammasini ko'radi
if ($f->huquqBormi(Huquq::BuyurtmaKorish)) {
return true;
}
// Mijoz faqat o'zinikini
return $f->id === $b->mijozId;
}
public function tahrirlashiMumkinmi(Foydalanuvchi $f, Buyurtma $b): bool
{
if (!$this->korishiMumkinmi($f, $b)) {
return false;
}
if ($f->huquqBormi(Huquq::BuyurtmaTahrirlash)) {
return true;
}
// Mijoz faqat "yangi" holatdagi buyurtmani tahrirlaydi
return $f->id === $b->mijozId
&& $b->holat() === BuyurtmaHolati::Yangi;
}
public function bekorQilaOladimi(Foydalanuvchi $f, Buyurtma $b): bool
{
if (!$this->korishiMumkinmi($f, $b)) {
return false;
}
if (!$b->holat()->bekorQilishMumkinmi()) {
return false;
}
return $f->huquqBormi(Huquq::BuyurtmaBekorQilish)
|| $f->id === $b->mijozId;
}
}
Insecure Direct Object Reference - identifikatorni o'zgartirib, boshqaning ma'lumotini ko'rish.
GET /api/buyurtmalar/1042 ← o'zimniki
GET /api/buyurtmalar/1043 ← boshqaniki, lekin ochiladi!
Bu zaiflik veb-ilovalardagi eng keng tarqalgan muammolardan biri.
Sabab: dasturchi "foydalanuvchi bunday URL ni bilmaydi" deb o'ylaydi.
// XAVFLI
public function korsating(int $id): Buyurtma
{
return $this->repozitoriy->toping($id);
}
// XAVFSIZ - variant 1: siyosat orqali
public function korsating(int $id, Foydalanuvchi $joriy): Buyurtma
{
$buyurtma = $this->repozitoriy->toping($id)
?? throw new TopilmadiIstisnosi();
if (!$this->siyosat->korishiMumkinmi($joriy, $buyurtma)) {
throw new RuxsatYoq();
}
return $buyurtma;
}
// XAVFSIZ - variant 2: so'rovda filtrlash
public function korsating(int $id, Foydalanuvchi $joriy): Buyurtma
{
return $this->repozitoriy->foydalanuvchiUchunToping($id, $joriy)
?? throw new TopilmadiIstisnosi();
}
// Repozitoriyda huquq tekshiruvi
public function foydalanuvchiUchunToping(int $id, Foydalanuvchi $f): ?Buyurtma
{
if ($f->huquqBormi(Huquq::BuyurtmaKorish)) {
$stmt = $this->pdo->prepare('SELECT * FROM buyurtmalar WHERE id = ?');
$stmt->execute([$id]);
} else {
$stmt = $this->pdo->prepare(
'SELECT * FROM buyurtmalar WHERE id = ? AND mijoz_id = ?'
);
$stmt->execute([$id, $f->id]);
}
$qator = $stmt->fetch();
return $qator ? Buyurtma::qatordan($qator) : null;
}
So'rovga filtr qo'shilgan bo'lsa, uni unutib bo'lmaydi.
Birinchi variantda dasturchi siyosat chaqiruvini unutishi mumkin -
va zaiflik paydo bo'ladi.
Xavfsiz standart prinsipi: to'g'ri yo'l eng oson yo'l bo'lsin.
// Ma'lumot beradi: "bunday buyurtma bor, lekin sizga ruxsat yo'q"
throw new RuxsatYoq(); // 403
// Ma'lumot bermaydi
throw new Topilmadi(); // 404
Ba'zi hollarda 404 xavfsizroq: hujumchi qaysi identifikatorlar mavjudligini bilmaydi.
Lekin ichki tizimlarda 403 tushunarliroq. Kontekstga qarab tanlang.
ABAC - atributlarga asoslangan #
Murakkabroq qoidalar uchun.
final class HujjatSiyosati
{
public function korishiMumkinmi(
Foydalanuvchi $f,
Hujjat $h,
Kontekst $k,
): bool {
// Maxfiylik darajasi
if ($h->maxfiylik->darajasi() > $f->ruxsatDarajasi) {
return false;
}
// Bo'lim
if ($h->faqatBolimUchun && $h->bolimId !== $f->bolimId) {
return false;
}
// Vaqt cheklovi
if ($h->maxfiylik === Maxfiylik::Maxfiy && !$k->ishVaqtimi()) {
return false;
}
// Joylashuv
if ($h->faqatOfisdan && !$k->ofisTarmogidanmi()) {
return false;
}
// Qurilma
if ($h->maxfiylik->darajasi() >= 3 && !$k->qurilmaIshonchlimi()) {
return false;
}
return true;
}
}
ABAC kuchli, lekin murakkab.
Ko'p loyihalar uchun RBAC + resurs egaligini tekshirish yetarli.
ABAC ga faqat haqiqiy ehtiyoj bo'lganda o'ting: murakkab tashkiliy tuzilma, qonuniy talablar, ko'p o'lchamli qoidalar.
Eng kam huquq prinsipi #
-- YOMON: ilova root sifatida ishlaydi
GRANT ALL PRIVILEGES ON *.* TO 'ilova'@'%';
-- YAXSHI: faqat kerakli huquqlar
CREATE USER 'dokon_app'@'10.0.1.%' IDENTIFIED BY '...';
GRANT SELECT, INSERT, UPDATE, DELETE ON dokon.* TO 'dokon_app'@'10.0.1.%';
-- Migratsiyalar uchun alohida
CREATE USER 'dokon_migrate'@'10.0.1.%' IDENTIFIED BY '...';
GRANT ALL PRIVILEGES ON dokon.* TO 'dokon_migrate'@'10.0.1.%';
-- Hisobotlar uchun faqat o'qish
CREATE USER 'dokon_report'@'10.0.1.%' IDENTIFIED BY '...';
GRANT SELECT ON dokon.* TO 'dokon_report'@'10.0.1.%';
SQL inyeksiya topilsa:
| Huquq | Hujumchi nima qila oladi |
|---|---|
ALL PRIVILEGES | Bazani o'chirish, fayl o'qish, kod bajarish |
SELECT, INSERT, UPDATE, DELETE | Faqat ma'lumot o'qish va o'zgartirish |
SELECT | Faqat o'qish |
Huquqni cheklash zaiflikni yo'q qilmaydi, lekin zararni sezilarli kamaytiradi.
# Linux da xizmatni cheklangan foydalanuvchi ostida ishlatish
sudo useradd -r -s /usr/sbin/nologin dokon
sudo chown -R dokon:dokon /var/www/dokon
# systemd bilan qo'shimcha cheklovlar
# /etc/systemd/system/dokon.service
[Service]
User=dokon
Group=dokon
WorkingDirectory=/var/www/dokon
# Xavfsizlik cheklovlari
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/www/dokon/storage
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
MemoryDenyWriteExecute=true
Vazifalarni ajratish #
final class TolovServisi
{
public function yarating(TolovSorovi $sorov, Foydalanuvchi $kim): Tolov
{
if (!$kim->huquqBormi(Huquq::TolovYaratish)) {
throw new RuxsatYoq();
}
return $this->repozitoriy->saqlang(
Tolov::yangi($sorov, yaratgan: $kim->id)
);
}
public function tasdiqlang(int $tolovId, Foydalanuvchi $kim): void
{
if (!$kim->huquqBormi(Huquq::TolovTasdiqlash)) {
throw new RuxsatYoq();
}
$tolov = $this->repozitoriy->toping($tolovId)
?? throw new TolovTopilmadi($tolovId);
// Muhim qoida: yaratgan odam tasdiqlay olmaydi
if ($tolov->yaratganId === $kim->id) {
throw new OziniTasdiqlashTaqiqlanadi();
}
if ($tolov->summa > self::IKKI_TASDIQ_CHEGARASI
&& count($tolov->tasdiqlaganlar) < 1) {
$tolov->tasdiqQoshing($kim->id);
$this->repozitoriy->saqlang($tolov);
return; // ikkinchi tasdiq kutiladi
}
$tolov->tasdiqlang($kim->id);
$this->repozitoriy->saqlang($tolov);
}
}
Bir odam to'lovni ham yaratib, ham tasdiqlay olmasin.
Bu klassik buxgalteriya prinsipi bo'lib, IT tizimlarida ham qo'llanadi.
Katta summalar uchun ikki tasdiq talab qilinishi mumkin.
Huquqlarni tekshirish joyi #
// 1. Interfeys - qulaylik uchun
if ($foydalanuvchi->huquqBormi(Huquq::BuyurtmaBekorQilish)) {
echo '<button>Bekor qilish</button>';
}
// 2. Kontroller - kirish nuqtasida
public function bekorQiling(int $id, Sorov $sorov): Javob
{
$this->huquqTekshiring(Huquq::BuyurtmaBekorQilish);
// ...
}
// 3. Servis - biznes mantiq bilan
public function bekorQiling(int $id, Foydalanuvchi $kim): void
{
if (!$this->siyosat->bekorQilaOladimi($kim, $buyurtma)) {
throw new RuxsatYoq();
}
// ...
}
// 4. Repozitoriy - so'rovda filtr
public function foydalanuvchiningBuyurtmalari(Foydalanuvchi $f): array
{
// Filtr har doim qo'llanadi
}
<!-- Tugma yashirilgan, lekin API ochiq -->
{% if huquq('buyurtma.bekor') %}
<button data-id="42">Bekor qilish</button>
{% endif %}
Hujumchi to'g'ridan-to'g'ri API ga so'rov yuboradi:
curl -X POST https://dokon.uz/api/buyurtmalar/42/bekor \
-H "Authorization: Bearer ..."
Har doim serverda tekshiring.
Nol ishonch modeli #
- Har bir so'rovni tekshiring - joylashuvidan qat'i nazar
- Eng kam huquq - har bir sessiya uchun
- Buzilish sodir bo'lgan deb faraz qiling
- Barcha trafikni shifrlang - ichki tarmoqda ham
- Doimiy tekshiring - bir marta emas, har safar
- Mikrosegmentatsiya - xizmatlar orasida ham cheklov
Xizmatlararo autentifikatsiya #
// Ichki xizmat chaqiruvi ham autentifikatsiya talab qiladi
final class IchkiApiMijozi
{
public function soraing(string $yol, array $malumot): array
{
$vaqt = time();
$tana = json_encode($malumot);
$imzo = hash_hmac('sha256', "{$vaqt}.{$yol}.{$tana}", $this->sir);
return $this->http->post($this->asosiyUrl . $yol, [
'sarlavhalar' => [
'X-Xizmat-Nomi' => $this->xizmatNomi,
'X-Vaqt' => (string) $vaqt,
'X-Imzo' => $imzo,
],
'tana' => $tana,
]);
}
}
// Qabul qiluvchi tomonda
final class XizmatAutentifikatsiyasi
{
private const MAKS_FARQ = 300; // 5 daqiqa
public function tekshiring(Sorov $sorov): string
{
$xizmat = $sorov->sarlavha('X-Xizmat-Nomi') ?? throw new RuxsatYoq();
$vaqt = (int) $sorov->sarlavha('X-Vaqt');
$imzo = $sorov->sarlavha('X-Imzo') ?? throw new RuxsatYoq();
// Qayta ishlatish hujumidan himoya
if (abs(time() - $vaqt) > self::MAKS_FARQ) {
throw new VaqtFarqiJudaKatta();
}
$sir = $this->sirlar->xizmatUchun($xizmat)
?? throw new NomalumXizmat($xizmat);
$kutilgan = hash_hmac(
'sha256',
"{$vaqt}.{$sorov->yol()}.{$sorov->xomTana()}",
$sir,
);
if (!hash_equals($kutilgan, $imzo)) {
throw new NotogriImzo();
}
return $xizmat;
}
}
Imzo faqat 5 daqiqa amal qiladi.
Hujumchi so'rovni ushlab qolsa ham, uni keyinroq qayta yubora olmaydi.
Huquqlarni audit qilish #
-- Kimda qanday huquq bor?
SELECT
f.email,
GROUP_CONCAT(DISTINCT r.nomi ORDER BY r.nomi) AS rollar,
f.oxirgi_kirish,
DATEDIFF(NOW(), f.oxirgi_kirish) AS kirmagan_kun
FROM foydalanuvchilar f
LEFT JOIN foydalanuvchi_rollari fr ON fr.foydalanuvchi_id = f.id
LEFT JOIN rollar r ON r.id = fr.rol_id
WHERE f.faol = 1
GROUP BY f.id, f.email, f.oxirgi_kirish
ORDER BY kirmagan_kun DESC;
-- Ortiqcha huquqlar: administrator, lekin 90 kun kirmagan
SELECT f.email, f.oxirgi_kirish
FROM foydalanuvchilar f
JOIN foydalanuvchi_rollari fr ON fr.foydalanuvchi_id = f.id
JOIN rollar r ON r.id = fr.rol_id
WHERE r.nomi = 'administrator'
AND (f.oxirgi_kirish IS NULL OR f.oxirgi_kirish < DATE_SUB(NOW(), INTERVAL 90 DAY));
Har chorakda:
- Barcha administrator huquqlarini qayta ko'rib chiqing
- Faol bo'lmagan hisoblarni bloklang
- Ishdan bo'shaganlarni o'chiring
- "Vaqtincha berilgan" huquqlarni tekshiring
- Har bir rol uchun so'rang: bu huquqlar hali ham kerakmi?
Huquqlar to'planishi - vaqt o'tishi bilan xodim ko'p huquq yig'ib oladi, lekin eskilari olib tashlanmaydi.
Audit jurnali #
final class AuditJurnali
{
public function yozing(
Foydalanuvchi $kim,
string $amal,
string $resursTuri,
?string $resursId,
array $tafsilotlar = [],
): void {
$this->repozitoriy->saqlang([
'foydalanuvchi_id' => $kim->id,
'email' => $kim->email,
'amal' => $amal,
'resurs_turi' => $resursTuri,
'resurs_id' => $resursId,
'tafsilotlar' => json_encode($tafsilotlar, JSON_UNESCAPED_UNICODE),
'ip' => $this->sorov->ip(),
'agent' => $this->sorov->agent(),
'vaqt' => new DateTimeImmutable(),
]);
}
}
$this->audit->yozing(
$joriy,
'buyurtma.bekor',
'buyurtma',
(string) $buyurtma->id,
['sabab' => $sabab, 'oldingi_holat' => $eskiHolat->value],
);
Hujumchi tizimga kirsa, birinchi ishi - izlarni o'chirish.
Himoya:
- Jurnal alohida serverga yuborilsin
- Ilova foydalanuvchisida
DELETEvaUPDATEhuquqi bo'lmasin - Faqat qo'shish (
append-only) - Yaxlitlikni tekshirish uchun zanjirli xesh
GRANT INSERT, SELECT ON dokon.audit_jurnali TO 'dokon_app'@'10.0.1.%';
-- DELETE va UPDATE berilmaydi
// Zanjirli xesh - o'zgartirishni aniqlash
final class YaxlitJurnal
{
public function yozing(array $yozuv): void
{
$oldingiXesh = $this->repozitoriy->oxirgiXesh() ?? str_repeat('0', 64);
$yozuv['oldingi_xesh'] = $oldingiXesh;
$yozuv['xesh'] = hash('sha256', $oldingiXesh . json_encode($yozuv));
$this->repozitoriy->qoshing($yozuv);
}
public function yaxlitmi(): bool
{
$oldingi = str_repeat('0', 64);
foreach ($this->repozitoriy->hammasi() as $yozuv) {
$xesh = $yozuv['xesh'];
unset($yozuv['xesh']);
if (!hash_equals($xesh, hash('sha256', $oldingi . json_encode($yozuv)))) {
return false;
}
$oldingi = $xesh;
}
return true;
}
}
RolvaHuquqenum larini yozing.huquqBormi()metodini amalga oshiring.BuyurtmaSiyosatiklassini yozing.- Loyihangizda IDOR zaifligini qidiring.
- Repozitoriyda filtrli so'rov variantini amalga oshiring.
- Baza foydalanuvchisi huquqlarini cheklang.
- systemd xizmat faylida xavfsizlik cheklovlarini qo'shing.
- Vazifalarni ajratish qoidasini amalga oshiring (yaratuvchi tasdiqlay olmasin).
- Audit jurnali yozing va uni faqat qo'shiladigan qiling.
- Zanjirli xesh bilan yaxlitlikni tekshiring.
Xulosa #
- Autentifikatsiya - kimligingiz; avtorizatsiya - nima qilishingiz mumkinligi.
- RBAC - ko'pchilik loyihalar uchun yetarli va sodda.
- Rolni emas, huquqni tekshiring - bu moslashuvchanroq.
- RBAC dan tashqari resurs egaligini ham tekshirish shart.
- IDOR - eng ko'p uchraydigan zaiflik.
- So'rovga filtr qo'shish siyosat chaqiruvidan ishonchliroq - uni unutib bo'lmaydi.
- Eng kam huquq: baza foydalanuvchisi, xizmat foydalanuvchisi, fayl huquqlari.
- Vazifalarni ajratish firibgarlikning oldini oladi.
- Interfeysda tugmani yashirish - himoya emas.
- Nol ishonch: ichki tarmoq ham ishonchsiz deb hisoblanadi.
- Huquqlarni muntazam ko'rib chiqing - ular to'planib boradi.
- Audit jurnali o'zgartirilmaydigan bo'lsin.
Keyingi bo'limda tarmoq xavfsizligini 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.