IlmHamroh
JavaScript Full-stack/27-qism. Ma'lumotlar bazasi chuqur: Postgres, ORM, Mongo, Redis11/65-dars21 daqiqa
Mundarija (32)

PostgreSQL rollar va huquqlar: GRANT, REVOKE va eng kam huquq

Qisqacha: Ilova bazaga superuser bilan ulanmasligi kerak. To'g'ri tizim — bir nechta rol: migratsiya roli (jadval yaratadi va o'zgartiradi), ilova roli (faqat SELECT/INSERT/UPDATE/DELETE, RLS ostida), hisobot roli (faqat o'qish, statement_timeout bilan). Huquqlar GRANT/REVOKE bilan beriladi, kelajakdagi jadvallar uchun — ALTER DEFAULT PRIVILEGES. Ehtiyot: SET ROLE rol sozlamalarini qo'llamaydi, REVOKE CONNECT faqat bitta bazaga ta'sir qiladi.

Bu darsda

  • CREATE ROLE, LOGIN, guruh rollari va a'zolikni tushuntirasiz.
  • Baza, sxema va jadval darajasidagi huquqlarni GRANT/REVOKE bilan boshqarasiz.
  • PostgreSQL 15 dagi public sxema o'zgarishini va ALTER DEFAULT PRIVILEGES ni bilasiz.
  • Read-only hisobot rolini to'g'ri qurasiz va SET ROLE tuzog'ini taniysiz.
  • Migratsiya roli va ilova roli nega alohida ekanini va egalik (owner) masalasini bilasiz.

Oldin bilishingiz kerak: psql chuqur, SQL tili va buyruq turlari (GRANT, SET ROLE bilan birinchi tanishuv), Row Level Security.

1. Nega bu kerak?

26-qismdan beri vazifalar-api bazaga postgres bilan ulanadi. postgres — superuser: u istalgan jadvalni o'chira oladi, RLS'ni chetlab o'tadi, server fayllarini o'qiy oladi. Agar API'da bitta SQL injection teshigi qolsa (Parametrli so'rovlar va SQL injection), hujumchi ham superuser bo'ladi.

Eng kam huquq (least privilege) tamoyili: har ishtirokchiga faqat o'z ishi uchun kerak bo'lgan huquq. O'xshatish: mehmonxona kartalari. Mehmon kartasi faqat o'z xonasini ochadi. Farrosh kartasi — hamma xonani, lekin seyfni emas. Direktor kartasi — hammasini. Mehmonga direktor kartasini bermaysiz — hatto u yaxshi odam bo'lsa ham: karta yo'qolishi mumkin.

Oldingi darsda RLS siyosatlarini yozdik va "ilova superuser bo'lsa, ular ishlamaydi" dedik. Bu darsda shu bo'shliqni yopamiz.

Bu darsdagi so'rovlarni biz ishga tushirdik: PostgreSQL 17.6, kanon holat 11 sxemasi (106-rollar.sql qo'llangan). Kanon (PostgreSQL 18.6) bilan chiqish bir xil (kanon o'lchovi, IlmHamroh, 2026-10).

2. Rol nima

2.1 Foydalanuvchi va guruh — bitta tushuncha

Postgres'da rol (role) — ham foydalanuvchi, ham guruh. Farq atributlarda:

Atribut Ma'nosi
LOGIN bu rol bilan ulanish mumkin ("foydalanuvchi")
NOLOGIN ulanib bo'lmaydi — huquqlar yig'indisi ("guruh")
SUPERUSER hamma tekshiruvdan ozod
BYPASSRLS RLS'ni chetlab o'tadi
CONNECTION LIMIT n bir vaqtda ko'pi bilan n ulanish
IN ROLE g yaratilganda g guruhiga a'zo

Rollar klaster darajasida — bitta serverdagi hamma bazalar uchun umumiy. Huquqlar esa har baza va har obyektga alohida.

2.2 Kanondagi to'rt rol

Kanon to'rt rolli tizim qurgan:

sql
SELECT rolname, rolsuper, rolinherit, rolcanlogin, rolbypassrls,
       rolconnlimit, rolconfig
FROM pg_roles WHERE rolname LIKE 'vazifalar_%' ORDER BY 1;
text
       rolname        | rolsuper | rolinherit | rolcanlogin | rolbypassrls | rolconnlimit |                        rolconfig
----------------------+----------+------------+-------------+--------------+--------------+----------------------------------------------------------
 vazifalar_api        | f        | t          | t           | f            |           40 |
 vazifalar_hisobot    | f        | t          | t           | t            |            5 | {statement_timeout=30s,default_transaction_read_only=on}
 vazifalar_ilova      | f        | t          | f           | f            |           -1 |
 vazifalar_migratsiya | f        | t          | t           | f            |           -1 |
(4 rows)

Diagrammada kim nima qiladi:

flowchart LR
  M["vazifalar_migratsiya"] -->|"CREATE, ALTER"| S["public sxema"]
  I["vazifalar_ilova<br/>NOLOGIN guruh"] -->|"DML + RLS"| J["jadvallar"]
  A["vazifalar_api<br/>LOGIN, 40 ulanish"] -->|"a'zo"| I
  H["vazifalar_hisobot<br/>read-only"] -->|"SELECT, BYPASSRLS"| J
  S --> J
  • vazifalar_ilova — guruh (NOLOGIN). Barcha DML huquqlari va RLS siyosatlari unga berilgan (Row Level Security darsidagi 105).
  • vazifalar_api — API jarayoni shu bilan ulanadi. O'zida huquq yo'q — u vazifalar_ilova a'zosi va rolinherit = t (a'zolik huquqlarini meros oladi).
  • vazifalar_hisobot — analitika uchun: hammasini o'qiydi (BYPASSRLS — ongli qaror, pastda), yoza olmaydi, ko'pi bilan 5 ulanish, so'rov 30 soniyadan oshmaydi.
  • vazifalar_migratsiya — sxemani o'zgartiradi. Ilova u bilan ishlamaydi.

Nega guruh va login alohida? Ertaga ikkinchi xizmat paydo bo'lsa (masalan, fon vazifalari ishchisi — 29-qism), unga vazifalar_ishchi LOGIN IN ROLE vazifalar_ilova yetadi — huquqlarni qayta yozish shart emas. Har xizmat o'z nomi bilan ulanadi — log'da "kim nima qildi" ko'rinadi.

A'zolik katalogda:

sql
SELECT r.rolname AS rol, m.rolname AS azosi
FROM pg_auth_members a
JOIN pg_roles r ON r.oid = a.member
JOIN pg_roles m ON m.oid = a.roleid
WHERE r.rolname LIKE 'vazifalar_%' ORDER BY 1;
text
      rol      |      azosi
---------------+-----------------
 vazifalar_api | vazifalar_ilova
(1 row)

Tekshirib ko'ring: Nega vazifalar_ilova ni NOLOGIN qilish kerak edi? LOGIN bo'lsa nima yomon?

Javob

Guruh — huquqlar to'plami, uning nomi bilan hech kim ulanmasligi kerak. LOGIN bo'lsa, unga parol berilishi, parol sizishi va kimdir "umumiy" hisob bilan ulanishi mumkin — log'da kim ekanini bilib bo'lmaydi. Kanon tekshirgan: psql -U vazifalar_ilova → role "vazifalar_ilova" is not permitted to log in.

3. Huquqlar uch qavatda

Biror jadvalga yetish uchun uch eshikdan o'tish kerak:

  1. Baza — CONNECT (ulanish), TEMPORARY (vaqtinchalik jadval), CREATE (sxema yaratish).
  2. Sxema — USAGE (ichidagi obyektlarni ko'rish), CREATE (obyekt yaratish).
  3. Obyekt — jadvalga SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER; sequence'ga USAGE; funksiyaga EXECUTE.

PUBLIC — "hamma rollar" degan maxsus nom. Sukut bo'yicha PUBLIC'da: har bazaga CONNECT va TEMPORARY, har funksiyaga EXECUTE. Kanon migratsiyasi bazadan PUBLIC'ni olib tashlaydi:

sql
DO $$ BEGIN
  EXECUTE format(
    'REVOKE CONNECT, TEMPORARY ON DATABASE %I FROM PUBLIC',
                 current_database());
  EXECUTE format(
    'GRANT CONNECT ON DATABASE %I TO vazifalar_migratsiya, '
    || 'vazifalar_api, vazifalar_hisobot', current_database());
END $$;

format('%I', …) — nomni identifikator sifatida xavfsiz qo'yadi (qo'shtirnoq kerak bo'lsa — qo'yadi). Baza nomi qat'iy yozilmagan — kanon xatosi shu yerda tuzatilgan: birinchi variantda vazifalar yozilgan edi, va migratsiya boshqa nomli bazada (tekshiruv uchun quriladigan tekshiruv) noto'g'ri bazaga ta'sir qilardi.

Jadval huquqlari katalogdan (\dp ning SQL shakli):

sql
SELECT table_name, grantee,
       string_agg(privilege_type, ',' ORDER BY privilege_type)
         AS huquqlar
FROM information_schema.role_table_grants
WHERE table_schema = 'public' AND grantee LIKE 'vazifalar_%'
GROUP BY 1, 2 ORDER BY 1, 2;
text
    table_name    |      grantee      |          huquqlar
------------------+-------------------+-----------------------------
 faol_vazifalar   | vazifalar_hisobot | SELECT
 faol_vazifalar   | vazifalar_ilova   | SELECT
 foydalanuvchilar | vazifalar_hisobot | SELECT
 foydalanuvchilar | vazifalar_ilova   | DELETE,INSERT,SELECT,UPDATE
 izohlar          | vazifalar_hisobot | SELECT
 izohlar          | vazifalar_ilova   | DELETE,INSERT,SELECT,UPDATE
 teglar           | vazifalar_hisobot | SELECT
 teglar           | vazifalar_ilova   | DELETE,INSERT,SELECT,UPDATE
 vazifa_tarixi    | vazifalar_hisobot | SELECT
 vazifa_tarixi    | vazifalar_ilova   | INSERT
 vazifa_teglari   | vazifalar_hisobot | SELECT
 vazifa_teglari   | vazifalar_ilova   | DELETE,INSERT,SELECT,UPDATE
 vazifalar        | vazifalar_hisobot | SELECT
 vazifalar        | vazifalar_ilova   | DELETE,INSERT,SELECT,UPDATE
(14 rows)

Ilovada TRUNCATE yo'q (butun jadvalni bir zumda tozalash — ilovaga kerak emas), vazifa_tarixi da faqat INSERT (trigger yozadi, ilova tarixni o'qimaydi va o'zgartirmaydi).

Bitta savol bilan tekshirish — has_*_privilege funksiyalari:

sql
SELECT has_database_privilege('vazifalar_api',
         current_database(), 'CONNECT') AS api_connect,
       has_database_privilege('public',
         current_database(), 'CONNECT') AS public_connect,
       has_schema_privilege('vazifalar_api', 'public', 'CREATE')
         AS api_create,
       has_schema_privilege('vazifalar_migratsiya', 'public',
         'CREATE')
         AS migratsiya_create;
text
 api_connect | public_connect | api_create | migratsiya_create
-------------+----------------+------------+-------------------
 t           | f              | f          | t
(1 row)

3.1 public sxema va PostgreSQL 15

PostgreSQL 14 gacha har qanday foydalanuvchi public sxemada jadval yarata olardi (PUBLIC'da CREATE bor edi). Bu xavfli edi: huquqsiz foydalanuvchi public ga lower nomli funksiya yoki boshqa jadval nomini "ekib", boshqalarni aldashi mumkin edi — Funksiyalar va PL/pgSQL darsidagi search_path hujumining qarindoshi. PostgreSQL 15 dan public sxemada CREATE faqat baza egasida. Kanon baribir aniq yozgan — eski versiyadan ko'chirilgan bazada eski huquq qolgan bo'lishi mumkin:

sql
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
GRANT USAGE, CREATE ON SCHEMA public TO vazifalar_migratsiya;
GRANT USAGE ON SCHEMA public TO vazifalar_hisobot;

Natija nspacl da (sxemaning huquqlar ro'yxati):

sql
SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'public';
text
 nspname |                                                                                      nspacl
---------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 public  | {pg_database_owner=UC/pg_database_owner,=U/pg_database_owner,vazifalar_ilova=U/pg_database_owner,vazifalar_migratsiya=UC/pg_database_owner,vazifalar_hisobot=U/pg_database_owner}
(1 row)

O'qish: rol=huquqlar/kim_bergan. U — USAGE, C — CREATE. =U (rol nomisiz) — PUBLIC: faqat ko'rish. C faqat egasida va migratsiya rolida.

4. API roli amalda

SET ROLE bilan API bo'lamiz (session_user — haqiqiy ulangan, current_user — hozir kim nomidan ishlayapmiz). Chiqishda har buyruqning javobiga qarang: kundalik ish (o'qish, UPDATE) o'tadi, "xavfli" to'rt buyruq rad etiladi:

sql
SET ROLE vazifalar_api;
SELECT current_user, session_user;
BEGIN;
SET LOCAL ilova.foydalanuvchi_id = '2';
SELECT count(*) AS dilnoza_vazifalari FROM vazifalar;
UPDATE vazifalar SET bajarildi = true,
  bajarilgan_vaqt = '2026-10-24 12:00+05'
WHERE id = (SELECT min(id) FROM vazifalar WHERE NOT bajarildi)
RETURNING id;
ROLLBACK;
CREATE TABLE api_yaratdi (x int);
DROP TABLE izohlar;
TRUNCATE vazifalar;
SELECT count(*) FROM vazifa_tarixi;
RESET ROLE;
text
SET
 current_user  | session_user
---------------+--------------
 vazifalar_api | postgres
(1 row)

BEGIN
SET
 dilnoza_vazifalari
--------------------
                  6
(1 row)

 id
----
 14
(1 row)

UPDATE 1
ROLLBACK
ERROR:  permission denied for schema public
LINE 1: CREATE TABLE api_yaratdi (x int);
                     ^
ERROR:  must be owner of table izohlar
ERROR:  permission denied for table vazifalar
ERROR:  permission denied for table vazifa_tarixi
RESET

Nima bo'ldi?

Buyruq Natija Sabab
SELECT, UPDATE o'tdi vazifalar_ilova dan meros DML
CREATE TABLE rad sxemada CREATE yo'q
DROP TABLE rad rol jadval egasi emas
TRUNCATE, tarixni o'qish rad huquq berilmagan
  • Kundalik ish — ishlaydi, RLS ostida (Dilnozaning 6 vazifasi).
  • CREATE TABLE — sxemada CREATE yo'q.
  • DROP TABLE — "must be owner": jadvalni faqat uning egasi o'chira oladi; bu huquq GRANT bilan berilmaydi.
  • TRUNCATE — berilmagan.
  • Tarixni o'qish — faqat INSERT berilgan.

Endi SQL injection bilan API'ni buzgan hujumchi ham shu cheklovlarda: jadvalni o'chira olmaydi, tarixni o'qiy olmaydi, begona foydalanuvchining qatorlarini ko'rmaydi (RLS).

5. Hisobot roli va SET ROLE tuzog'i

5.1 ALTER ROLE … SET

Kanon hisobot roliga ikki sozlama bergan:

sql
ALTER ROLE vazifalar_hisobot SET statement_timeout = '30s';
ALTER ROLE vazifalar_hisobot SET default_transaction_read_only = on;

statement_timeout — og'ir analitik so'rov 30 soniyadan keyin bekor bo'ladi va bazani band qilib qo'ymaydi. default_transaction_read_only — har tranzaksiya sukut bo'yicha faqat o'qish uchun.

5.2 SET ROLE sozlamalarni qo'llamaydi

Kanon topilmasi. SET ROLE bilan hisobot bo'lamiz va sozlamani tekshiramiz:

sql
SET ROLE vazifalar_hisobot;
SHOW default_transaction_read_only;
SELECT count(*) AS hisobot_hammasini_koradi FROM vazifalar;
UPDATE vazifalar SET matn = 'x' WHERE id = 1;
RESET ROLE;
text
SET
 default_transaction_read_only
-------------------------------
 off
(1 row)

 hisobot_hammasini_koradi
--------------------------
                       48
(1 row)

ERROR:  permission denied for table vazifalar
RESET

off! ALTER ROLE … SET sozlamalari faqat ulanish paytida (LOGIN) o'qiladi, SET ROLE da emas. UPDATE baribir to'xtadi — lekin GRANT yo'qligi sababli (permission denied), read-only sababli emas.

Haqiqiy ulanish bilan (psql'da \c - rol — shu bazaga boshqa rol bilan qayta ulanish; \set QUIET on faqat ulanish xabarini yashiradi):

sql
\set QUIET on
\c - vazifalar_hisobot
\set QUIET off
SELECT current_user, session_user;
SHOW default_transaction_read_only;
SHOW statement_timeout;
UPDATE vazifalar SET matn = 'x' WHERE id = 1;
text
   current_user    |   session_user
-------------------+-------------------
 vazifalar_hisobot | vazifalar_hisobot
(1 row)

 default_transaction_read_only
-------------------------------
 on
(1 row)

 statement_timeout
-------------------
 30s
(1 row)

ERROR:  cannot execute UPDATE in a read-only transaction

Endi sozlamalar ishladi, xato ham boshqa: "faqat o'qish tranzaksiyasida UPDATE bajarib bo'lmaydi". Ikki himoya qatlami: read-only sessiya va GRANT yo'q. Kanon pg_sleep(31) bilan timeout'ni ham ko'rgan: canceling statement due to statement timeout.

Saboq: rol sozlamalarini haqiqiy login bilan sinang; SET ROLE — huquqlarni tekshirish uchun, sozlamalarni emas.

5.3 Nega hisobotga BYPASSRLS

Hisobot "bugun nechta vazifa yaratildi (hammaniki)" kabi savollarga javob beradi — u hamma qatorni ko'rishi kerak. Ikki yo'l bor edi: hisobot uchun alohida siyosatlar yoki BYPASSRLS. Kanon ikkinchisini ongli tanlagan va migratsiya izohida yozgan. Narxi: bu rol sizsa — hamma ma'lumot ochiq. Shuning uchun u read-only, 5 ulanish, alohida parol va faqat ichki tarmoqdan.

Tekshirib ko'ring: Hisobot roli default_transaction_read_only = on bilan ulandi. U SET default_transaction_read_only = off; qilib, keyin UPDATE qila oladimi?

Javob

Sozlamani o'chira oladi — bu sessiya sozlamasi, foydalanuvchi o'zgartirishi mumkin. Lekin UPDATE baribir o'tmaydi: hisobot rolida jadvalga UPDATE huquqi (GRANT) yo'q. Shuning uchun read-only sozlama — qulaylik va xato oldini olish, haqiqiy himoya — GRANT. Ikkalasi birga.

6. Migratsiya roli va egalik

6.1 "must be owner"

Migratsiya roli jadval yaratadi, lekin postgres yaratgan jadvalni o'zgartira olmaydi:

sql
SET ROLE vazifalar_migratsiya;
CREATE TABLE migratsiya_sinov (id int);
ALTER TABLE vazifalar ADD COLUMN x int;
SELECT grantee,
       string_agg(privilege_type, ',' ORDER BY privilege_type)
FROM information_schema.role_table_grants
WHERE table_name = 'migratsiya_sinov'
  AND grantee <> 'vazifalar_migratsiya'
GROUP BY 1 ORDER BY 1;
DROP TABLE migratsiya_sinov;
RESET ROLE;
text
SET
CREATE TABLE
ERROR:  must be owner of table vazifalar
      grantee      |         string_agg
-------------------+-----------------------------
 vazifalar_hisobot | SELECT
 vazifalar_ilova   | DELETE,INSERT,SELECT,UPDATE
(2 rows)

DROP TABLE
RESET

ALTER TABLE — faqat egasiga. Holat 11 da barcha jadvallar egasi — postgres (ularni 26-qismda superuser yaratgan). To'g'ri yechim: jadvallar egasi migratsiya roli bo'lsin (ALTER TABLE … OWNER TO vazifalar_migratsiya yoki bazani boshidan shu rol bilan qurish). Kanon buni keyingi bosqichga — migratsiya vositasi darslariga (Prisma Migrate chuqur) qoldirgan.

6.2 ALTER DEFAULT PRIVILEGES — kelajak uchun

Ikkinchi natijaga qarang: yangi migratsiya_sinov jadvali o'zi huquqlarni oldi — hech kim GRANT yozmadi. Bu default privileges:

sql
ALTER DEFAULT PRIVILEGES FOR ROLE vazifalar_migratsiya
  IN SCHEMA public
  GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO vazifalar_ilova;
ALTER DEFAULT PRIVILEGES FOR ROLE vazifalar_migratsiya
  IN SCHEMA public GRANT USAGE ON SEQUENCES TO vazifalar_ilova;
ALTER DEFAULT PRIVILEGES FOR ROLE vazifalar_migratsiya
  IN SCHEMA public GRANT SELECT ON TABLES TO vazifalar_hisobot;

"vazifalar_migratsiya kelajakda public da yaratadigan har jadvalga — avtomatik shu huquqlar". GRANT … ON ALL TABLES esa faqat hozir mavjud jadvallarga ta'sir qiladi. Ikkalasi kerak: biri o'tmish uchun, biri kelajak uchun. Unutilsa — har yangi migratsiyadan keyin API permission denied for table … bilan to'xtaydi (va bu odatda prod'da birinchi bo'lib ko'rinadi).

FOR ROLE muhim: default privileges faqat ko'rsatilgan rol yaratgan obyektlarga ishlaydi. Boshqa rol yaratgan jadval huquqsiz qoladi.

6.3 Kanon xatosi va "qo'llangan migratsiya o'zgarmaydi" qoidasi

Kanonning 106 faylidagi uchta qator dastlab FOR ROLE postgres bilan yozilgan edi. 27-qism oxiridagi Testcontainers testlari (Testcontainers darsida; sinov konteynerida superuser nomi test) xatoni topdi: role "postgres" does not exist — migratsiya superuser nomi boshqa bo'lgan har serverda (masalan, managed bazada) yiqilardi. Tuzatish — FOR ROLE CURRENT_USER ("migratsiyani bajarayotgan rol"):

sql
ALTER DEFAULT PRIVILEGES FOR ROLE CURRENT_USER IN SCHEMA public
  GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO vazifalar_ilova;

Qoida bor edi: qo'llangan migratsiya fayli o'zgartirilmaydi — o'zgarish yangi fayl bilan. Bu yerda istisno qilindi va bu bosh muharrir qarori bilan qabul qilindi. Sabab: yangi bazada eski 106 baribir role "postgres" does not exist da yiqiladi — keyingi "tuzatuvchi" fayl unga yetib bormaydi. Kurs hali nashr qilinmagan, ya'ni hech qayerda eski 106 qo'llanmagan. Real loyihada (fayl prod'da qo'llangan bo'lsa) bunday istisno bo'lmaydi — u holda yangi migratsiya va eski bazalar uchun alohida tuzatish yo'li tanlanadi (Migratsiya tushunchasi darsida).

7. Yana bir topilma: REVOKE CONNECT faqat bitta bazada

Kanon haqiqiy login bilan tekshirgan: vazifalar_api boshqa bazalarga ham ulana oldi. Biz o'z klasterimizda takrorladik — h07 — boshqa holat bazasi, 106 u yerda qo'llanmagan:

bash
psql -h 127.0.0.1 -p 5552 -U vazifalar_api -d h07 -c "SELECT 1"
text
 ?column?
----------
        1
(1 row)

Sabab: REVOKE CONNECT … FROM PUBLIC faqat vazifalar bazasida qilingan. Boshqa bazalarda PUBLIC'ning sukut CONNECT huquqi qoldi, rollar esa klaster bo'ylab umumiy. Yechimlar: har bazada REVOKE CONNECT … FROM PUBLIC, va asosiysi — pg_hba.conf da "qaysi rol qaysi bazaga qayerdan ulanadi" qoidasi (Postgres arxitekturasi va asosiy sozlamalar darsida).

8. Parollar haqida

Kanon (va bizning lokal klaster) trust rejimida — parolsiz. Bu faqat lokal tajriba uchun. Serverda:

sql
-- serverda, migratsiyada EMAS — parol fayl tarixiga tushadi
CREATE ROLE vazifalar_api LOGIN PASSWORD '…' IN ROLE vazifalar_ilova;
  • Parol hech qachon migratsiya fayliga yoki git'ga yozilmaydi — u .env da yoki sirlar xizmatida. Rolni migratsiya yaratadi, parolni administrator alohida qo'yadi (ALTER ROLE … PASSWORD, psql'dagi \password — u parolni terminalda yashiradi va log'ga ochiq yozmaydi).
  • Saqlash algoritmi — scram-sha-256 (PostgreSQL 14 dan sukut); eski md5 — ishlatmang.
  • DATABASE_URL da har xizmat o'z roli bilan: API — vazifalar_api, migratsiya — vazifalar_migratsiya. Bu kursda parolli ulanish sinalmagan (trust); tafsilot — hujjatda.

9. Ko'p uchraydigan xatolar

9.1 Ilova superuser bilan ulangan

RLS ishlamaydi, SQL injection — butun server. Ilova — LOGIN rol, guruh a'zosi, faqat DML.

9.2 GRANT … ON ALL TABLES dan keyin yangi jadval huquqsiz

permission denied for table … faqat yangi jadvalda. ALTER DEFAULT PRIVILEGES FOR ROLE <jadval yaratuvchi>.

9.3 SET ROLE bilan sozlamani tekshirish

ALTER ROLE … SET ko'rinmaydi. Haqiqiy login (psql -U, \c - rol).

9.4 Sequence'ni unutish

INSERT da permission denied for sequence … (identity ustun ham sequence ishlatadi). GRANT USAGE ON SEQUENCES (yoki default privileges'da).

9.5 Migratsiya va ilova bitta rol

Ilova jadvalni o'chira oladi. Ikki rol; ilova jadval egasi emas.

10. Mashqlar

Mashqlar o'z rollaringiz bilan. Rollar klaster darajasida — oxirida o'chiring (DROP OWNED BY rolning huquqlari va obyektlarini tozalaydi).

1-mashq (oson): Faqat o'qiydigan rol

m11_menyu (nom text, narx int) jadvalini yarating. Unga osh (35 000) va manti (30 000) ni qo'shing. m11_oquvchi rolini yarating. Unga faqat SELECT bering. SET ROLE bilan: menyuni o'qing (narxlar yig'indisi), keyin UPDATE ga urining — xatoni ushlash uchun Funksiyalar va PL/pgSQL darsidagi kabi EXCEPTION WHEN insufficient_privilege li yordamchi funksiya yozing.

Yechim
sql
CREATE ROLE m11_oquvchi;
CREATE TABLE m11_menyu (nom text, narx int);
INSERT INTO m11_menyu VALUES ('osh', 35000), ('manti', 30000);
GRANT SELECT ON m11_menyu TO m11_oquvchi;
CREATE FUNCTION m11_urin() RETURNS text LANGUAGE plpgsql AS $$
BEGIN
  UPDATE m11_menyu SET narx = 0;
  RETURN 'o''zgardi';
EXCEPTION WHEN insufficient_privilege THEN
  RETURN 'rad: ' || SQLERRM;
END $$;
SET ROLE m11_oquvchi;
SELECT 'jami: ' || sum(narx) FROM m11_menyu;
SELECT m11_urin();
RESET ROLE;
DROP FUNCTION m11_urin;
DROP TABLE m11_menyu;
DROP ROLE m11_oquvchi;

Biz ishga tushirdik (psql -X -q -A -t, PostgreSQL 17.6):

text
jami: 65000
rad: permission denied for table m11_menyu

Funksiya SECURITY DEFINER emas — chaqiruvchi (m11_oquvchi) huquqi bilan ishladi, shuning uchun UPDATE rad etildi.

2-mashq (o'rta): Guruh va a'zo

m11_kassa guruhini (NOLOGIN) yarating, unga m11_buyurtma (id int GENERATED ALWAYS AS IDENTITY, taom text) jadvaliga SELECT, INSERT va sequence'ga USAGE bering. m11_kassir (LOGIN IN ROLE m11_kassa) — o'zida huquq yo'q. SET ROLE m11_kassir bilan buyurtma qo'shing va soni chiqaring. Keyin a'zolikni olib tashlang (REVOKE m11_kassa FROM m11_kassir) va has_table_privilege('m11_kassir', 'm11_buyurtma', 'INSERT') ni chiqaring.

Yechim
sql
CREATE ROLE m11_kassa NOLOGIN;
CREATE ROLE m11_kassir LOGIN IN ROLE m11_kassa;
CREATE TABLE m11_buyurtma (
  id int GENERATED ALWAYS AS IDENTITY, taom text);
GRANT SELECT, INSERT ON m11_buyurtma TO m11_kassa;
GRANT USAGE ON SEQUENCE m11_buyurtma_id_seq TO m11_kassa;
SET ROLE m11_kassir;
INSERT INTO m11_buyurtma (taom) VALUES ('osh'), ('lag''mon');
SELECT 'buyurtmalar: ' || count(*) FROM m11_buyurtma;
RESET ROLE;
REVOKE m11_kassa FROM m11_kassir;
SELECT 'insert huquqi: '
  || has_table_privilege('m11_kassir', 'm11_buyurtma', 'INSERT');
DROP TABLE m11_buyurtma;
DROP ROLE m11_kassir;
DROP ROLE m11_kassa;

Biz ishga tushirdik (psql -X -q -A -t, PostgreSQL 17.6):

text
buyurtmalar: 2
insert huquqi: false

Kassir huquqni guruhdan meros oldi; a'zolik olingach — huquq ham ketdi. Xodim ishdan ketsa — bitta REVOKE (yoki DROP ROLE), jadvallarni birma-bir aylanish shart emas.

3-mashq (qiyin): Default privileges

m11_mig (jadval yaratuvchi) va m11_app rollarini yarating. m11_mig ga public sxemada CREATE bering va ALTER DEFAULT PRIVILEGES FOR ROLE m11_mig IN SCHEMA public GRANT SELECT ON TABLES TO m11_app qiling. Keyin: (1) SET ROLE m11_mig bilan m11_a jadvalini yarating; (2) superuser sifatida m11_b ni yarating. Ikkala jadval uchun m11_app ning SELECT huquqini chiqaring. Tushuntiring. Tozalash: DROP OWNED BY har rol uchun alohida, keyin DROP ROLE.

Yechim
sql
CREATE ROLE m11_mig;
CREATE ROLE m11_app;
GRANT USAGE, CREATE ON SCHEMA public TO m11_mig;
ALTER DEFAULT PRIVILEGES FOR ROLE m11_mig IN SCHEMA public
  GRANT SELECT ON TABLES TO m11_app;
SET ROLE m11_mig;
CREATE TABLE m11_a (x int);
RESET ROLE;
CREATE TABLE m11_b (x int);
SELECT t || ': ' || has_table_privilege('m11_app', t, 'SELECT')
FROM unnest(ARRAY['m11_a', 'm11_b']) AS t;
DROP TABLE m11_b;
DROP OWNED BY m11_mig;
DROP OWNED BY m11_app;
DROP ROLE m11_mig;
DROP ROLE m11_app;

Biz ishga tushirdik (psql -X -q -A -t, PostgreSQL 17.6):

text
m11_a: true
m11_b: false

Default privileges faqat m11_mig yaratgan jadvalga ishladi. Superuser yaratgan m11_b huquqsiz qoldi — shuning uchun hamma migratsiya bitta rol (migratsiya roli) bilan bajarilishi kerak. DROP OWNED BY — rolga tegishli obyektlarni (m11_a) va unga berilgan huquqlarni (sxemadagi CREATE, default privileges) o'chiradi; usiz DROP ROLE role … cannot be dropped because some objects depend on it beradi. Ikki rolni bitta buyruqda (DROP OWNED BY m11_mig, m11_app) tozalashga urinsak, PostgreSQL 17.6 da could not find tuple for default ACL … xatosini oldik (default privileges ikkala rolga bog'liq bo'lgani uchun) — alohida-alohida chaqirish ishladi.

11. Loyiha qadami: rollar migratsiyasi (holat 11)

Holat 09 → holat 11. Bitta fayl — sql/106-rollar.sql. Bu — P bosqichning (Postgres chuqur) yakuniy sxemasi. Kanon fayli to'liq; uzun qatorlar va izohlar telefon uchun bo'lindi, mazmuni aynan:

sql
-- 106-rollar.sql — 27-qism #11 (DB-72, holat 11): rollar va huquqlar
-- — eng kam huquq tamoyili.
--   vazifalar_migratsiya — sxemani o'zgartiradi (DDL), jadvallar
--     egasi bo'la oladi; ilova u bilan ISHLAMAYDI
--   vazifalar_ilova — guruh (NOLOGIN, 105): faqat DML, RLS ostida
--   vazifalar_api — LOGIN, vazifalar_ilova a'zosi (API jarayoni
--     shu bilan ulanadi)
--   vazifalar_hisobot — LOGIN, faqat o'qish (analitika, BI). RLS'ni
--     chetlab o'tishi kerak — BYPASSRLS (ongli qaror, izohda)
-- Parollar: lokal kanon konteyneri trust (parolsiz). Serverda:
-- CREATE ROLE … PASSWORD '…' (scram-sha-256) — sir .env da, bu faylda
-- EMAS.

DO $$ BEGIN
  IF NOT EXISTS (SELECT 1 FROM pg_roles
                 WHERE rolname = 'vazifalar_migratsiya') THEN
    CREATE ROLE vazifalar_migratsiya LOGIN; END IF;
  IF NOT EXISTS (SELECT 1 FROM pg_roles
                 WHERE rolname = 'vazifalar_api') THEN
    CREATE ROLE vazifalar_api LOGIN IN ROLE vazifalar_ilova; END IF;
  IF NOT EXISTS (SELECT 1 FROM pg_roles
                 WHERE rolname = 'vazifalar_hisobot') THEN
    CREATE ROLE vazifalar_hisobot LOGIN BYPASSRLS; END IF;
END $$;
-- Ulanish limiti: bitta rol butun max_connections'ni egallab olmasin
ALTER ROLE vazifalar_api CONNECTION LIMIT 40;
ALTER ROLE vazifalar_hisobot CONNECTION LIMIT 5;
-- Hisobot roli: uzun so'rov ilovani to'xtatmasin
ALTER ROLE vazifalar_hisobot SET statement_timeout = '30s';
ALTER ROLE vazifalar_hisobot SET default_transaction_read_only = on;

-- Bazaga ulanish: PUBLIC dan olib, aniq rollarga
-- baza nomi qat'iy yozilmaydi (holat boshqa nom bilan qurilishi
-- mumkin — tekshir.sh "tekshiruv" bazasi)
DO $$ BEGIN
  EXECUTE format(
    'REVOKE CONNECT, TEMPORARY ON DATABASE %I FROM PUBLIC',
                 current_database());
  EXECUTE format(
    'GRANT CONNECT ON DATABASE %I TO vazifalar_migratsiya, '
                 || 'vazifalar_api, vazifalar_hisobot',
                 current_database());
END $$;

-- Sxema: PG 15+ da public sxemaga CREATE PUBLIC'da yo'q (eski
-- versiyalarda bor edi — aniq yozamiz)
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
GRANT USAGE, CREATE ON SCHEMA public TO vazifalar_migratsiya;
GRANT USAGE ON SCHEMA public TO vazifalar_hisobot;

-- Hisobot: hamma jadval va view'ni o'qish (yozish yo'q)
GRANT SELECT ON ALL TABLES IN SCHEMA public TO vazifalar_hisobot;

-- Kelajakdagi jadvallar (migratsiyani bajarayotgan rol —
-- CURRENT_USER —
-- yoki migratsiya roli yaratganda) huquqlarni o'zi oladi.
-- KANON XATOSI TUZATILDI (27 holat 65, Testcontainers topdi): avval
-- "FOR ROLE postgres" edi — superuser boshqa nomli serverda
-- (Testcontainers "test", managed DB) migratsiya
-- `role "postgres" does
-- not exist` bilan yiqilardi.
ALTER DEFAULT PRIVILEGES FOR ROLE CURRENT_USER IN SCHEMA public
  GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO vazifalar_ilova;
ALTER DEFAULT PRIVILEGES FOR ROLE CURRENT_USER IN SCHEMA public
  GRANT USAGE ON SEQUENCES TO vazifalar_ilova;
ALTER DEFAULT PRIVILEGES FOR ROLE CURRENT_USER IN SCHEMA public
  GRANT SELECT ON TABLES TO vazifalar_hisobot;
ALTER DEFAULT PRIVILEGES FOR ROLE vazifalar_migratsiya
  IN SCHEMA public
  GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO vazifalar_ilova;
ALTER DEFAULT PRIVILEGES FOR ROLE vazifalar_migratsiya
  IN SCHEMA public
  GRANT USAGE ON SEQUENCES TO vazifalar_ilova;
ALTER DEFAULT PRIVILEGES FOR ROLE vazifalar_migratsiya
  IN SCHEMA public
  GRANT SELECT ON TABLES TO vazifalar_hisobot;

-- Funksiyalar: PUBLIC'da EXECUTE sukut bo'yicha bor — qoldiramiz
-- (trigger funksiyalari, uz_normal, joriy_foydalanuvchi kerak)

Oxirgi izohga e'tibor bering: funksiyalardagi PUBLIC EXECUTE ongli qoldirilgan — loyihada SECURITY DEFINER funksiya yo'q, qolganlari chaqiruvchi huquqi bilan ishlaydi va hech narsani "ochmaydi".

Commit:

bash
git add sql/106-rollar.sql
M="db: rollar — migratsiya, api (ilova a'zosi), hisobot (read-only);"
M+=" default privileges (106)"
git commit -m "$M"

Halol eslatma: rollar tayyor, lekin vazifalar-api hali postgres bilan ulanadi — DATABASE_URL ni vazifalar_api ga almashtirish API'ning har so'rovda set_config qilishini talab qiladi (Row Level Security darsidagi naqsh), "kim kirgan" esa 28-qismdagi autentifikatsiyadan keladi. Jadval egaligi (must be owner) — migratsiya vositasi darslarida.

12. Real ishda

  • Har prod bazada kamida ikki rol: migratsiya va ilova. Xavfsizlik auditida birinchi tekshiriladigan narsa — "ilova superuser bilan ulanmaydimi?".
  • Analitika va BI (Metabase, Superset kabi) — alohida read-only rol, ko'pincha replika bazaga (asosiy bazaning faqat o'qiladigan nusxasiga) ulanadi.
  • Managed bazalarda haqiqiy superuser umuman berilmaydi — provayderning "admin" roli bor; default privileges va egalik masalalari aynan shu yerda ko'p chiqadi (kanon xatosi — misol).
  • Intervyu: "Least privilege'ni bazada qanday qo'llaysiz?", "Yangi jadval qo'shilganda ilova huquqni qanday oladi?".

Xulosa

  • Rol — foydalanuvchi ham, guruh ham; LOGIN va NOLOGIN farqi; a'zolik huquqni meros qiladi (rolinherit).
  • Huquq uch qavatda: baza (CONNECT), sxema (USAGE, CREATE), obyekt (SELECT …); PUBLIC — "hamma".
  • PG 15+ da public sxemada CREATE faqat egasida; REVOKE CONNECT faqat o'sha bazaga ta'sir qiladi.
  • GRANT … ON ALL TABLES — hozirgilar uchun, ALTER DEFAULT PRIVILEGES FOR ROLE … — kelajakdagilar uchun.
  • ALTER ROLE … SET faqat haqiqiy login'da ishlaydi, SET ROLE da emas.
  • DROP/ALTER — faqat egasi; migratsiya roli jadvallar egasi bo'lishi kerak.

Keyingi dars: Postgres arxitekturasi va asosiy sozlamalar — process-per-connection, shared_buffers, work_mem, WAL, pg_hba.conf va ALTER SYSTEM.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
PostgreSQL rollar va huquqlar: GRANT, REVOKE va eng kam huquq — IlmHamroh