Next.js Server Actions ਆਪਣੇ ਆਪ ਹਰ ਐਕਸਪੋਰਟ ਕੀਤੇ ਫੰਕਸ਼ਨ ਨੂੰ, ਜੋ ਕਿ ਇੱਕ use-server ਫਾਈਲ ਵਿੱਚ ਹੁੰਦਾ ਹੈ, ਇੱਕ ਪਬਲਿਕ HTTP endpoint ਵਜੋਂ ਪ੍ਰਗਟ ਕਰਦੇ ਹਨ, ਅਤੇ ਇਸਨੂੰ ਇੱਕ ਵਿਲੱਖਣ ਆਈਡੀ (identifier) ਦਿੰਦੇ ਹਨ ਜਿਸਦੀ ਵਰਤੋਂ ਕਲਾਇੰਟ ਫੰਕਸ਼ਨ ਨੂੰ ਕਾਲ ਕਰਨ ਲਈ ਕਰਦਾ ਹੈ। ਇਹ endpoint ਉਦੋਂ ਵੀ ਮੌਜੂਦ ਹੁੰਦਾ ਹੈ ਜਦੋਂ ਕੋਈ ਬਟਨ, ਲਿੰਕ, ਜਾਂ ਕੋਈ ਹੋਰ UI element ਰੈਂਡਰ ਨਹੀਂ ਹੁੰਦਾ, ਜਿਸਦਾ ਮਤਲਬ ਹੈ ਕਿ ਕੋਈ ਵੀ ਜਿਸਨੂੰ ਇਹ ਆਈਡੀ ਮਿਲ ਜਾਂਦੀ ਹੈ, ਉਹ ਸਿੱਧਾ ਫੰਕਸ਼ਨ ਨੂੰ ਕਾਲ ਕਰ ਸਕਦਾ ਹੈ।
ਫਰੇਮਵਰਕ ਦਾ ਡਿਜ਼ਾਈਨ Server Actions ਨੂੰ ਆਮ helper functions ਵਾਂਗ ਮੰਨਦਾ ਹੈ, ਪਰ runtime ਵੇਲੇ ਉਹ ਪਹੁੰਚਯੋਗ URLs ਬਣ ਜਾਂਦੇ ਹਨ। ਉਹਨਾਂ ਡਿਵੈਲਪਰਾਂ ਲਈ ਜਿਨ੍ਹਾਂ ਨੇ ਇਹ ਮੰਨ ਲਿਆ ਸੀ ਕਿ UI ਹੀ ਇਕਲੌਤਾ ਗੇਟਕੀਪਰ ਹੈ, ਇਹ ਇੱਕ ਅਦਿੱਖ ਹਮਲੇ ਦੀ ਸਤ੍ਹਾ (attack surface) ਬਣਾਉਂਦਾ ਹੈ ਜਿਸਦਾ ਇੱਕ ਸਿੰਗਲ ਤਿਆਰ ਕੀਤੇ ਗਏ (crafted) ਰਿਕੁਐਸਟ ਨਾਲ ਫਾਇਦਾ ਉਠਾਇਆ ਜਾ ਸਕਦਾ ਹੈ।
Server Actions ਸੁਰੱਖਿਅਤ ਕਿਉਂ ਲੱਗਦੇ ਸਨ
Server Actions ਡਿਵੈਲਪਰਾਂ ਨੂੰ ਉਹਨਾਂ ਦੇ ਕੰਪੋਨੈਂਟਸ ਦੇ ਨਾਲ ਹੀ server-side ਕੋਡ ਲਿਖਣ ਦੀ ਇਜਾਜ਼ਤ ਦੇਣ ਲਈ ਲਿਆਂਦੇ ਗਏ ਸਨ, ਤਾਂ ਜੋ ਵੱਖਰੇ API routes ਦੇ ਬੋਏਲਰਪਲੇਟ (boilerplate) ਤੋਂ ਬਚਿਆ ਜਾ ਸਕੇ। ਇੱਕ ਆਮ ਵਰਤੋਂ ਇਸ ਤਰ੍ਹਾਂ ਦੀ ਹੁੰਦੀ ਹੈ:
<form action={deleteInvoice}>
<button type="submit">Delete</button>
</form>
ਕਿਉਂਕਿ ਫਾਰਮ deleteInvoice ਨੂੰ ਟ੍ਰਿਗਰ ਕਰਨ ਦਾ ਇਕਲੌਤਾ ਦਿਖਾਈ ਦੇਣ ਵਾਲਾ ਤਰੀਕਾ ਹੈ, ਬਹੁਤ ਸਾਰੇ ਡਿਵੈਲਪਰ ਅਣਅਧਿਕਾਰਤ (unauthorised) ਯੂਜ਼ਰਾਂ ਲਈ ਬਟਨ ਨੂੰ ਲੁਕਾ ਦਿੰਦੇ ਹਨ, ਇਹ ਸੋਚਦੇ ਹਨ ਕਿ TypeScript signatures ਗਲਤ ਡੇਟਾ ਨੂੰ ਰੋਕ ਦੇਣਗੇ, ਅਤੇ ਇਸ ਤੱਥ 'ਤੇ ਭਰੋਸਾ ਕਰਦੇ ਹਨ ਕਿ ਫੰਕਸ਼ਨ ਸਿਰਫ ਸਰਵਰ-ਸਿਰਫ ਮੋਡਿਊਲ ਵਿੱਚ ਹੈ। ਇਹਨਾਂ ਵਿੱਚੋਂ ਕੋਈ ਵੀ ਅੰਦਾਜ਼ਾ ਅਸਲ ਸੁਰੱਖਿਆ ਪ੍ਰਦਾਨ ਨਹੀਂ ਕਰਦਾ।
ਲੁਕਿਆ ਹੋਇਆ ਐਕਸਪੋਜ਼ਰ
ਜਦੋਂ ਕਿਸੇ ਫਾਈਲ ਵਿੱਚ “use server” ਹੁੰਦਾ ਹੈ, ਤਾਂ Next.js ਹਰ ਐਕਸਪੋਰਟ ਕੀਤੇ ਫੰਕਸ਼ਨ ਨੂੰ ਇੱਕ endpoint ਵਿੱਚ ਕੰਪਾਈਲ ਕਰਦਾ ਹੈ ਜਿਵੇਂ ਕਿ:
POST /_next/data/<build-id>/<page>.json?__rsc=<action-id>
<action-id> ਇੱਕ ਸਟੇਬਲ ਹੈਸ਼ (stable hash) ਹੈ ਜੋ ਕਲਾਇੰਟ ਬੰਡਲ ਵਿੱਚ ਇਨਬੈਡ ਹੁੰਦਾ ਹੈ। ਇੱਕ ਹਮਲਾਵਰ ਇਸਨੂੰ ਇਹਨਾਂ ਤਰੀਕਿਆਂ ਨਾਲ ਪ੍ਰਾਪਤ ਕਰ ਸਕਦਾ ਹੈ:
- ਪੇਜ ਦੇ ਨੈੱਟਵਰਕ ਟ੍ਰੈਫਿਕ ਦੀ ਜਾਂਚ ਕਰਕੇ।
- ਬੰਡਲਡ JavaScript ਨੂੰ ਪੜ੍ਹ ਕੇ (ID ਇੱਕ ਸਾਧਾਰਨ string ਹੁੰਦੀ ਹੈ)।
- ਜੇਕਰ ਪ੍ਰੋਜੈਕਟ ਅਨੁਮਾਨਯੋਗ ਪੈਟਰਨਾਂ ਦੀ ਪਾਲਣਾ ਕਰਦਾ ਹੈ, ਤਾਂ ਨਾਮਕਰਨ ਦੇ ਰਿਵਾਜਾਂ (naming conventions) ਦੇ ਅਧਾਰ 'ਤੇ ਅੰਦਾਜ਼ਾ ਲਗਾ ਕੇ।
ਇੱਕ ਵਾਰ ਜਦੋਂ ID ਪਤਾ ਲੱਗ ਜਾਂਦੀ ਹੈ, ਤਾਂ ਕਿਸੇ ਵੀ ਟੂਲ—cURL, Postman, ਜਾਂ ਕਿਸੇ ਮਾਲੀਸ਼ੀਅਸ ਸਕ੍ਰਿਪਟ—ਤੋਂ ਰਿਕੁਐਸ ਭੇਜੀ ਜਾ ਸਕਦੀ ਹੈ, ਜੋ ਕਿਸੇ ਵੀ UI-level ਚੈੱਕ ਨੂੰ ਬਾਈਪਾਸ ਕਰ ਦਿੰਦੀ ਹੈ।
ਤਿੰਨ ਅਸਲ ਜੋਖਮ
| ਜੋਖਮ | ਇਹ ਕਿਉਂ ਮਾਇਨੇ ਰੱਖਦਾ ਹੈ |
|---|---|
| UI ਚੈੱਕ ਅਸਰਹੀਣ ਹਨ | ਕਿਸੇ ਬਟਨ ਜਾਂ ਲਿੰਕ ਨੂੰ ਲੁਕਾਉਣ ਨਾਲ ਅਸਲ endpoint ਡਿਲੀਟ ਨਹੀਂ ਹੁੰਦਾ। endpoint ਪਹੁੰਚਯੋਗ ਰਹਿੰਦਾ ਹੈ, ਬਿਲਕੁਲ ਉਸੇ ਤਰ੍ਹਾਂ ਜਿਵੇਂ ਇੱਕ ਲੁਕਾ ਹੋਇਆ ਐਡਮਿਨ ਪੇਜ ਜੋ ਅਜੇ ਵੀ ਸਰਵਰ 'ਤੇ ਮੌਜੂਦ ਹੈ। |
| TypeScript ਕੋਈ runtime ਸੁਰੱਖਿਆ ਨਹੀਂ ਦਿੰਦਾ | ਜਦੋਂ ਕੋਡ ਚਲਦਾ ਹੈ ਤਾਂ types ਹਟਾ ਦਿੱਤੇ ਜਾਂਦੇ ਹਨ। deleteInvoice(id: number) ਵਜੋਂ ਘੋਸ਼ਿਤ ਕੀਤਾ ਗਿਆ ਫੰਕਸ਼ਨ ਇੱਕ ਵੱਡੀ string, ਇੱਕ array, ਜਾਂ ਮਾਲੀਸ਼ੀਅਸ JSON ਵੀ ਪ੍ਰਾਪਤ ਕਰ ਸਕਦਾ ਹੈ, ਜਿਸ ਨਾਲ ਲੌਜਿਕ ਗਲਤੀਆਂ ਜਾਂ ਇੰਜੈਕਸ਼ਨ ਹਮਲੇ ਹੋ ਸਕਦੇ ਹਨ। |
| ਕੋਈ ਡਿਫੌਲਟ ਅਥੈਂਟੀਕੇਸ਼ਨ ਜਾਂ ਅਥੋਰਾਈਜ਼ੇਸ਼ਨ ਨਹੀਂ | Server Actions ਸਥਾਨਕ helpers ਵਾਂਗ ਲੱਗਦੇ ਹਨ, ਇਸ ਲਈ ਡਿਵੈਲਪਰ ਅਕਸਰ session ਚੈੱਕ, CSRF ਪ੍ਰੋਟੈਕਸ਼ਨ, ਜਾਂ row-level ਪਰਮਿਸ਼ਨ ਚੈੱਕ ਜੋੜਨਾ ਭੁੱਲ ਜਾਂਦੇ ਹਨ ਜੋ ਕਿ ਰਵਾਇਤੀ API routes ਵਿੱਚ ਮਿਆਰੀ ਹੁੰਦੇ ਹਨ। |
Server Action ਨੂੰ ਕਿਵੇਂ ਸੁਰੱਖਿਅਤ ਕਰਨਾ ਹੈ
- ਕਾਲਰ (caller) ਦੀ ਅਥੈਂਟੀਕੇਸ਼ਨ ਕਰੋ – ਕੋਈ ਵੀ ਬਿਜ਼ਨਸ ਲੌਜਿਕ ਚੱਲਣ ਤੋਂ ਪਹਿਲਾਂ ਪੁਸ਼ਟੀ ਕਰੋ ਕਿ ਇੱਕ ਵੈਧ session ਜਾਂ token ਮੌਜੂਦ ਹੈ।
- ਪੇਲੋਡ (payload) ਨੂੰ ਵੈਲੀਡੇਟ ਕਰੋ – runtime ਵੇਲੇ ਡੇਟਾ ਟਾਈਪਸ ਅਤੇ ਮੁੱਲ ਦੀਆਂ ਸੀਮਾਵਾਂ ਨੂੰ ਲਾਗੂ ਕਰਨ ਲਈ ਇੱਕ schema ਲਾਇਬ੍ਰੇਰੀ (ਜਿਵੇਂ ਕਿ Zod, Yup) ਦੀ ਵਰਤੋਂ ਕਰੋ।
- ਆਪਰੇਸ਼ਨ ਨੂੰ ਅਥੋਰਾਈਜ਼ ਕਰੋ – "ਕੀ ਯੂਜ਼ਰ ਲੌਗਇਨ ਹੈ?" ਤੋਂ ਇਲਾਵਾ, ਇਸ ਗੱਲ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ ਕਿ ਯੂਜ਼ਰ ਉਸ ਖਾਸ ਰਿਕਾਰਡ ਦਾ ਮਾਲਕ ਹੈ ਜਿਸ ਨੂੰ ਉਹ ਸੋਧਣ ਜਾਂ ਡਿਲੀਟ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰ ਰਿਹਾ ਹੈ।
ਇੱਕ ਨਿਮਨਤਮ ਉਦਾਹਰਣ:
'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_ ਨਾਲ ਸ਼ੁਰੂ ਹੋਣ ਵਾਲੀ ਕੋਈ ਵੀ ਚੀਜ਼ ਕਲਾਇੰਟ ਵਿੱਚ ਬੰਡਲ ਹੋ ਜਾਂਦੀ ਹੈ, ਜਿਸ ਨਾਲ secrets ਖੁੱਲ੍ਹ ਜਾਂਦੇ ਹਨ। |
Secrets ਨੂੰ ਸਾਧਾਰਨ env vars ਵਿੱਚ ਰੱਖੋ, ਉਹਨਾਂ ਦੇ ਅੱਗੇ ਕਦੇ ਵੀ NEXT_PUBLIC_ ਨਾ ਲਗਾਓ। |
| Open redirects | ਇੱਕ redirect ਕੁਐਰੀ ਪੈਰਾਮੀਟਰ ਨੂੰ ਸਵੀਕਾਰ ਕਰਨਾ ਅਤੇ ਉਸਨੂੰ ਨਾ ਸਮਝਦੇ ਹੋਏ ਜੋੜਨਾ ਯੂਜ਼ਰਾਂ ਨੂੰ //evil.com 'ਤੇ ਭੇਜ ਸਕਦਾ ਹੈ। |
ਟਾਰਗੇਟ ਨੂੰ ਵ੍ਹਾਈਟਲਿਸਟ ਦੇ ਵਿਰੁੱਧ ਵੈਲੀਡੇਟ ਕਰੋ ਜਾਂ same-origin ਚੈੱਕ ਲਾਗੂ ਕਰੋ। |
| Server-Side Request Forgery (SSRF) | ਯੂਜ਼ਰ ਦੁਆਰਾ ਦਿੱਤੀ ਗਈ URL ਨੂੰ ਫੈਚ ਕਰਨ ਨਾਲ ਹਮਲਾਵਰ ਅੰਦਰੂਨੀ ਸੇਵਾਵਾਂ ਜਾਂ ਕਲਾਉਡ ਮੈਟਾਡਾਟਾ endpoints ਤੱਕ ਪਹੁੰਚ ਸਕਦੇ ਹਨ। | Hostnames ਨੂੰ allow-list ਵਿੱਚ ਰੱਖੋ, ਪ੍ਰਾਈਵੇਟ IP ਰੇਂਜਾਂ ਨੂੰ ਬਲੌਕ ਕਰੋ, ਅਤੇ timeouts ਸੈੱਟ ਕਰੋ। |
dangerouslySetInnerHTML |
ਸੈਨੀਟਾਈਜ਼ੇਸ਼ਨ ਤੋਂ ਬਿਨਾਂ ਯੂਜ਼ਰ ਦੁਆਰਾ ਦਿੱਤਾ ਗਿਆ HTML ਰੈਂਡਰ ਕਰਨਾ XSS ਦੇ ਖਤਰੇ ਨੂੰ ਵਧਾਉਂਦਾ ਹੈ। | DOMPurify ਵਰਗੀ ਲਾਇਬ੍ਰੇਰੀ ਦੀ ਵਰਤੋਂ ਕਰੋ ਜਾਂ ਕੱਚੇ HTML ਤੋਂ ਬਿਲਕੁਲ ਬਚੋ। |
ਇਹਨਾਂ ਪੈਟਰਨਾਂ ਲਈ ਆਪਣੇ
- ਫਰੇਮਵਰਕ ਅੱਪਡੇਟਸ – Next.js ਰਿਲੀਜ਼ਾਂ 'ਤੇ ਨਜ਼ਰ ਰੱਖੋ; ਟੀਮ ਬਿਲਟ-ਇਨ authentication hooks ਪੇਸ਼ ਕਰ ਸਕਦੀ ਹੈ ਜਾਂ ਬਣੇ ਹੋਏ endpoints ਨੂੰ sandbox ਕਰ ਸਕਦੀ ਹੈ।
- ਕਮਿਊਨਿਟੀ ਟੂਲਿੰਗ – ਇੱਥੇ ਦੱਸੇ ਗਏ ਸੁਰੱਖਿਆ ਉਪਾਵਾਂ ਨੂੰ ਆਟੋਮੇਟ ਕਰਨ ਲਈ ਨਵੇਂ ESLint ਪਲਗਇਨ ਅਤੇ Next.js-ਵਿਸ਼ੇਸ਼ Semgrep ਨਿਯਮ ਉਭਰ ਰਹੇ ਹਨ।
- ਅਸਲ-ਦੁਨੀਆ ਦੀਆਂ ਘਟਨਾਵਾਂ – ਜਿਵੇਂ-ਜਿਵੇਂ ਵਧੇਰੇ ਪ੍ਰੋਜੈਕਟ Server Actions ਨੂੰ ਅਪਣਾ ਰਹੇ ਹਨ, ਪ੍ਰਗਟ ਕੀਤੇ ਗਏ exploits 'ਤੇ ਨਜ਼ਰ ਰੱਖੋ ਜੋ ਅਸਲ ਵਿੱਚ ਜੋਖਮ ਨੂੰ ਦਰਸਾਉਂਦੇ ਹਨ। ਕਿਸੇ breach ਹੋਣ ਤੋਂ ਪਹਿਲਾਂ ਜਲਦੀ ਪਤਾ ਲੱਗਣ ਨਾਲ ਅੰਦਰੂਨੀ ਸੁਰੱਖਿਆ ਸਮੀਖਿਆਵਾਂ ਵਿੱਚ ਮਦਦ ਮਿਲ ਸਕਦੀ ਹੈ।
ਮੁੱਖ ਗੱਲ (Takeaway): ਇੱਕ Next.js Server Action ਕੋਈ ਨਿੱਜੀ helper ਨਹੀਂ ਹੈ; ਜਿਸ ਪਲ ਤੁਸੀਂ ਇਸਨੂੰ export ਕਰਦੇ ਹੋ, ਇਹ ਇੱਕ public HTTP endpoint ਬਣ ਜਾਂਦਾ ਹੈ। ਇਸਨੂੰ ਕਿਸੇ ਵੀ ਹੋਰ API route ਵਾਂਗ ਹੀ ਸਮਝੋ—authenticate, validate, ਅਤੇ authorize ਕਰੋ—ਨਹੀਂ ਤਾਂ UI ਦੇ ਨਾਲ server code ਲਿਖਣ ਦੀ ਸਹੂਲਤ ਜਲਦੀ ਹੀ ਇੱਕ security liability ਬਣ ਸਕਦੀ ਹੈ।
