`Cookie` في TypeScript
تشرح هذه المقالة Cookie في TypeScript۔
نستعرض أنماطًا عملية للتعامل مع ملفات تعريف الارتباط بأمان وموثوقية في كلٍ من المتصفح والخادم۔
YouTube Video
Cookie في TypeScript
المفاهيم الأساسية لملفات تعريف الارتباط
ملف تعريف الارتباط هو آلية لتخزين سلاسل صغيرة (أزواج الاسم=القيمة) على جانب العميل، ويتم إنشاؤه عبر ترويسة HTTP Set-Cookie أو document.cookie۔ يمكنك التحكم في سلوكها باستخدام السمات الأمنية (HttpOnly وSecure وSameSite وغيرها).۔
العمليات الأساسية في المتصفح: document.cookie
أدناه مثالٌ بسيط لكتابة ملف تعريف ارتباط في المتصفح۔ أنشِئ ملف تعريف ارتباط عبر إلحاق سلسلة نصية بـ document.cookie۔
1// Set a simple cookie that expires in 7 days.
2// Note: Comments are in English per the user's preference.
3const setSimpleCookie = (name: string, value: string) => {
4 const days = 7;
5 const expires = new Date(Date.now() + days * 86400_000).toUTCString();
6 // name=value; Expires=...; Path=/
7 document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; Expires=${expires}; Path=/`;
8};
9
10setSimpleCookie('theme', 'dark');- ينشئ هذا الكود ملف تعريف ارتباط تنتهي صلاحيته خلال 7 أيام۔ يسهل استخدامه على جانب المتصفح، ولكن نظرًا لعدم إمكانية ضبط
HttpOnly، ينبغي تجنّب تخزين المعلومات الحساسة۔
قراءة ملفات تعريف الارتباط (المتصفح)
الدالة التالية هي دالة مساعدة تسترجع قيمة ملف تعريف الارتباط ذي الاسم المحدد من document.cookie۔ تُعاد قيمة document.cookie كسلسلة نصية مفصولة بفاصلة منقوطة ومسافة ('; ')۔ لذلك نقوم بتقسيمها ونبحث عن ملف تعريف الارتباط المطلوب۔
1// Parse document.cookie and return value for given name or null if not found.
2const getCookie = (name: string): string | null => {
3 const cookies = document.cookie ? document.cookie.split('; ') : [];
4 for (const cookie of cookies) {
5 const [k, ...rest] = cookie.split('=');
6 const v = rest.join('=');
7 if (decodeURIComponent(k) === name) {
8 return decodeURIComponent(v);
9 }
10 }
11 return null;
12};
13
14// Example usage:
15const theme = getCookie('theme'); // => "dark" if set
16console.log('theme cookie:', theme);- هذه الدالة تفك ترميز كلٍّ من اسم ملف تعريف الارتباط وقيمته لمقارنتهما واسترجاعهما بأمان۔ وهي تتعامل مع أسماء ملفات تعريف الارتباط المكررة والمحارف الخاصة المُرمَّزة، مما يجعلها تنفيذًا بسيطًا لكنه قوي۔
فهم وتطبيق السمات الأمنية
لملفات تعريف الارتباط عدة سمات مهمة، تتحكم كل سمة منها في الأمان ونطاق السلوك۔
- تمنع سمة
HttpOnlyالوصول إلى ملف تعريف الارتباط من JavaScript، مما يساعد على التخفيف من هجمات XSS (البرمجة النصية عبر المواقع)۔ لاحظ أنك لا تستطيع تعيينHttpOnlyمن جانب المتصفح۔ - تقصر سمة
Secureإرسال ملفات تعريف الارتباط على بروتوكول HTTPS فقط، مما يقلل خطر التنصت والعبث۔ - تتحكم سمة
SameSiteفي ما إذا كانت ملفات تعريف الارتباط تُرسل مع الطلبات عبر المواقع، وتساعد على منع هجمات CSRF (تزوير الطلبات عبر المواقع)۔ - تحدد سمة
Pathنطاق مسارات الطلبات التي يُرسل معها ملف تعريف الارتباط، وتحدد سمةDomainالنطاق الذي يكون فيه ملف تعريف الارتباط صالحًا۔ - من خلال تعيين سمة
ExpiresأوMax-Age، يمكنك التحكم في انتهاء صلاحية ملف تعريف الارتباط۔
مثال على إضافة SameSite / Secure في المتصفح (المتاح فعليًا)
لا يمكن تعيين سمة HttpOnly على جانب العميل۔ فيما يلي مثال على إضافة SameSite وSecure على جانب العميل۔ ومع ذلك، فإن سمة Secure لا تسري إلا على صفحات HTTPS۔
1// Set cookie with SameSite and Secure attributes (Secure only effective over HTTPS).
2const setCookieWithAttributes = (name: string, value: string) => {
3 const maxAge = 60 * 60 * 24 * 7; // 7 days in seconds
4 // Note: HttpOnly cannot be set from JS; set it on server-side when you want to restrict JS access.
5 document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; `
6 + `Max-Age=${maxAge}; `
7 + `Path=/; `
8 + `SameSite=Lax; `
9 + `Secure`;
10};
11
12setCookieWithAttributes('session_hint', 'true');SameSite=Laxخيار شائع وآمن۔ لمزيد من القيود استخدمSameSite=Strict، ولكن قد تتوقف التنقلات المشروعة القادمة من مواقع خارجية عن العمل۔
تعيين ملفات تعريف الارتباط على الخادم (Set-Cookie) (Node / TypeScript)
على الخادم يمكنك إضافة HttpOnly عبر ترويسة Set-Cookie، لذا يُفضَّل إدارة الجلسات على جانب الخادم۔ أدناه مثال باستخدام وحدة http في Node۔
1// A minimal Node HTTP server in TypeScript that sets a secure HttpOnly cookie.
2// This example uses built-in 'crypto' for a random session id.
3import http from 'http';
4import crypto from 'crypto';
5
6const server = http.createServer((req, res) => {
7 if (req.url === '/login') {
8 const sessionId = crypto.randomBytes(16).toString('hex');
9 // Set cookie with HttpOnly, Secure, SameSite and Path
10 // Expires is optional — Max-Age preferred for relative lifetimes.
11 res.setHeader('Set-Cookie', `sid=${sessionId}; `
12 + `HttpOnly; `
13 + `Secure; `
14 + `SameSite=Strict; `
15 + `Path=/; `
16 + `Max-Age=3600`
17 );
18 res.writeHead(302, { Location: '/' });
19 res.end();
20 return;
21 }
22
23 res.writeHead(200, { 'Content-Type': 'text/plain' });
24 res.end('Hello\n');
25});
26
27server.listen(3000, () => {
28 console.log('Server running on http://localhost:3000');
29});- عند تعيين سمة
HttpOnlyعلى جانب الخادم، يصبح ملف تعريف الارتباط غير قابل للوصول من JavaScript، مما يصعّب على هجمات XSS (البرمجة النصية عبر المواقع) سرقته۔ بالإضافة إلى ذلك، بإضافة سمةSecureبحيث تُرسل ملفات تعريف الارتباط دائمًا عبر HTTPS، يمكنك منع التنصت والعبث وتحسين أمان الاتصال۔
تنفيذ تسلسل/تحليل ملفات تعريف الارتباط (أداة مساعدة على جانب الخادم)
فيما يلي أداة بسيطة على جانب الخادم تبني سلسلة ترويسة Set-Cookie۔
1// Cookie serialization helper for server-side use.
2// Returns a properly formatted Set-Cookie header value.
3type CookieOptions = {
4 path?: string;
5 domain?: string;
6 maxAge?: number;
7 expires?: Date;
8 httpOnly?: boolean;
9 secure?: boolean;
10 sameSite?: 'Strict' | 'Lax' | 'None';
11};
12
13const serializeCookie = (name: string, value: string, opts: CookieOptions = {}): string => {
14 const parts: string[] = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];
15
16 if (opts.maxAge != null) parts.push(`Max-Age=${Math.floor(opts.maxAge)}`);
17 if (opts.expires) parts.push(`Expires=${opts.expires.toUTCString()}`);
18 if (opts.domain) parts.push(`Domain=${opts.domain}`);
19 parts.push(`Path=${opts.path ?? '/'}`);
20 if (opts.httpOnly) parts.push('HttpOnly');
21 if (opts.secure) parts.push('Secure');
22 if (opts.sameSite) parts.push(`SameSite=${opts.sameSite}`);
23
24 return parts.join('; ');
25};
26
27// Example usage:
28const headerValue = serializeCookie('uid', 'abc123', {
29 httpOnly: true,
30 secure: true,
31 sameSite: 'Lax',
32 maxAge: 3600
33});
34console.log(headerValue);
35// => "uid=abc123; Max-Age=3600; Path=/; HttpOnly; Secure; SameSite=Lax"
- تشكل هذه الأداة أساسًا لإنشاء
Set-Cookieعلى خادم مخصص۔ يمكن للمكتبات التعامل مع الكثير من الحالات الحدّية الأخرى۔
منع العبث عبر التواقيع (HMAC)
من الخطر تخزين القيم المهمة مباشرةً في ملفات تعريف الارتباط۔ نقدّم طريقة تُوقّع القيم على الخادم لاكتشاف العبث۔ هنا نستخدم HMAC-SHA256۔ في بيئة الإنتاج، تحتاج أيضًا إلى إدارة إبطال الرموز المميّزة على جانب الخادم۔
1// A simple HMAC signing and verification helper for cookies.
2// Signing prevents client from tampering cookie values.
3import crypto from 'crypto';
4
5const SECRET = 'replace_with_env_secret'; // store in env var in production
6
7const signValue = (value: string) => {
8 const hmac = crypto.createHmac('sha256', SECRET);
9 hmac.update(value);
10 return `${value}.${hmac.digest('hex')}`;
11};
12
13const verifySignedValue = (signed: string): string | null => {
14 const idx = signed.lastIndexOf('.');
15 if (idx === -1) return null;
16 const value = signed.slice(0, idx);
17 const sig = signed.slice(idx + 1);
18
19 const hmac = crypto.createHmac('sha256', SECRET);
20 hmac.update(value);
21 const expected = hmac.digest('hex');
22 // use timing-safe comparison in production
23 if (crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
24 return value;
25 }
26 return null;
27};
28
29// Example usage:
30const signed = signValue('userId=42');
31console.log('signed:', signed);
32console.log('verified:', verifySignedValue(signed)); // => "userId=42"
- بهذه الطريقة، يمكنك التحقق مما إذا كانت قيمة ملف تعريف الارتباط قد تم التلاعب بها۔ لا تضمّن مفتاح التوقيع في الشيفرة المصدرية; قم بإدارته عبر وسائل آمنة مثل متغيرات البيئة۔
ملفات تعريف الارتباط وتخفيف هجمات CSRF (أنماط عامة)
الجلسات التي تستخدم ملفات تعريف الارتباط تحتاج إلى حماية من CSRF۔ تشمل إجراءات التخفيف الشائعة:۔
- عيّن
SameSiteإلىLaxأوStrictلمنع الإرسال عبر المواقع غير الضروري۔ استخدمSameSite=None; Secureفقط عندما يكون التواصل عبر الأصول المختلفة ضرورياً۔ - استخدم الرموز المميّزة لـ CSRF، التي يتحقق منها الخادم، للنماذج وطلبات واجهة برمجة التطبيقات (API)۔ لا تخزّن الرمز في ملف تعريف ارتباط
HttpOnly؛ بل قدّمه عبر جسم الاستجابة أو وسوم الميتا، ودَع JavaScript يقرأه فقط عند الضرورة۔ - إذا كان بالإمكان الوصول إلى الرمز من JavaScript، فتأكد أيضاً من تنفيذ وسائل الحماية من XSS (مثل CSP، وهروب المخرجات، وغيرها)۔
- اطلب إعادة المصادقة للإجراءات الحساسة وعند تسجيل الدخول، وامنع هجمات تثبيت الجلسة۔
فيما يلي مثال لعميل آمن يرسل رمز CSRF في ترويسة HTTP۔ يفترض هذا أن العميل قد استلم الرمز مسبقاً من الخادم، مثلاً ضمن جسم الاستجابة۔
1// Example of sending CSRF token safely in a header using fetch.
2// Assumes CSRF token was provided securely (e.g., via response body or meta tag).
3async function sendProtectedRequest(url: string, csrfToken: string) {
4 const res = await fetch(url, {
5 method: 'POST',
6 credentials: 'same-origin', // include cookies for same-site requests
7 headers: {
8 'Content-Type': 'application/json',
9 'X-CSRF-Token': csrfToken
10 },
11 body: JSON.stringify({ action: 'doSomething' })
12 });
13 return res;
14}- إذا حددت
credentials: 'same-origin'، فسيتم إرسال ملفات تعريف الارتباط الخاصة بنفس الأصل فقط۔ على الخادم، تحقّق من قيمة ترويسةX-CSRF-Tokenوتأكد من تطابق الرمز۔ إذا كانت الطلبات عبر المواقع ضرورية، فقم بتهيئة CORS بعناية۔
CORS (مشاركة الموارد عبر المصادر) وعلاقته بـ credentials
لإرسال واستلام ملفات تعريف الارتباط في اتصال عبر الأصول، يجب على العميل تحديد credentials: 'include' ويجب على الخادم تعيين Access-Control-Allow-Credentials: true۔ ومع ذلك، احصر هذا الإعداد على الأصول الموثوقة ولا تستخدم Access-Control-Allow-Origin: *۔
1async function fetchCrossOriginData() {
2 // Example: cross-origin fetch sending cookies.
3 // Server must set Access-Control-Allow-Credentials: true
4 // and a specific trusted origin.
5 const res = await fetch('https://api.example.com/data', {
6 credentials: 'include', // send cookies only to trusted domains
7 method: 'GET'
8 });
9 return res;
10}- إن الجمع بين CORS وملفات تعريف الارتباط حساس للغاية من منظور أمني۔ أدِر الأصول المسموح بها بصرامة باستخدام قائمة بيضاء، وتجنّب التواصل عبر المواقع غير الضروري۔ وأيضاً، عند استخدام
SameSite=None; Secure، ألزم استخدام HTTPS لمنع هجمات الرجل في الوسط۔
مثال: إعدادات آمنة لملفات تعريف الارتباط الخاصة بالجلسة باستخدام Express (TypeScript)
عمليًا، استخدم Express ومكتبة cookie وexpress-session وما إلى ذلك۔ أدناه مثال بسيط باستخدام express وcookie-parser۔ عمليًا، عيّن secure إلى true وأدِر secret عبر المتغيرات البيئية۔
1// Express example using cookie-parser and setting a secure httpOnly cookie.
2// npm install express cookie-parser @types/express @types/cookie-parser
3import express from 'express';
4import cookieParser from 'cookie-parser';
5
6const app = express();
7app.use(cookieParser(process.env.COOKIE_SECRET));
8
9app.post('/login', (req, res) => {
10 // authenticate user (omitted)
11 const sessionId = 'generated-session-id';
12 res.cookie('sid', sessionId, {
13 httpOnly: true,
14 secure: true, // require HTTPS in production
15 sameSite: 'lax',
16 maxAge: 1000 * 60 * 60 // 1 hour
17 });
18 res.json({ ok: true });
19});
20
21app.listen(3000);- باستخدام ميزة
secretفيcookieParser، يمكنك التعامل بسهولة مع ملفات تعريف الارتباط الموقَّعة۔ ومع ذلك، في التطبيقات الفعلية، ومن منظورَي الأمان وقابلية التوسع، لا ينبغي تخزين البيانات مباشرةً في ملفات تعريف الارتباط؛ بل استخدم مخزن جلسات مخصصًا۔
بادئات ملفات تعريف الارتباط __Host- و__Secure-
تفرض المتصفحات قواعد خاصة لبعض البادئات۔
- بادئة
__Secure-إذا بدأ اسم ملف الارتباط بـ__Secure-فإن سمةSecureمطلوبة۔ - بادئة
__Host-إذا بدأ بـ__Host-فستكونSecureمطلوبة، ويجب أن يكون المسار/(الجذر)، ولا يجوز تعيينDomain۔
يساعد استخدام هذه البادئات على تقليل أخطاء الإعداد وتحسين الأمان۔
أفضل الممارسات لملفات تعريف الارتباط
للتعامل مع ملفات تعريف الارتباط بأمان، ضع النقاط التالية في الاعتبار۔
- لا تُخزّن المعلومات الحساسة مباشرةً في ملفات تعريف الارتباط۔ يفضّل استخدام معرّف جلسة على جانب الخادم لرموز الوصول۔
- اضبط ملفات ارتباط الجلسة مع
HttpOnlyوSecureوSameSite=Lax(أوStrict)۔ - استفد من البادئات مثل
__Host-و__Secure-۔ - فكّر في استخدام التواقيع (HMAC) والتشفير لمنع العبث والتنصّت۔
- فعّل
Secureواشترط HTTPS۔ - استهدف أقل قدر من الامتيازات وفترات صلاحية قصيرة۔
- استخدم رموز CSRF۔
- احذر من اختلافات سلوك
SameSiteبين المتصفحات، خصوصًا القديمة منها۔
مفاهيم خاطئة شائعة حول ملفات تعريف الارتباط
فيما يتعلق بملفات تعريف الارتباط، انتبه لما يلي من مفاهيم خاطئة شائعة۔
- 'إضافة
HttpOnlyتُزيل تأثير XSS.' على الرغم من أنHttpOnlyتمنع JavaScript من الوصول إلى ملفات تعريف الارتباط، لا يزال بالإمكان استخدام XSS لإصدار طلبات اعتباطية۔ ينبغي أيضًا استخدام التحقق من رمز CSRF وCSP (سياسة أمن المحتوى) وتطهير المدخلات۔ - 'سمة
Secureغير ضرورية للتطوير المحلي.' إن محاكاة HTTPS والتحقق من السلوك حتى في البيئات المحلية يحسّن دقة الاختبارات۔ على الحد الأدنى، يجب استخدام HTTPS في بيئات التدريج والإنتاج۔ - 'فترات انتهاء الصلاحية الطويلة مريحة.' إذا عيّنت أعمارًا طويلة لملفات تعريف الارتباط، تزداد الفترة التي يمكن إساءة استخدامها خلالها إذا سُرقت۔ يمكنك تعيين فترات انتهاء أقصر ودمج إعادة المصادقة الدورية وتدوير الرموز المميِّزة۔
الملخص
على الرغم من سهولة استخدام ملفات تعريف الارتباط، فإن سوء التعامل معها قد يسبب ثغرات أمنية۔ لإدارتها بشكل صحيح مع TypeScript، من المهم فهم سمات مثل HttpOnly وSecure وSameSite وفرض إعدادات آمنة على جانب الخادم۔ من خلال عدم تخزين البيانات الحساسة مباشرةً والجمع بين التواقيع وفترات الصلاحية القصيرة، يمكنك تحقيق إدارة جلسات آمنة وموثوقة۔
يمكنك متابعة المقالة أعلاه باستخدام Visual Studio Code على قناتنا على YouTube.۔ يرجى التحقق من القناة على YouTube أيضًا.۔