Serverless डेटा रिसाव: अनुरोध के अनुसार अलगाव
जानें कि दोबारा उपयोग किए गए serverless runtime पुराने उपयोगकर्ता डेटा को कैसे उजागर कर सकते हैं और request के अनुसार पहचान व स्थिति कैसे अलग रखें।
💡 सरल व्याख्या
Authentication, tenant पहचान और request-विशिष्ट डेटा को visitor badge की तरह मानें: यह एक visit का है और साझा मेज़ पर नहीं छोड़ना चाहिए। कुछ serverless runtime बाद की requests के लिए execution environment या isolate फिर उपयोग करते हैं। यदि code किसी उपयोगकर्ता का context module-level mutable state में रखे और बाद की request उसे पढ़े, तो app उपयोगकर्ता या tenant की अपनी सीमा पार कर सकता है।
Reuse implementation का व्यवहार है; यह गारंटी नहीं कि अगला उपयोगकर्ता उसी runtime पर पहुँचेगा या runtime किसी निश्चित अवधि तक रहेगा। Leak के लिए साझा execution state और ऐसा code path दोनों आवश्यक हैं जो request data बचाए रखे या पुरानी स्थिति पढ़े। पहचान को request-local रखें और ज़रूरी operations को स्पष्ट रूप से दें।
मुख्य अवधारणाएँ और शब्द
- Execution environment reuse
- Execution environment reuse: platform किसी runtime या isolate को अगली invocations के लिए रख सकता है, लेकिन उसे कभी भी हटा सकता है।
- Module-level state
- Module-level state: handler के बाहर शुरू किया गया मान। यह उसी runtime में invocations के बीच बना रह सकता है, इसलिए इसमें user या tenant का निजी डेटा न रखें।
- AsyncLocalStorage
- AsyncLocalStorage: async execution context में store आगे ले जाने की Node.js API। यह context track करती है; user को authenticate या authorize नहीं करती।
- Cross-request data exposure
- Cross-request data exposure: बाद की request, reused context में पहले की request का data पढ़ती है। केवल reuse से leak साबित नहीं होता।
डेटा उजागर होने का चरण-दर-चरण प्रवाह
एक request अपना डेटा बनाती है
Request A authenticate होती है और अपना user या tenant context प्राप्त करती है। समस्या तब होती है जब code इसे handler में रखने के बजाय module-level mutable variable में लिखता है।
Runtime फिर इस्तेमाल हो सकता है
Response के बाद platform environment को warm रख सकता है। AWS reuse दस्तावेज़ित करता है; Cloudflare Workers isolate दोबारा उपयोग कर सकता है, लेकिन routing और जीवनकाल उपयोगकर्ताओं के बीच तय क्रम नहीं हैं।
बाद की request उसी runtime तक पहुँचती है
यदि Request B उसी process या isolate में संभाली जाए, तो A का छोड़ा module value overwrite या clear होने तक उपलब्ध है। Early return या authentication check की कमी पुरानी स्थिति को पढ़ने योग्य बना सकती है।
App गलत पहचान के तहत डेटा लौटाती है
यदि handler इस पुराने मान से database query या response बनाता है, तो B को A का डेटा मिल सकता है। यह ऐप की isolation में सशर्त गलती है, serverless platforms का अनिवार्य गुण नहीं।
Source code: दोषपूर्ण पैटर्न और सुरक्षित तरीका
// UNSAFE: request identity is mutable process-wide state.
import { NextResponse } from "next/server";
let cachedUserContext: { userId: string; role: string } | null = null;
export async function POST(req: Request) {
const token = req.headers.get("authorization");
if (token) {
cachedUserContext = await decodeUser(token);
}
// If token is missing, stale state can still be used.
return NextResponse.json({ actor: cachedUserContext?.userId });
}
// SAFE PATTERN: keep identity local and pass it to the service.
import { NextResponse } from "next/server";
export async function POST(req: Request) {
const user = await authenticate(req);
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const account = await loadAccountForUser(user.id);
return NextResponse.json({ actor: user.id, account });
}
// The service enforces ownership in its query:
async function loadAccountForUser(userId: string) {
return db.account.findFirst({ where: { ownerId: userId } });
}
Engineering hardening checklist
- Authentication, user identity, tenant IDs और request-specific mutable data handler या स्पष्ट request-scoped context में रखें; module-level global variables में न रखें।
- Authenticated principal को service और data-access functions तक explicit parameters से पहुँचाएँ। Node.js में
AsyncLocalStorage.run()केवल implicit async context की ज़रूरत पर उपयोग करें; यह context आगे ले जाता है, authorization नहीं देता। - Database client singleton को user state न मानें। Client reuse हो सकता है, पर हर query में authenticated user/tenant दें और query condition से ownership लागू करें।
React cache()केवल React Server Components की दस्तावेज़ित request-scoped memoization के लिए है; इसे cross-request authentication cache न बनाएँ।- अलग users और unauthenticated request को interleave करने वाला integration test जोड़ें; सत्यापित करें कि हर response और DB operation अपनी पहचान से जुड़ा रहे।
- Platform lifecycle docs देखें। परीक्षण में environment reuse संभव मानें, पर यह न मानें कि कौन-सी request किस instance तक जाएगी।