Next.js Server Actions एक use-server फ़ाइल में एक्सपोर्ट किए गए हर फंक्शन को स्वचालित रूप से एक पब्लिक HTTP एंडपॉइंट के रूप में एक्सपोज़ कर देते हैं, और उसे एक यूनिक आइडेंटिफायर (unique identifier) असाइन करते हैं जिसका उपयोग क्लाइंट फंक्शन को कॉल करने के लिए करता है। यह एंडपॉइंट तब भी मौजूद रहता है चाहे कोई बटन, लिंक या कोई अन्य UI एलिमेंट रेंडर हुआ हो या नहीं, जिसका अर्थ है कि जो कोई भी इस आइडेंटिफायर को खोज लेता है, वह सीधे फंक्शन को कॉल कर सकता है।

फ्रेमवर्क का डिज़ाइन Server Actions को साधारण हेल्पर फंक्शन की तरह मानता है, लेकिन रनटाइम पर वे सुलभ (reachable) URLs बन जाते हैं। उन डेवलपर्स के लिए जो यह मान लेते हैं कि UI ही एकमात्र गेटकीपर है, यह एक अदृश्य अटैक सरफेस (attack surface) बनाता है जिसे एक सिंगल क्राफ्टेड रिक्वेस्ट (crafted request) के साथ एक्सप्लॉइट किया जा सकता है।

Server Actions सुरक्षित क्यों लगते थे

Server Actions को इसलिए पेश किया गया था ताकि डेवलपर्स अपने कंपोनेंट्स के ठीक बगल में सर्वर-साइड कोड लिख सकें, जिससे अलग API रूट्स के बॉयलरप्लेट (boilerplate) से बचा जा सके। एक सामान्य उपयोग ऐसा दिखता है:

<form action={deleteInvoice}>
  <button type="submit">Delete</button>
</form>

क्योंकि deleteInvoice को ट्रिगर करने का एकमात्र दृश्य तरीका फॉर्म है, इसलिए कई डेवलपर्स अनधिकृत (unauthorised) उपयोगकर्ताओं के लिए बटन को छिपा देते हैं, यह सोचते हैं कि TypeScript सिग्नेचर गलत डेटा (malformed data) को रोक देंगे, और इस तथ्य पर भरोसा करते हैं कि फंक्शन केवल सर्वर-ओनली मॉड्यूल में रहता है। इनमें से कोई भी धारणा वास्तविक सुरक्षा प्रदान नहीं करती है।

छिपा हुआ एक्सपोज़र

जब किसी फ़ाइल में “use server” होता है, तो Next.js प्रत्येक एक्सपोर्ट किए गए फंक्शन को इस तरह के एंडपॉइंट में कंपाइल कर देता है:

POST /_next/data/<build-id>/<page>.json?__rsc=<action-id>

<action-id> एक स्टेबल हैश (stable hash) है जिसे क्लाइंट बंडल एम्बेड करता है। एक हमलावर इसे निम्नलिखित तरीकों से प्राप्त कर सकता है:

  • पेज के नेटवर्क ट्रैफिक का निरीक्षण करके।
  • बंडल किए गए JavaScript को पढ़कर (ID एक साधारण स्ट्रिंग होती है)।
  • यदि प्रोजेक्ट प्रेडिक्टेबल पैटर्न का पालन करता है, तो नेमिंग कन्वेंशन के आधार पर अनुमान लगाकर।

एक बार ID पता चल जाने के बाद, किसी भी टूल—cURL, Postman, या किसी दुर्भावनापूर्ण (malicious) स्क्रिप्ट—से रिक्वेस्ट भेजी जा सकती है, जो किसी भी UI-लेवल चेक को बायपास कर देती है।

तीन ठोस जोखिम

जोखिम यह क्यों महत्वपूर्ण है
UI चेक अप्रभावी हैं बटन या लिंक छिपाने से अंतर्निहित (underlying) एंडपॉइंट डिलीट नहीं होता है। एंडपॉइंट सुलभ रहता है, ठीक उसी तरह जैसे एक छिपा हुआ एडमिन पेज जो सर्वर पर अभी भी मौजूद है।
TypeScript रनटाइम सुरक्षा प्रदान नहीं करता है जब कोड चलता है तो टाइप्स हटा दिए जाते हैं। deleteInvoice(id: number) के रूप में घोषित फंक्शन एक विशाल स्ट्रिंग, एक ऐरे, या यहाँ तक कि दुर्भावनापूर्ण JSON भी प्राप्त कर सकता है, जिससे लॉजिक एरर या इंजेक्शन अटैक हो सकते हैं।
कोई डिफॉल्ट ऑथेंटिकेशन या ऑथराइजेशन नहीं Server Actions स्थानीय हेल्पर की तरह दिखते हैं, इसलिए डेवलपर्स अक्सर सेशन चेक, CSRF प्रोटेक्शन, या रो-लेवल परमिशन चेक जोड़ना भूल जाते हैं जो पारंपरिक API रूट्स में मानक होते हैं।

Server Action को कैसे सुरक्षित करें

  1. कॉलर को ऑथेंटिकेट करें – कोई भी बिजनेस लॉजिक चलने से पहले यह सत्यापित करें कि एक वैध सेशन या टोकन मौजूद है।
  2. पेलोड को वैलिडेट करें – रनटाइम पर डेटा टाइप्स और वैल्यू कंस्ट्रेंट्स को लागू करने के लिए एक स्कीमा लाइब्रेरी (जैसे, Zod, Yup) का उपयोग करें।
  3. ऑपरेशन को ऑथराइज करें – "क्या यूजर लॉग इन है?" के अलावा, यह पुष्टि करें कि यूजर उस विशिष्ट रिकॉर्ड का मालिक है जिसे वह संशोधित (modify) या डिलीट करने की कोशिश कर रहा है।

एक न्यूनतम उदाहरण:

'use server';
import { getSession } from '@/auth';
import { z } from 'zod';
import { db } from '@/db';

const DeleteInvoiceSchema = z.object({
  id: z.number().int().positive(),
});

export async function deleteInvoice(formData: FormData) {
  const session = await getSession();
  if (!session) throw new Error('Unauthenticated');

  const parsed = DeleteInvoiceSchema.safeParse({
    id: Number(formData.get('id')),
  });
  if (!parsed.success) throw new Error('Invalid input');

  const invoice = await db.invoice.findUnique({ where: { id: parsed.data.id } });
  if (!invoice || invoice.ownerId !== session.userId) {
    throw new Error('Unauthorized');
  }

  await db.invoice.delete({ where: { id: invoice.id } });
}

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

Next.js की अन्य सूक्ष्म कमियां

समस्या लक्षण समाधान
NEXT_PUBLIC_ env vars NEXT_PUBLIC_ से शुरू होने वाली कोई भी चीज़ क्लाइंट में बंडल हो जाती है, जिससे सीक्रेट्स एक्सपोज़ हो जाते हैं। सीक्रेट्स को साधारण env vars में रखें, उन्हें कभी भी NEXT_PUBLIC_ के साथ प्रीफिक्स न करें।
ओपन रीडायरेक्ट्स (Open redirects) एक redirect क्वेरी पैरामीटर को स्वीकार करना और उसे लापरवाही से कॉन्कैटिनेट (concatenate) करना यूजर्स को //evil.com पर भेज सकता है। व्हाइटलिस्ट के विरुद्ध टारगेट को वैलिडेट करें या सेम-ओरिजिन (same-origin) चेक लागू करें।
Server-Side Request Forgery (SSRF) यूजर द्वारा दिए गए URL को फेच करने से हमलावर आंतरिक सेवाओं या क्लाउड मेटाडेटा एंडपॉइंट्स तक पहुँच सकते हैं। होस्टनेम को अलाउ-लिस्ट (allow-list) में रखें, प्राइवेट IP रेंज को ब्लॉक करें, और टाइमआउट सेट करें।
dangerouslySetInnerHTML सैनिटाइजेशन के बिना यूजर-प्रदान किए गए HTML को रेंडर करने से XSS का खतरा बढ़ जाता है। DOMPurify जैसी लाइब्रेरी का उपयोग करें या कच्चे (raw) HTML से पूरी तरह बचें।

इन पैटर्न्स को स्वचालित रूप से स्कैन करना

स्टैटिक-एनालिसिस टूल्स ऊपर सूचीबद्ध जोखिम भरे कंस्ट्रक्ट्स (constructs) को फ्लैग कर सकते हैं। एक हल्का विकल्प Semgrep है, जो तेज़ी से चलता है और जिसे CI पाइपलाइन्स में एकीकृत (integrate) किया जा सकता है:

npx --yes semgrep --config https://raw.githubusercontent.com/catidegla/stacksec/main/rules .

रूल सेट में एक्सपोर्ट किए गए सर्वर फंक्शन, NEXT_PUBLIC_ का दुरुपयोग, ओपन रीडायरेक्ट्स, SSRF पैटर्न और असुरक्षित HTML इंसर्शन के लिए चेक शामिल हैं।

आगे क्या देखें

  • फ्रेमवर्क अपडेट्स – Next.js रिलीज़ पर नज़र रखें; टीम बिल्ट-इन ऑथेंटिकेशन हुक्स पेश कर सकती है या जनरेट किए गए एंडपॉइंट्स को सैंडबॉक्स कर सकती है।
  • कम्युनिटी टूलिंग – यहाँ बताए गए सुरक्षा उपायों को ऑटोमेट करने के लिए नए ESLint प्लगइन्स और Next.js-विशिष्ट Semgrep नियम उभर रहे हैं।
  • वास्तविक दुनिया की घटनाएँ – जैसे-जैसे अधिक प्रोजेक्ट्स Server Actions को अपना रहे हैं, तो उन उजागर किए गए एक्सप्लॉइट्स पर नज़र रखें जो व्यवहार में जोखिम को दर्शाते हैं। किसी ब्रीच (breach) के होने से पहले शुरुआती पहचान आंतरिक सुरक्षा समीक्षाओं में मदद कर सकती है।

निष्कर्ष: एक Next.js Server Action कोई प्राइवेट हेल्पर नहीं है; जैसे ही आप इसे एक्सपोर्ट करते हैं, यह एक पब्लिक HTTP एंडपॉइंट बन जाता है। इसे किसी भी अन्य API रूट की तरह मानें—ऑथेंटिकेट, वैलिडेट और ऑथोराइज़ करें—अन्यथा UI के पास सर्वर कोड लिखने की सुविधा जल्दी ही एक सुरक्षा जोखिम (security liability) बन सकती है।