03 मार्च 2025·8 मिनट पढ़ने में

मल्टी-टेनेंट ऐप्स के लिए PostgreSQL रो-लेवल सिक्योरिटी पैटर्न

PostgreSQL रो-लेवल सिक्योरिटी सीखें—टेनेंट आइसोलेशन और रोल नियमों के व्यावहारिक पैटर्न ताकि एक्सेस सिर्फ ऐप पर नहीं बल्कि डेटाबेस में ही लागू हो।

मल्टी-टेनेंट ऐप्स के लिए PostgreSQL रो-लेवल सिक्योरिटी पैटर्न

व्यवसायिक ऐप्स में डेटाबेस-निर्धारित एक्सेस क्यों मायने रखता है

व्यवसायिक ऐप्स में अक्सर नियम होते हैं जैसे “यूज़र्स केवल अपनी कंपनी के रिकॉर्ड ही देख सकते हैं” या “केवल मैनेजर ही रिफंड को अप्रूव कर सकते हैं।” कई टीमें ये नियम UI या API में लागू करती हैं और मानती हैं कि यही काफी है। समस्या यह है कि डेटाबेस तक हर नया रास्ता डेटा लीक होने का नया मौका बन जाता है: एक आंतरिक एडमिन टूल, बैकग्राउंड जॉब, एनालिटिक्स क्वेरी, भूला हुआ एंडपॉइंट, या कोई बग जो एक चेक छोड़ देता है।

टेनेंट आइसोलेशन का मतलब है कि एक ग्राहक (टेनेंट) कभी भी दूसरे ग्राहक का डेटा पढ़ या बदल न सके, भले ही गलती से ही क्यों न हो। रोल-आधारित एक्सेस का अर्थ है कि एक ही टेनेंट के अंदर लोग अलग अधिकार रखते हैं, जैसे एजेंट, मैनेजर, या फाइनेंस। ये नियम बयान करने में आसान हैं, लेकिन जब ये कई जगह रहते हैं तो इन्हें पूरी तरह सुसंगत रखना मुश्किल होता है।

PostgreSQL row-level security (RLS) एक डेटाबेस फीचर है जो तय करता है कि कोई रिक्वेस्ट कौन-सी पंक्तियाँ देख या बदल सकती है। हर क्वेरी में सही WHERE क्लॉज़ याद रखने की उम्मीद करने के बजाय, डेटाबेस अपने आप पॉलिसीज़ लागू करता है।

RLS सब कुछ के लिए जादुई ढाल नहीं है। यह आपके स्कीमा को डिज़ाइन नहीं करेगा, ऑथेंटिकेशन की जगह नहीं लेगा, और किसी ऐसे व्यक्ति से सुरक्षा नहीं देगा जिसके पास पहले से ही शक्तिशाली डेटाबेस रोल (जैसे superuser) है। यह उन लॉजिक गलतियों से भी नहीं बचाएगा जैसे “कोई row अपडेट कर सकता है जिसे वह नहीं चुन सकता” जब तक आप पढ़ने और लिखने दोनों के लिए पॉलिसियाँ न लिखें।

जो आप पाते हैं वह एक मजबूत सुरक्षा नेट है:

  • हर कोड पाथ के लिए एक ही नियमों का सेट
  • नई फ़ीचर के आने पर कम “ओह!” पल
  • SQL में दिखाई देने वाले नियमों के कारण स्पष्ट ऑडिट
  • अगर API में बग छूट भी जाए तो बेहतर रक्षा

सेटअप की एक छोटी लागत है। आपको “यह यूज़र कौन है” और “किस टेनेंट से है” जैसी जानकारी डेटाबेस तक संगत तरीके से भेजनी होगी, और जैसे-जैसे ऐप बढ़े आपको पॉलिसीज़ बनाए रखनी होंगी। भुगतान इसका बड़ा होता है, खासकर SaaS और इन-हाउस टूल्स के लिए जहाँ संवेदनशील ग्राहक डेटा दांव पर होता है।

बिना जार्गन के रो-लेवल सिक्योरिटी की बुनियादी बातें

Row-Level Security (RLS) अपने आप फ़िल्टर कर देता है कि कौन-सी पंक्तियाँ किसी क्वेरी द्वारा देखी या बदली जा सकती हैं। हर स्क्रीन, API एंडपॉइंट, या रिपोर्ट को नियम याद रखने पर छोड़ने के बजाय, डेटाबेस उनके लिए नियम लागू करता है।

PostgreSQL RLS में आप पॉलिसीज़ लिखते हैं जो हर SELECT, INSERT, UPDATE, और DELETE पर चेक होती हैं। अगर पॉलिसी कहती है “यह यूजर केवल टेनेंट A की पंक्तियाँ देख सकता है,” तो एक भूला हुआ एडमिन पेज, एक नई क्वेरी, या जल्दी में किया गया हॉटफिक्स तब भी वही गार्ड्रेल पाएगा।

RLS, GRANT/REVOKE से अलग है। GRANT तय करता है कि कोई रोल तालिका तक कुल मिलाकर पहुँच सकता है या नहीं (या किसी कॉलम तक)। RLS तय करता है कि उस तालिका के भीतर कौन-सी पंक्तियाँ अनुमति प्राप्त हैं। व्यवहार में आप अक्सर दोनों का उपयोग करते हैं: तालिका तक पहुँच सीमित करने के लिए GRANT, और किस डेटा तक पहुँच है यह सीमित करने के लिए RLS।

यह असमान दुनिया में भी काम करता है। व्यूज़ आम तौर पर RLS का पालन करते हैं क्योंकि अंडरलाइनिंग टेबल एक्सेस अभी भी पॉलिसी को ट्रिगर करता है। जॉइन्स और सबक्वेरीज़ भी फ़िल्टर होती हैं, इसलिए यूज़र जॉइन करके किसी दूसरे का डेटा नहीं निकाल सकता। और पॉलिसी किसी भी क्लाइंट द्वारा चलाए जाने वाले क्वेरी पर लागू होती है: ऐप कोड, SQL कंसोल, बैकग्राउंड जॉब, या रिपोर्टिंग टूल।

RLS तब अच्छा मेल खाता है जब आपकी टेनेंट आइसोलेशन ज़रूरतें मजबूत हों, एक ही डेटा को कई तरीकों से क्वेरी किया जाता हो, या कई रोल्स एक ही तालिकाएँ शेयर करते हों (SaaS और इन-हाउस टूल्स में सामान्य)। यह बहुत छोटी ऐप्स के लिए जिनके पास एक भरोसेमंद बैकएंड है, या उस डेटा के लिए जो संवेदनशील नहीं है और कभी एक नियंत्रित सेवा से बाहर नहीं जाता — वहाँ ज़रूरी नहीं। लेकिन जैसे ही आपके पास एक से अधिक एंट्री पॉइंट होते हैं (एडमिन टूल्स, एक्सपोर्ट्स, BI, स्क्रिप्ट्स), RLS आमतौर पर अपना खर्च निकाल देता है।

टेनेंट्स, रोल्स और डेटा ओनरशिप की मैपिंग से शुरू करें

एक भी पॉलिसी लिखने से पहले यह स्पष्ट कर लें कि किसका क्या मालिकाना है। PostgreSQL RLS तब सबसे अच्छा काम करता है जब आपका डेटा मॉडल पहले से ही टेनेंट्स, रोल्स और ओनरशिप को दर्शाता हो।

टेनेंट्स से शुरू करें। अधिकतर SaaS ऐप्स में सबसे सरल नियम यह है: हर साझा तालिका जिसमें ग्राहक डेटा है उसमें एक tenant_id होता है। इसमें इनवॉइस जैसी “स्पष्ट” तालिकाएँ शामिल हैं, और साथ ही वे चीज़ें जो लोग भूल जाते हैं, जैसे attachments, comments, audit logs, और background jobs।

फिर वे रोल नाम रखें जो वास्तव में इस्तेमाल होते हैं। सेट को छोटा और मानवीय रखें: owner, manager, agent, read-only। ये बिज़नेस रोल्स हैं जिन्हें आप बाद में पॉलिसी चेक से मैप करेंगे (ये डेटाबेस रोल्स से अलग होते हैं)।

फिर तय करें कि रिकॉर्ड्स किसके ओनर हैं। कुछ तालिकाएँ एक ही यूज़र द्वारा ओनर की जाती हैं (उदा. निजी नोट)। अन्य टीम-ओनर वाली हो सकती हैं (उदा. साझा इनबॉक्स)। बिना योजना के दोनों को मिक्स करने से पॉलिसीज़ पढ़ने में कठिन और बायपास करने में आसान हो जाती हैं।

अपनी नियमावली को दस्तावेज़ करने का एक सरल तरीका यह है कि प्रत्येक तालिका के लिए वही प्रश्न जवाब करें:

  • टेनेंट सीमा क्या है (कौन-सा कॉलम इसे लागू करता है)?
  • कौन पंक्तियाँ पढ़ सकता है (रोल और ओनरशिप के अनुसार)?
  • कौन पंक्ति बना और अपडेट कर सकता है (और किन शर्तों में)?
  • कौन पंक्तियाँ डिलीट कर सकता है (अक्सर सबसे सख्त नियम)?
  • कौन से अपवाद की अनुमति है (सपोर्ट स्टाफ, ऑटोमेशन, एक्सपोर्ट)?

उदाहरण: “Invoices” में मैनेजर को सभी टेनेंट इनवॉइस देखने की अनुमति हो सकती है, एजेंट को असाइन किए गए कस्टमर के इनवॉइस देखने की, और read-only उपयोगकर्ता को केवल पढ़ने की अनुमति हो सकती है पर संपादन नहीं। पहले तय कर लें कि कौन से नियम सख्त होने चाहिए (टेनेंट आइसोलेशन, डिलीट्स) और कौन लचीला हो सकते हैं (मैनेजर के लिए अतिरिक्त विजिबिलिटी)। अगर आप AppMaster जैसे नो-कोड टूल में बनाते हैं, तो यह मैपिंग UI अपेक्षाओं और डेटाबेस नियमों को भी संरेखित करने में मदद करती है।

मल्टी-टेनेंट तालिकाओं के लिए डिज़ाइन पैटर्न

मल्टी-टेनेंट RLS तब सबसे बेहतर काम करता है जब आपकी तालिकाएँ अनुमान लगाने योग्य दिखें। अगर हर तालिका टेनेंट को अलग तरीके से स्टोर करे तो आपकी पॉलिसीज़ पहेली बन जाएँगी। एक संगत संरचना PostgreSQL RLS को पढ़ने, टेस्ट करने और समय के साथ सही रखने में आसान बनाती है।

एक टेनेंट आइडेंटिफायर चुनें और उसे हर जगह इस्तेमाल करें। UUIDs आम हैं क्योंकि वे अनुमान लगाना मुश्किल और कई सिस्टम में जनरेट करना आसान होते हैं। इंटेजर्स भी ठीक हैं, खासकर आंतरिक ऐप्स के लिए। स्लग्स (जैसे "acme") मानव-पठनीय होते हैं, पर बदल सकते हैं, इसलिए उन्हें डिस्प्ले फील्ड मानें, मूल कुंजी नहीं।

टेनेंट-स्कोप्ड डेटा के लिए हर तालिका में tenant_id कॉलम जोड़ें और जहाँ संभव हो इसे NOT NULL बनाएं। अगर कोई पंक्ति बिना टेनेंट के मौजूद हो सकती है तो अक्सर यह एक गंध है — यानी आप ग्लोबल और टेनेंट डेटा को एक टेबल में मिला रहे हैं, जो RLS पॉलिसीज़ को जटिल और भंगुर बनाता है।

इंडेक्सिंग सरल लेकिन महत्वपूर्ण है। ज़्यादातर SaaS क्वेरीज पहले tenant_id से फ़िल्टर करती हैं, फिर किसी बिज़नेस फील्ड जैसे status या date से। एक अच्छा डिफ़ॉल्ट tenant_id पर इंडेक्स है, और हाई-ट्रैफिक तालिकाओं के लिए composite index जैसे (tenant_id, created_at) या (tenant_id, status) जहाँ आपकी सामान्य फ़िल्टरिंग इसे सपोर्ट करे।

पहले तय करें कौन सी तालिकाएँ ग्लोबल हैं और कौन सी टेनेंट-स्कोप्ड। आम ग्लोबल तालिकाओं में देशों की लिस्ट, करेंसी कोड, या प्लान डेफिनिशन्स शामिल हो सकती हैं। टेनेंट-स्कोप्ड तालिकाओं में customers, invoices, tickets और जो कुछ भी टेनेंट का है वह आता है।

अगर आप नियमों को बनाए रखने योग्य रखना चाहते हैं तो संकीर्ण रखें:

  • टेनेंट-स्कोप्ड तालिकाएँ: tenant_id NOT NULL, RLS सक्षम, पॉलिसीज़ हमेशा tenant_id की जांच करें।
  • ग्लोबल रेफ़रेंस तालिकाएँ: no tenant_id, कोई टेनेंट पॉलिसी नहीं, अधिकांश रोल्स के लिए read-only।
  • शेयर की गई पर नियंत्रित तालिकाएँ: प्रत्येक कॉन्सेप्ट के लिए अलग तालिकाएँ रखें (ग्लोबल और टेनेंट रोज़ को मिक्स न करें)।

अगर आप AppMaster जैसे टूल के साथ बना रहे हैं तो यह संगति डेटा मॉडल में भी लाभ देती है। एक बार tenant_id मानक फील्ड बन जाए, आप मॉड्यूल्स में एक ही पैटर्न बिना आश्चर्यों के दुबारा इस्तेमाल कर सकते हैं।

चरण-दर-चरण: अपनी पहली टेनेंट पॉलिसी बनाएँ

मल्टी-टेनेंट फाउंडेशन से शुरू करें
SaaS और पोर्टल्स के लिए साफ स्ट्रक्चर के साथ शुरुआत करें, फिर रोल्स और डेटा ओनरशिप के अनुसार ढालें।
टेम्पलेट के साथ बनाएं

PostgreSQL RLS का एक अच्छा पहला कदम एक साधारण तालिका है जो केवल मौजूदा टेनेंट के अंदर पढ़ने योग्य हो। मकसद सरल है: अगर API में कोई WHERE क्लॉज़ भूल भी जाए तो भी डेटाबेस अन्य टेनेंट की पंक्तियाँ लौटाने से मना कर दे।

एक तालिका से शुरू करें जिसमें tenant_id कॉलम हो:

ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;

RLS सक्षम होने के बाद, डिफ़ॉल्ट व्यवहार अक्सर लोगों को आश्चर्यचकित करता है: अगर किसी रोल पर RLS लागू है और कोई मिलती-जुलती पॉलिसी नहीं है, तो SELECT शून्य पंक्तियाँ लौटाता है (और राइट्स फेल हो जाते हैं)। शुरुआत में यही आप चाहते हैं।

अब एक न्यूनतम रीड पॉलिसी जोड़ें। यह उदाहरण मानता है कि आपका ऐप लॉगिन के बाद किसी session variable जैसे app.tenant_id को सेट करता है:

CREATE POLICY invoices_tenant_read
ON invoices
FOR SELECT
USING (tenant_id = current_setting('app.tenant_id')::uuid);

इसके बाद, राइट रूल्स जोड़ें। RLS में USING नियंत्रित करता है कि आप कौन-सी मौजूदा पंक्तियों को छू सकते हैं, और WITH CHECK यह नियंत्रित करता है कि आप किन नई वैल्यूज़ को लिखने की अनुमति रखते हैं।

CREATE POLICY invoices_tenant_insert
ON invoices
FOR INSERT
WITH CHECK (tenant_id = current_setting('app.tenant_id')::uuid);

CREATE POLICY invoices_tenant_update
ON invoices
FOR UPDATE
USING (tenant_id = current_setting('app.tenant_id')::uuid)
WITH CHECK (tenant_id = current_setting('app.tenant_id')::uuid);

CREATE POLICY invoices_tenant_delete
ON invoices
FOR DELETE
USING (tenant_id = current_setting('app.tenant_id')::uuid);

पॉलिसीज़ डिफ़ॉल्ट रूप से PERMISSIVE होती हैं, जिसका मतलब है कि कोई भी एक पॉलिसी पहुँच की अनुमति दे सकती है। जब आप चाहें कि कई नियम सभी पास हों तो RESTRICTIVE चुनें (उदाहरण के लिए, “केवल सक्रिय खाते” जैसी दूसरी जाँच जोड़ते समय)।

पॉलिसीज़ को छोटा और रोल-फोकस्ड रखें। एक बड़ी OR वाली हीन पॉलिसी बनाने की बजाय, हर ऑडियंस के लिए अलग पॉलिसी बनाएं (जैसे invoices_tenant_read_app_user और invoices_tenant_read_support_agent)। इसे टेस्ट करना आसान होता है, समीक्षा करना आसान होता है, और बाद में बदलना सुरक्षित रहता है।

टेनेंट व यूज़र कॉन्टेक्स्ट को सुरक्षित रूप से पास करना

RLS के काम करने के लिए डेटाबेस को जानना चाहिए “कौन कॉल कर रहा है” और “वे किस टेनेंट से हैं।” RLS पॉलिसीज़ केवल उन वैल्यूज़ से पंक्तियों की तुलना कर सकती हैं जिन्हें डेटाबेस क्वेरी समय पर पढ़ सकता है, इसलिए आपको वह कॉन्टेक्स्ट सेशन में पास करना होगा।

एक सामान्य पैटर्न यह है कि ऑथेंटिकेशन के बाद सेशन वेरिएबल सेट किए जाते हैं, फिर पॉलिसीज़ उन्हें current_setting() से पढ़ती हैं। ऐप पहचान प्रमाणित करता है (उदा. JWT को वेरिफाई करके), फिर केवल आवश्यक फ़ील्ड्स (tenant_id, user_id, role) को डेटाबेस कनेक्शन में कॉपी करता है।

-- Run once per request (or per transaction)
SELECT set_config('app.tenant_id', '3f2a0c3e-9c7b-4d3f-9c5c-3c5e9c5d1a11', true);
SELECT set_config('app.user_id',   '8d9c6b1a-6b6d-4e32-9c0d-2bfe6f6c1111', true);
SELECT set_config('app.role',      'support_agent', true);

-- In a policy
-- tenant_id column is a UUID
USING (tenant_id = current_setting('app.tenant_id', true)::uuid);

तीसरा आर्गुमेंट true इसे वर्तमान ट्रांज़ैक्शन के लिए “लोकल” बनाता है। यह कनेक्शन पूलिंग के उपयोग में मायने रखता है: एक पूल्ड कनेक्शन दूसरे अनुरोध द्वारा पुन: उपयोग हो सकता है, इसलिए आप नहीं चाहेंगे कि पिछला टेनेंट कॉन्टेक्स्ट वहीं पड़ा रहे।

JWT claims से कॉन्टेक्स्ट भरना

यदि आपका API JWTs का उपयोग करता है, तो क्लेम्स को इनपुट की तरह मानें, सच्चाई की तरह नहीं। पहले टोकन सिग्नेचर और एक्सपायरी वेरिफाई करें, फिर केवल आवश्यक फ़ील्ड्स (tenant_id, user_id, role) ही सेशन सेटिंग्स में कॉपी करें। क्लाइंट्स को सीधे हेडर्स या क्वेरी पैरामीटर के तौर पर ये वैल्यूज़ भेजने की अनुमति न दें।

गायब या अवैध कॉन्टेक्स्ट: डिफ़ॉल्ट रूप से अस्वीकार करें

पॉलिसीज़ इस तरह डिज़ाइन करें कि गायब सेटिंग्स से कोई पंक्तियाँ न मिलें।

current_setting('app.tenant_id', true) का उपयोग करें ताकि गायब वैल्यूज़ NULL लौटाएँ। सही प्रकार में कास्ट करें (जैसे ::uuid) ताकि अवैध फॉर्मैट जल्दी फेल हो जाए। और अगर टेनेंट/यूज़र कॉन्टेक्स्ट सेट नहीं हो सकता तो रिक्वेस्ट फेल कर दें, बजाय किसी डिफ़ॉल्ट अनुमान के।

यह सुनिश्चित करता है कि एक्सेस कंट्रोल तब भी सुसंगत रहे जब कोई क्वेरी UI को बायपास कर दे या कोई नया एंडपॉइंट जोड़ दिया जाए।

व्यावहारिक रोल पैटर्न जो बनाए रखना आसान हों

RLS-फ्रेंडली ऐप फ्लोज़ टेस्ट करें
टेनेंट आइसोलेशन वर्कफ़्लो जल्दी प्रोटोटाइप करें, फिर तकनीकी कर्ज बढ़ाए बिना इटरेट करें।
अभी शुरू करें

PostgreSQL RLS पॉलिसीज़ को पठनीय रखने का सबसे आसान तरीका पहचान को परमिशन्स से अलग रखना है। एक ठोस बेसलाइन users तालिका प्लस एक memberships तालिका है जो यूज़र को टेनेंट और रोल (या कई रोल्स) से जोड़ती है। फिर आपकी पॉलिसीज़ एक प्रश्न का उत्तर दे सकें: “क्या वर्तमान यूज़र के पास इस पंक्ति के लिए सही मेंबरशिप है?”

रोल नामों को असली क्रियाओं से जोड़ें, नौकरी के शीर्षकों से नहीं। “invoice_viewer” और “invoice_approver” अक्सर “manager” से बेहतर उम्र लेते हैं, क्योंकि पॉलिसी सामान्य शब्दों में लिखी जा सकती है।

कुछ रोल पैटर्न जो बड़े होने पर भी सरल रहते हैं:

  • Owner-only: पंक्ति में created_by_user_id (या owner_user_id) होता है और एक्सेस उसी मिलान से चेक होती है।
  • Team-only: पंक्ति में team_id होता है, और पॉलिसी चेक करती है कि यूज़र उसी टेनेंट के अंदर उस टीम का सदस्य है।
  • Approved-only: पढ़ना केवल तब अनुमति है जब status = 'approved' हो, और लिखने की अनुमति केवल अप्रूवर्स को हो।
  • Mixed rules: पहले सख्त शुरू करें, फिर छोटे अपवाद जोड़ें (उदा. “सपोर्ट पढ़ सकता है, पर केवल टेनेंट के भीतर”)।

क्रॉस-टेनेंट एडमिन्स कई टीमों को मुश्किल में डालते हैं। उन्हें स्पष्ट रूप से हैंडल करें, एक छिपे हुए “superuser” शॉर्टकट के रूप में नहीं। एक अलग कॉन्सेप्ट बनाएं जैसे platform_admin (ग्लोबल) और पॉलिसी में जानबूझकर चेक मांगें। बेहतर यह है कि क्रॉस-टेनेंट एक्सेस डिफ़ॉल्ट रूप से रीड-ओनली रखी जाए और राइट्स के लिए उच्चतर मानदंड आवश्यक हों।

डॉक्यूमेंटेशन उतना ही मायने रखता है जितना लगता है। हर पॉलिसी के ऊपर एक छोटा कमेंट रखें जो इरादा बताए, SQL नहीं। “Approvers status बदल सकते हैं। Viewers केवल approved invoices पढ़ सकते हैं।” छह महीने बाद वही नोट नीति संपादनों को सुरक्षित रखेगा।

अगर आप नो-कोड टूल जैसे AppMaster के साथ बनाते हैं, तो ये पैटर्न अभी भी लागू होते हैं। आपका UI और API तेज़ी से बढ़ सकते हैं, पर डेटाबेस नियम स्थिर रहते हैं क्योंकि वे मेंबरशिप्स और स्पष्ट रोल अर्थों पर निर्भर करते हैं।

उदाहरण परिदृश्य: एक साधारण SaaS जिसमें इनवॉइस और सपोर्ट है

जल्दी से सुरक्षित बैकएंड शिप करें
जब आपकी आवश्यकताएँ बदलें तो साफ सोर्स कोड वाले प्रोडक्शन-रेडी API जेनरेट करें।
बिल्ड बैकएंड

कल्पना करें एक छोटा SaaS जो कई कंपनियों को सेवा देता है। हर कंपनी एक टेनेंट है। ऐप में invoices (पैसे) और support tickets (दैनिक सहायता) हैं। यूज़र एजेंट, मैनेजर, या सपोर्ट हो सकते हैं।

डेटा मॉडल (सरलीकृत): हर invoice और ticket रो में tenant_id होता है। Tickets में assignee_user_id भी होता है। ऐप लॉगिन के बाद डेटाबेस सेशन में वर्तमान टेनेंट और यूज़र सेट करता है।

यहाँ PostgreSQL RLS किस तरह दिन-प्रतिदिन के जोखिम को बदल देता है।

Tenant A का एक यूज़र invoices स्क्रीन खोलता है और Tenant B का कोई invoice ID जाँचना चाहता है (या UI गलती से इसे भेज देता है)। क्वेरी चलती है, पर डेटाबेस शून्य पंक्तियाँ लौटाता है क्योंकि पॉलिसी की आवश्यकता है invoice.tenant_id = current_tenant_id। कोई “access denied” लीक नहीं, बस खाली रिज़ल्ट।

एक ही टेनेंट के अंदर, रोल्स और भी संकुचित करते हैं। एक मैनेजर अपने टेनेंट की सभी इनवॉइस और टिकट देख सकता है। एक एजेंट केवल उन्हें असाइन किए गए टिकट देख सकता है, और शायद अपने ड्राफ्ट्स। यही वह जगह है जहाँ टीमें अक्सर API में गलतियाँ करती हैं, ख़ासकर जब फ़िल्टर्स वैकल्पिक हों।

सपोर्ट एक विशेष मामला है। उन्हें ग्राहकों की मदद के लिए इनवॉइस देखने की ज़रूरत पड़ सकती है, पर उन्हें संवेदनशील फ़ील्ड्स जैसे amount, bank_account, या tax_id बदलने की अनुमति नहीं मिलनी चाहिए। एक व्यवहारिक पैटर्न यह है:

  • सपोर्ट रोल के लिए invoices पर SELECT की अनुमति दें (फिर भी टेनेंट-स्कोप्ड)।
  • UPDATE केवल एक “सेफ” पाथ के जरिए अनुमति दें (उदा. केवल एडिटेबल कॉलम दिखाने वाला एक व्यू, या एक सख्त अपडेट पॉलिसी जो प्रोटेक्टेड फ़ील्ड्स में बदलाव को अस्वीकार कर दे)।

अब “एक्सीडेंटल API बग” परिदृश्य: किसी एंडपॉइंट ने रिफैक्टर के दौरान टेनेंट फ़िल्टर लागू करना भूल गया। RLS के बिना, यह क्रॉस-टेनेंट इनवॉइस लीक कर सकता है। RLS के साथ, डेटाबेस सत्र टेनेंट के बाहर की पंक्तियाँ लौटाने से मना कर देता है, तो बग एक टूटा हुआ स्क्रीन बन जाती है, डेटा उल्लंघन नहीं।

अगर आप इस तरह का SaaS AppMaster में बनाते हैं, तो भी आप चाहते हैं कि ये नियम डेटाबेस में हों। UI चेक सहायक होते हैं, पर जब कुछ छूट जाए तब डेटाबेस नियम ही मजबूती से काम करते हैं।

सामान्य गलतियाँ और उनसे कैसे बचें

PostgreSQL RLS शक्तिशाली है, पर छोटी चूकें "सुरक्षित" को "अचरजजनक" बना सकती हैं। ज़्यादातर समस्याएँ तब दिखती हैं जब कोई नई तालिका जोड़ दी जाती है, कोई रोल बदल जाता है, या कोई गलत database user से टेस्ट करता है।

एक आम गलती नई तालिका पर RLS को सक्षम करना भूल जाना है। आप अपने कोर टेबल्स के लिए सावधान पॉलिसीज़ लिख लेते हैं, फिर बाद में एक "notes" या "attachments" तालिका जोड़ देते हैं और उसे फुल एक्सेस के साथ शिप कर देते हैं। आदत बनाएँ: नई तालिका = RLS सक्षम + कम से कम एक पॉलिसी।

एक और आम झटका यह है कि एक्शन के बीच पॉलिसीज़ असंगत हों। एक पॉलिसी जो INSERT की अनुमति देती है पर SELECT को ब्लॉक कर देती है, बनते ही ऐसा लग सकता है कि “डेटा गायब हो गया”। इसका विपरीत भी कष्टप्रद है: यूज़र पंक्तियाँ पढ़ सकते हैं जिन्हें वे नहीं बना सकते, इसलिए वे UI में वर्कअराउंड करते हैं। फ़्लोज़ में सोचें: "बनाना फिर देखना", "अपडेट फिर फिर खोलना", "डिलीट फिर लिस्ट करना"।

SECURITY DEFINER फ़ंक्शंस के साथ सावधान रहें। वे फ़ंक्शन मालिक की अधिकारों के साथ चलते हैं, जो RLS को बायपास कर सकते हैं अगर आप सतर्क न हों। अगर आप इन्हें उपयोग करें तो उन्हें छोटा रखें, इनपुट वेरिफाई करें, और डायनामिक SQL से बचें जब तक सचमुच आवश्यकता न हो।

ऐप-साइड फ़िल्टरिंग पर भरोसा करने से बचें जबकि डेटाबेस एक्सेस खुला छोड़ दिया गया हो। अच्छी तरह बने APIs भी नए एंडपॉइंट्स, बैकग्राउंड जॉब्स, और एडमिन स्क्रिप्ट्स बढ़ाते हैं। अगर डेटाबेस रोल सब कुछ पढ़ सकता है, तो किसी न किसी समय कुछ होगा।

समस्याएँ जल्दी पकड़ने के लिए चेक्स को व्यावहारिक रखें:

  • प्रोडक्शन ऐप के समान DB रोल का उपयोग करके टेस्ट करें, अपने पर्सनल एडमिन यूज़र का नहीं।
  • हर तालिका के लिए एक नकारात्मक टेस्ट जोड़ें: दूसरे टेनेंट का यूज़र शून्य पंक्तियाँ देखे।
  • पुष्टि करें कि हर तालिका वे एक्शन्स सपोर्ट करती है जो आप अपेक्षित हैं: SELECT, INSERT, UPDATE, DELETE
  • SECURITY DEFINER उपयोग की समीक्षा करें और लिखें कि यह क्यों आवश्यक है।
  • RLS सक्षम करने को कोड रिव्यू चेकलिस्ट और माइग्रेशन्स में शामिल करें।

उदाहरण: अगर एक सपोर्ट एजेंट एक इनवॉइस नोट बनाता है पर उसे वापस नहीं पढ़ पाता, तो अक्सर यह एक INSERT पॉलिसी है बिना मिलते-जुलते SELECT पॉलिसी के (या उस सेशन के लिए टेनेंट कॉन्टेक्स्ट सेट न होना)।

अपने RLS सेटअप को मान्य करने के लिए त्वरित चेकलिस्ट

बिज़नेस नियम एक जगह रखें
रोल, ओनरशिप और अप्रूवल्स को बैकएंड लॉजिक में बदलें बिना हर जगह ऑथराइज़ेशन चेक फिर से लिखे।
प्रोजेक्ट बनाएं

RLS समीक्षा में सही दिख सकती है और फिर भी असली उपयोग में फेल कर सकती है। सत्यापन पॉलिसीज़ पढ़ने से अधिक तोड़ने की कोशिश करने के बारे में है। इसे उसी तरह टेस्ट करें जिस तरह आपका ऐप इसे उपयोग करेगा, न कि जिस तरह आप आशा करते हैं कि यह चलेगा।

पहले एक छोटे टेस्ट आइडेंटिटीज़ सेट बनाइए। कम से कम दो टेनेंट बनायें (Tenant A और Tenant B)। हर टेनेंट के लिए एक सामान्य यूज़र और एक एडमिन/मैनेजर जोड़ें। अगर आप “सपोर्ट एजेंट” या “रीड-ओनली” रोल सपोर्ट करते हैं तो उन रोल्स के लिए भी एक-क-एक अकाउंट जोड़ें।

फिर PostgreSQL RLS को इन छोटे, फिर से चलाने योग्य चेक्स से दबाएँ:

  • हर रोल के लिए कोर ऑपरेशन्स चलाएँ: लिस्ट रॉज़, एकल रो id से फ़ेच करें, इन्सर्ट, अपडेट, और डिलीट। हर ऑपरेशन के लिए “अनुमत” और “ब्लॉक होना चाहिए” केस दोनों आज़माएँ।
  • टेनेंट सीमाएँ साबित करें: Tenant A के रूप में Tenant B डेटा पढ़ने या संशोधित करने की कोशिश करें — आपको शून्य पंक्तियाँ या अनुमति त्रुटि मिलनी चाहिए, कभी "कुछ पंक्तियाँ" नहीं।
  • लीक के लिए जॉइन्स टेस्ट करें: सुरक्षित तालिकाओं को अन्य तालिकाओं (शामिल कर के लुकअप तालिकाएँ) से जोइन करें। पुष्टि करें कि जॉइन किसी दूसरे टेनेंट की संबंधित पंक्तियाँ फ़ेच नहीं कर सकता।
  • गलत या गायब कॉन्टेक्स्ट अस्वीकार करता है: टेनेंट/यूज़र कॉन्टेक्स्ट साफ़ करें और फिर से आज़माएँ। “नो कॉन्टेक्स्ट” क्लोज़ फेल करना चाहिए। एक अवैध टेनेंट id भी आज़माएँ।
  • बुनियादी परफ़ॉर्मेंस पुष्टि करें: क्वेरी योजनाएँ देखें और सुनिश्चित करें कि इंडेक्स आपके टेनेंट फ़िल्टर पैटर्न (आम तौर पर tenant_id प्लस सॉर्ट/सर्च फ़ील्ड) को सपोर्ट करते हैं।

अगर कोई भी टेस्ट आपको अचरज में डालता है, पहले पॉलिसी या कॉन्टेक्स्ट-सेटिंग ठीक करें। इसे UI या API में पैच न करें और उम्मीद करें कि डेटाबेस नियम “ज्यादातर” काम करेंगे।

अगला कदम: सुरक्षित रूप से रोलआउट करें और संगति बनाए रखें

PostgreSQL RLS को एक सुरक्षा सिस्टम की तरह मानें: इसे सावधानीपूर्वक परिचित कराएं, बार-बार सत्यापित करें, और नियमों को इतना सरल रखें कि आपकी टीम उनका पालन करे।

छोटे से शुरू करें। उन तालिकाओं को चुनें जहाँ लीक सबसे ज़्यादा नुकसान पहुंचा सकता है (payments, invoices, HR डेटा, customer messages) और पहले वहाँ RLS सक्षम करें। शुरुआती जीत बड़ी रोलआउट से बेहतर होती है जिसे कोई पूरी तरह समझ न पाए।

एक व्यावहारिक रोलआउट क्रम अक्सर कुछ ऐसा दिखता है:

  • कोर “owned” तालिकाएँ पहले (जिनकी पंक्तियाँ स्पष्ट रूप से एक टेनेंट की होती हैं)
  • व्यक्तिगत डेटा वाली तालिकाएँ (PII)
  • साझा पर टेनेंट से फ़िल्टर की जाने वाली तालिकाएँ (reports, analytics)
  • जॉइन तालिकाएँ और एज केस (many-to-many संबंध)
  • बुनियादी चीज़ें स्थिर होने पर बाकी सब कुछ

टेस्टिंग को अनिवार्य बनाइए। ऑटोमेटेड टेस्ट्स को अलग टेनेंट्स और रोल्स के रूप में वही क्वेरीज चलानी चाहिए और बदलावों की पुष्टि करनी चाहिए। "अनुमत" और "अस्वीकृत" दोनों प्रकार के चेक शामिल करें, क्योंकि सबसे महँगे बग अक्सर शांत ओवर-परमिशन होते हैं।

रुको, एक साफ जगह रखें जहां प्रत्येक रिक्वेस्ट फ़्लो में सेशन कॉन्टेक्स्ट सेट होता है — इससे पहले कि कोई क्वेरी चले। टेनेंट id, user id, और role को एक बार जल्दी सेट करें और बाद में अनुमान न लगाएँ। अगर आप ट्रांज़ैक्शन के बीच कॉन्टेक्स्ट सेट करते हैं तो अंततः आप किसी क्वेरी के साथ गायब या स्टेल वैल्यू देखेंगे।

जब आप AppMaster के साथ बनाते हैं, तो अपने जनरेटेड बैकएंड APIs और आपके PostgreSQL पॉलिसीज़ के बीच सुसंगति की योजना बनाएं। सेशन में टेनेंट और रोल कॉन्टेक्स्ट पास करने के लिये एक ही तरीका स्टैण्डर्डाइज़ करें ताकि पॉलिसीज़ हर जगह एक समान रहें। अगर आप AppMaster पर (appmaster.io) बना रहे हैं, तब भी RLS को टेनेंट आइसोलेशन का अंतिम प्राधिकारी मानना चाहिए, भले ही आप UI में भी एक्सेस गेट करते हों।

अंत में, देखें कि क्या फेल होता है। ऑथोराइज़ेशन फेल्यर्स उपयोगी संकेत होते हैं, खासकर रोलआउट के शुरुआती समय में। बार-बार होने वाली डिनायल्स को ट्रैक करें और जांचें कि क्या वे वास्तविक अटैक, टूटा क्लाइंट फ्लो, या बहुत सख्त पॉलिसी की ओर इशारा करती हैं।

RLS को स्वस्थ रखने में मदद करने वाली छोटी आदतें:

  • डिफ़ॉल्ट-डिनाय मानसिकता, और अपवाद इरादतन जोड़े जाएँ
  • स्पष्ट पॉलिसी नाम (तालिका + क्रिया + ऑडियंस)
  • पॉलिसी बदलावों की कोड-स्तर समीक्षा
  • प्रारंभिक रोलआउट के दौरान डिनायल्स लॉग करें और समीक्षा करें
  • हर नई RLS तालिका के लिए एक छोटा टेस्ट सेट जोड़ें
शुरू करना आसान
कुछ बनाएं अद्भुत

फ्री प्लान के साथ ऐपमास्टर के साथ प्रयोग करें।
जब आप तैयार होंगे तब आप उचित सदस्यता चुन सकते हैं।

शुरू हो जाओ