Next.js Server Actions ஒரு use-server கோப்பில் ஏற்றுமதி செய்யப்பட்ட (exported) ஒவ்வொரு செயல்பாட்டையும் (function) ஒரு பொதுவான HTTP endpoint ஆக தானாகவே வெளிப்படுத்துகிறது. இதற்கு ஒரு தனித்துவமான அடையாள எண்ணை (unique identifier) ஒதுக்குகிறது, இதைப் பயன்படுத்தி கிளையண்ட் அந்தச் செயல்பாட்டை அழைக்க முடியும். ஒரு பட்டன், லிங்க் அல்லது வேறு எந்த UI உறுப்பும் திரையில் காட்டப்படாவிட்டாலும் அந்த endpoint இருக்கும், அதாவது அந்த அடையாள எண்ணைக் கண்டறியும் எவரும் நேரடியாக அந்தச் செயல்பாட்டை அழைக்க முடியும்.
இந்த கட்டமைப்பின் வடிவமைப்பு Server Actions-களை சாதாரண helper functions போலக் கருதுகிறது, ஆனால் runtime-இல் அவை அணுகக்கூடிய URLs ஆக மாறுகின்றன. UI மட்டுமே ஒரு பாதுகாப்புக் கதவு (gatekeeper) என்று நினைக்கும் டெவலப்பர்களுக்கு, இது ஒரு மறைமுகமான தாக்குதல் பரப்பளவை (attack surface) உருவாக்குகிறது, இதை ஒரு முறையான கோரிக்கை (crafted request) மூலம் பயன்படுத்த முடியும்.
Server Actions ஏன் பாதுகாப்பானதாகத் தோன்றியது
தனித்த API routes-களின் சிக்கலான வேலைகளைத் (boilerplate) தவிர்க்கவும், டெவலப்பர்கள் தங்கள் components-களுக்கு அருகிலேயே server-side குறியீடுகளை எழுதவும் Server Actions அறிமுகப்படுத்தப்பட்டன. ஒரு பொதுவான பயன்பாடு இதோ:
<form action={deleteInvoice}>
<button type="submit">Delete</button>
</form>
deleteInvoice-ஐத் தூண்டுவதற்கு (trigger) அந்த form மட்டுமே கண்ணுக்குத் தெரியும் வழியாக இருப்பதால், பல டெவலப்பர்கள் அங்கீகாரம் இல்லாத பயனர்களிடமிருந்து பட்டனை மறைத்து வைக்கிறார்கள், TypeScript signatures தவறான தரவைத் தடுத்துவிடும் என்று நினைக்கிறார்கள், மேலும் அந்தச் செயல்பாடு ஒரு server-only module-இல் உள்ளது என்ற உண்மையை நம்பியிருக்கிறார்கள். இந்த யூகங்கள் எதுவும் உண்மையான பாதுகாப்பை வழங்காது.
மறைந்துள்ள வெளிப்பாடு (The hidden exposure)
ஒரு கோப்பில் “use server” இருக்கும்போது, Next.js ஒவ்வொரு exported function-ஐயும் பின்வருவன போன்ற ஒரு endpoint ஆக மாற்றுகிறது:
POST /_next/data/<build-id>/<page>.json?__rsc=<action-id>
<action-id> என்பது client bundle-இல் இணைக்கப்பட்டுள்ள ஒரு நிலையான hash ஆகும். ஒரு தாக்குதல் நடத்துபவர் (attacker) இதை பின்வரும் வழிகளில் பெறலாம்:
- பக்கத்தின் network traffic-ஐ ஆய்வு செய்வதன் மூலம்.
- bundled JavaScript-ஐப் படிப்பதன் மூலம் (ID என்பது ஒரு சாதாரண string ஆகும்).
- திட்டமானது கணிக்கக்கூடிய முறைகளைப் பின்பற்றினால், பெயரிடும் முறைகளின் (naming conventions) அடிப்படையில் ஊகிப்பதன் மூலம்.
ID தெரிந்தவுடன், cURL, Postman அல்லது ஒரு தீங்கிழைக்கும் script போன்ற எந்தக் கருவியிலிருந்தும் ஒரு கோரிக்கையை அனுப்பி, UI-மட்ட சோதனைகளைத் (UI-level checks) தவிர்க்க முடியும்.
மூன்று உறுதியான அபாயங்கள்
| அபாயம் | ஏன் இது முக்கியமானது |
|---|---|
| UI சோதனைகள் பயனற்றவை | பட்டன் அல்லது லிங்க்கை மறைப்பது அந்த அடிப்படையிலான endpoint-ஐ நீக்கிவிடாது. சர்வரில் மறைக்கப்பட்ட ஒரு admin page இன்னும் இருப்பது போல, அந்த endpoint அணுகக்கூடிய நிலையிலேயே இருக்கும். |
| TypeScript runtime பாதுகாப்பை வழங்குவதில்லை | குறியீடு இயங்கும்போது Types நீக்கப்படுகின்றன. deleteInvoice(id: number) என அறிவிக்கப்பட்ட ஒரு செயல்பாடு, ஒரு மிகப்பெரிய string, ஒரு array அல்லது தீங்கிழைக்கும் JSON-ஐப் பெறலாம், இது தர்க்கப் பிழைகள் (logic errors) அல்லது injection தாக்குதல்களுக்கு வழிவகுக்கும். |
| இயல்பான அங்கீகாரம் (authentication) அல்லது அதிகாரம் (authorization) இல்லை | Server Actions உள்ளூர் helper functions போலத் தெரிவதால், பாரம்பரிய API routes-களில் தரநிலையாக இருக்கும் session checks, CSRF protection அல்லது row-level permission checks போன்றவற்றைச் சேர்க்க டெவலப்பர்கள் பெரும்பாலும் மறந்துவிடுகிறார்கள். |
ஒரு Server Action-ஐ எவ்வாறு பாதுகாப்பது
- அழைப்பவரை அங்கீகரிக்கவும் (Authenticate the caller) – எந்தவொரு business logic இயங்குவதற்கு முன்பும், ஒரு செல்லுபடியாகும் session அல்லது token இருப்பதை உறுதி செய்யவும்.
- Payload-ஐச் சரிபார்க்கவும் (Validate the payload) – runtime-இல் தரவு வகைகள் (data types) மற்றும் மதிப்பு கட்டுப்பாடுகளை (value constraints) நடைமுறைப்படுத்த ஒரு schema library-யைப் (எ.கா., Zod, Yup) பயன்படுத்தவும்.
- செயல்பாட்டிற்கு அதிகாரம் அளிக்கவும் (Authorize the operation) – “பயனர் லாக்-இன் செய்துள்ளாரா?” என்பதையும் தாண்டி, பயனர் மாற்றவோ அல்லது நீக்கவோ முயற்சிக்கும் குறிப்பிட்ட பதிவின் (record) உரிமையாளர் அவர்தான் என்பதை உறுதிப்படுத்தவும்.
ஒரு சிறிய உதாரணம்:
'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 } });
}
இந்த குறியீடு அங்கீகாரத்தைச் (authentication) தெளிவாகச் சரிபார்க்கிறது, வரும் id-ஐச் சரிபார்க்கிறது, மேலும் நீக்குவதற்கு முன் லாக்-இன் செய்த பயனர் உண்மையில் அந்த invoice-ன் உரிமையாளர் என்பதை உறுதி செய்கிறது.
பிற நுணுக்கமான Next.js சிக்கல்கள்
| சிக்கல் | அறிகுறி | தீர்வு |
|---|---|---|
NEXT_PUBLIC_ env vars |
NEXT_PUBLIC_ என்று தொடங்கும் அனைத்தும் client-இல் இணைக்கப்படுவதால், ரகசியங்கள் (secrets) வெளிப்படும். |
ரகசியங்களை சாதாரண env vars-இல் வைக்கவும், அவற்றை ஒருபோதும் NEXT_PUBLIC_ என்று தொடங்க வேண்டாம். |
| Open redirects | ஒரு redirect query parameter-ஐ ஏற்றுக்கொண்டு அதை அப்படியே இணைப்பது பயனர்களை //evil.com-க்கு அனுப்பக்கூடும். |
இலக்கை (target) ஒரு whitelist உடன் சரிபார்க்கவும் அல்லது same-origin சோதனைகளை நடைமுறைப்படுத்தவும். |
| Server-Side Request Forgery (SSRF) | பயனர் வழங்கும் URL-ஐப் பெறுவது, தாக்குதல் நடத்துபவர்கள் உள் சேவைகளை (internal services) அல்லது cloud metadata endpoints-களை அணுக அனுமதிக்கலாம். | Hostnames-களை அனுமதிக்கப்பட்ட பட்டியலில் (allow-list) சேர்க்கவும், தனிப்பட்ட IP வரம்புகளைத் தடுக்கவும் மற்றும் timeouts-களை அமைக்கவும். |
dangerouslySetInnerHTML |
பயனரால் வழங்கப்பட்ட HTML-ஐச் சுத்திகரிக்காமல் (sanitisation) காண்பிப்பது XSS தாக்குதலுக்கு வழிவகுக்கும். | DOMPurify போன்ற ஒரு library-யைப் பயன்படுத்தவும் அல்லது நேரடி HTML-ஐத் தவிர்க்கவும். |
இந்த முறைகளைத் தானாகவே ஸ்கேன் செய்தல்
மேலே பட்டியலிடப்பட்ட ஆபத்தான கட்டமைப்புகளை static-analysis கருவிகள் கண்டறிய முடியும். Semgrep என்பது ஒரு இலகுவான விருப்பமாகும், இது விரைவாக இயங்கும் மற்றும் CI pipelines-இல் ஒருங்கிணைக்கப்படலாம்:
npx --yes semgrep --config https://raw.githubusercontent.com/catidegla/stacksec/main/rules .
இந்த விதித் தொகுப்பு (rule set), exported server functions, NEXT_PUBLIC_-ன் தவறான பயன்பாடு, open redirects, SSRF முறைகள் மற்றும் பாதுகாப்பற்ற HTML சேர்த்தல் ஆகியவற்றிற்கான சோதனைகளை உள்ளடக்கியது.
அடுத்து கவனிக்க வேண்டியவை
- Framework updates – Keep an eye on Next.js releases; the team may introduce built-in authentication hooks or sandbox the generated endpoints.
- Community tooling – New ESLint plugins and Next.js-specific Semgrep rules are emerging to automate the safeguards described here.
- Real-world incidents – As more projects adopt Server Actions, watch for disclosed exploits that illustrate the risk in practice. Early detection can inform internal security reviews before a breach occurs.
Takeaway: A Next.js Server Action is not a private helper; it is a public HTTP endpoint the moment you export it. Treat it like any other API route—authenticate, validate, and authorize—otherwise the convenience of writing server code next to UI can quickly become a security liability.
