دخول
مصادقة JWT (JSON Web Token) هي آلية مصادقة وتوثيق موحدة ومتناسقة إلى حد ما بين الخادم والعملاء. ميزة JWT هي أنه يسمح لنا بإدارة الحالة بشكل أقل والمقاييس بشكل جيد. ليس من المستغرب أن يتم استخدام التفويض والمصادقة بمساعدته بشكل متزايد في تطبيقات الويب الحديثة.
عند تطوير التطبيقات باستخدام JWT ، غالبًا ما يطرح السؤال: أين وكيف يُنصح بتخزين الرمز المميز؟ إذا قمنا بتطوير تطبيق ويب ، فلدينا خياران من أكثر الخيارات شيوعًا:
- HTML5 Web Storage (localStorage أو sessionStorage)
- بسكويت
بمقارنة هذه الطرق ، يمكننا القول بأن كلاهما يخزن القيم في متصفح العميل ، وكلاهما سهل الاستخدام ويمثلان تخزينًا منتظمًا لأزواج القيمة الرئيسية. الفرق هو في بيئة التخزين.
يمكن الوصول إلى تخزين الويب (localStorage / sessionStorage) عبر جافا سكريبت في نفس المجال. هذا يعني أن أي كود JavaScript في التطبيق الخاص بك لديه حق الوصول إلى تخزين الويب ، وهذا يخلق ثغرة أمنية لهجمات البرمجة النصية عبر المواقع (XSS). كمحرك تخزين ، لا يوفر Web Storage طريقة لتأمين بياناتك أثناء التخزين والمشاركة. لا يمكننا استخدامه إلا للبيانات المساعدة التي نريد الاحتفاظ بها عند التحديث (F5) أو إغلاق علامة التبويب: الحالة ورقم الصفحة ، المرشحات ، إلخ.
يمكن أيضًا نقل الرموز المميزة عبر متصفح ملفات تعريف الارتباط. ملفات تعريف الارتباط المستخدمة مع علامة
httpOnly لا تتأثر XSS. httpOnly هي علامة للوصول إلى القراءة والكتابة وحذف ملفات تعريف الارتباط فقط على الخادم. لن يمكن الوصول إليها من خلال جافا سكريبت على العميل ، لذلك لن يعرف العميل الرمز المميز ، وستتم معالجة التفويض بالكامل من جانب الخادم.
يمكننا أيضًا تعيين إشارة
آمنة للتأكد من أن ملفات تعريف الارتباط تنتقل فقط عبر HTTPS. بالنظر إلى هذه المزايا ، وقع اختياري على ملفات تعريف الارتباط.
توضح هذه المقالة طريقة لتطبيق المصادقة والمصادقة باستخدام ملفات تعريف الارتباط الآمن httpOnly + JSON Web Token في ASP.NET Core Web Api بالاشتراك مع SPA. يعتبر الخيار الذي يكون فيه الخادم والعميل في أصل مختلف.
إعداد بيئة التنمية المحلية الخاصة بك
لتكوين علاقات العميل - الخادم وتصحيحها بشكل صحيح عبر HTTPS ، أوصي بشدة بتكوين بيئة التطوير المحلية على الفور بحيث يكون لكل من العميل والخادم اتصال HTTPS.
إذا لم تقم بذلك على الفور وحاولت بناء علاقات دون اتصال HTTPS ، فستظهر تفاصيل كثيرة رائعة بدونها لن تعمل ملفات تعريف الارتباط الآمنة والسياسات الآمنة الإضافية في الإنتاج مع HTTPS بشكل صحيح.
سأعرض مثالًا لتكوين HTTPS على نظام التشغيل Windows 10 ، الخادم - ASP.NET Core ، SPA - React.
يمكنك تكوين HTTPS في ASP.NET Core باستخدام علامة
التكوين لـ HTTPS عند إنشاء مشروع أو ، إذا لم نقم بذلك عند إنشاء ، قم بتمكين الخيار المناظر في خصائص.
لتكوين SPA ، تحتاج إلى تعديل البرنامج النصي
"للبدء" ،
وتعيينه على "تعيين HTTPS = صواب" . الإعداد الخاص بي كما يلي:
'start': 'set HTTPS=true&&rimraf ./build&&react-scripts start'
أنصحك
بالاطلاع على إعداد HTTPS لبيئة التطوير في البيئات الأخرى على
create-react-app.dev/docs/using-https-in-developmentتكوين خادم ASP.NET الأساسية
إعداد JWT
في هذه الحالة ، فإن التطبيق الأكثر شيوعًا لـ JWT من الوثائق أو من أي مقال ، مع
options.RequireHttpsMetadata = true;
إعدادات إضافية
options.RequireHttpsMetadata = true;
حيث تستخدم بيئة التطوير الخاصة بنا HTTPS:
ConfigureServices services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => { options.RequireHttpsMetadata = true; options.SaveToken = true; options.TokenValidationParameters = new TokenValidationParameters {
تكوين سياسة كورس
هام : يجب أن تحتوي سياسة CORS على
AllowCredentials()
. يعد ذلك ضروريًا لتلقي طلب من
XMLHttpRequest.withCredentials وإرسال ملفات تعريف الارتباط مرة أخرى إلى العميل. سيتم كتابة المزيد حول هذا الموضوع لاحقًا. يتم تكوين الخيارات الأخرى حسب احتياجات المشروع.
إذا كان الخادم والعميل على نفس الأصل ، فلن تكون هناك حاجة إلى التكوين بأكمله أدناه.
ConfigureServices services.AddCors();
تكوين app.UseCors(x => x .WithOrigins("https://localhost:3000")
وضع سياسة ملفات تعريف الارتباط
فرض سياسة ملفات تعريف الارتباط على httpOnly وآمنة.
إذا كان ذلك ممكنًا ، قم بتعيين
MinimumSameSitePolicy = SameSiteMode.Strict;
- يعمل ذلك على تحسين أمان ملفات تعريف الارتباط لأنواع التطبيقات التي لا تعتمد على معالجة الطلبات المشتركة.
تكوين app.UseCookiePolicy(new CookiePolicyOptions { MinimumSameSitePolicy = SameSiteMode.Strict, HttpOnly = HttpOnlyPolicy.Always, Secure = CookieSecurePolicy.Always });
فكرة تبادل رمز آمن
هذا الجزء هو مفهوم. سنقوم بأمرين:
- قم بإدخال الرمز المميز في طلب HTTP باستخدام httpOnly والأعلام الآمنة.
- تلقي والتحقق من صحة الرموز تطبيق العميل من طلب HTTP.
للقيام بذلك ، نحتاج إلى:
- اكتب الرمز المميز في ملف تعريف الارتباط httpOnly عند تسجيل الدخول وحذفه من هناك عند تسجيل الدخول.
- إذا كان هناك رمز مميز في ملفات تعريف الارتباط ، فاستبدل الرمز المميز في رأس HTTP لكل طلب لاحق.
- إذا لم يكن هناك رمز مميز في ملفات تعريف الارتباط ، فسيكون العنوان فارغًا ، ولن يتم اعتماد الطلب.
الوسيطة
الفكرة الرئيسية هي تطبيق Custom Middleware لإدراج رمز مميز في طلب HTTP وارد. بعد إذن المستخدم ، نقوم بحفظ ملف تعريف الارتباط تحت مفتاح معين ، على سبيل المثال:
".AspNetCore.Application.Id" . أوصي بتعيين اسم لا علاقة له بالتفويض أو الرموز - في هذه الحالة ، سيبدو ملف تعريف الارتباط مع رمز مميز وكأنه نوع من نظام ثابت
غير ملحوظ لتطبيق AspNetCore. لذلك ، هناك فرصة أكبر لأن يرى المهاجم العديد من متغيرات النظام ، ودون فهم آلية التخويل المستخدمة ، سوف يذهب أبعد من ذلك. بالطبع ، إذا لم يقرأ هذا المقال ولم يبحث على وجه التحديد عن مثل هذا الثابت.
بعد ذلك ، نحتاج إلى إدراج هذا الرمز المميز في جميع طلبات HTTP الواردة التالية. للقيام بذلك ، سنقوم بكتابة بضعة أسطر من كود الوسيطة. هذا ليس سوى خط أنابيب HTTP.
تكوين app.Use(async (context, next) => { var token = context.Request.Cookies[".AspNetCore.Application.Id"]; if (!string.IsNullOrEmpty(token)) context.Request.Headers.Add("Authorization", "Bearer " + token); await next(); }); app.UseAuthentication();
يمكننا أن نأخذ هذا المنطق إلى خدمة الوسيطة منفصلة حتى لا تسد Startup.cs ، فإن الفكرة لن تتغير.
من أجل كتابة القيمة في ملفات تعريف الارتباط ، نحتاج فقط إلى إضافة السطر التالي إلى منطق التفويض:
if (result.Succeeded) HttpContext.Response.Cookies.Append(".AspNetCore.Application.Id", token, new CookieOptions { MaxAge = TimeSpan.FromMinutes(60) });
باستخدام سياسات ملفات تعريف الارتباط الخاصة بنا ، سيتم إرسال ملفات تعريف الارتباط هذه تلقائيًا على أنها httpOnly وآمنة. لا حاجة لإعادة تحديد سياستهم في خيارات ملفات تعريف الارتباط.
في CookieOptions ، يمكنك ضبط MaxAge لتحديد العمر. من المفيد تحديد جنبا إلى جنب مع JWT Lifetime عند إصدار الرمز المميز بحيث يختفي ملف تعريف الارتباط بعد فترة من الوقت. يتم تكوين خصائص أخرى لـ CookieOptions اعتمادًا على متطلبات المشروع.
لمزيد من الأمان ، أوصي بإضافة الرؤوس التالية إلى الوسيطة:
context.Response.Headers.Add("X-Content-Type-Options", "nosniff"); context.Response.Headers.Add("X-Xss-Protection", "1"); context.Response.Headers.Add("X-Frame-Options", "DENY");
- يتم استخدام رأس X-Content-Type-Options للحماية من نقاط الضعف في MIME. يمكن أن تحدث مشكلة عدم الحصانة هذه عندما يسمح موقع ما للمستخدمين بتنزيل المحتوى ، لكن المستخدم يخفي نوعًا معينًا من الملفات كشيء آخر. هذا يمكن أن يعطي المهاجمين القدرة على تنفيذ البرامج النصية عبر المواقع النصية أو تسوية موقع الويب.
- تحتوي جميع المتصفحات الحديثة على إمكانات تصفية XSS مضمنة تحاول التقاط ثغرات XSS قبل عرض الصفحة بالكامل لنا. بشكل افتراضي ، يتم تمكينها في المستعرض ، ولكن قد يكون المستخدم أكثر صعوبة وتعطيلها. باستخدام رأس X-XSS-Protection ، يمكننا في الواقع إخبار المستعرض بتجاهل ما قام به المستخدم وتطبيق المرشح المدمج.
- تخبر X-Frame-Options المتصفح أنه إذا تم وضع موقعك داخل إطار HTML ، فلا تعرض أي شيء. هذا مهم جدًا عند محاولة حماية نفسك من محاولات clickjacking.
لقد وصفت بعيدا عن كل العناوين. هناك الكثير من الطرق لتحقيق أمان تطبيق ويب أكبر. أنصحك بالتركيز على قائمة التحقق من الأمان من مورد securityheaders.com.
إعداد عميل SPA
عندما يكون العميل والخادم موجودان في أصل مختلف ، يلزم أيضًا تكوين إضافي على العميل. يجب عليك التفاف كل طلب باستخدام
XMLHttpRequest.withCredentials .
لقد اختتمت أساليبي كما يلي:
import axios from "axios"; const api = axios.create({ baseURL: process.env.REACT_APP_API_URL }); api.interceptors.request.use(request => requestInterceptor(request)) const requestInterceptor = (request) => { request.withCredentials = true; return request; } export default api;
يمكننا التفاف تهيئة طلبنا بأي شكل من الأشكال ، والشيء الرئيسي هو أن
withCredentials = صحيح هناك .
تحدد خاصية
XMLHttpRequest.withCredentials ما إذا كان ينبغي إنشاء طلبات عبر المجال باستخدام بيانات اعتماد مثل ملفات تعريف الارتباط أو رؤوس التخويل أو شهادات TLS.
تُستخدم هذه العلامة أيضًا لتحديد ما إذا كان سيتم تجاهل ملفات تعريف الارتباط المرسلة في الاستجابة أم لا. لا يمكن لـ XMLHttpRequest من مجال آخر تعيين ملف تعريف ارتباط على المجال الخاص به إذا لم يتم تعيين علامة withCredential على "true" قبل إنشاء هذا الطلب.
بمعنى آخر ، إذا لم تحدد هذه السمة ، فلن يتم حفظ ملف تعريف الارتباط الخاص بنا من خلال المتصفح ، أي لن نتمكن من إرسال ملف تعريف الارتباط مرة أخرى إلى الخادم ، ولن يعثر الخادم على ملف تعريف الارتباط المطلوب مع JWT ولن نوقع رمز Bearer في رمز HTTP الخاص بنا.
ما كل هذا؟
أعلاه ، لقد وصفت طريقة مقاومة XSS لتبادل الرموز. دعنا نذهب وننظر في نتيجة وظيفة تنفيذها.
إذا ذهبت إلى أدوات مطوّري البرامج ،
فسنرى الأعلام
المرغوب فيها httpOnly وآمنة :

لنقم باختبار سحق ، حاول إخراج ملفات تعريف الارتباط من العميل:

نحن نراقب "، أي لا يمكن الوصول إلى ملفات تعريف الارتباط من مساحة المستند ، مما يجعل من المستحيل قراءتها باستخدام البرامج النصية.
يمكننا محاولة الحصول على ملفات تعريف الارتباط هذه بمساعدة أدوات أو إضافات إضافية ، ولكن كل الأدوات التي جربتها استدعت التطبيق الأصلي من مساحة المستند.
مشروع تجريبي
تعليمات بدء التشغيل في README.MD
محدث: الحماية ضد CSRF
تكوين خادم ASP.NET الأساسية
خدمات الوسيطةXsrfProtectionMiddleware.cs public class XsrfProtectionMiddleware { private readonly IAntiforgery _antiforgery; private readonly RequestDelegate _next; public XsrfProtectionMiddleware(RequestDelegate next, IAntiforgery antiforgery) { _next = next; _antiforgery = antiforgery; } public async Task InvokeAsync(HttpContext context) { context.Response.Cookies.Append( ".AspNetCore.Xsrf", _antiforgery.GetAndStoreTokens(context).RequestToken, new CookieOptions {HttpOnly = false, Secure = true, MaxAge = TimeSpan.FromMinutes(60)}); await _next(context); } }
MiddlewareExtensions.cs public static class MiddlewareExtensions { public static IApplicationBuilder UseXsrfProtection(this IApplicationBuilder builder, IAntiforgery antiforgery) => builder.UseMiddleware<XsrfProtectionMiddleware>(antiforgery); }
ConfigureServices services.AddAntiforgery(options => { options.HeaderName = "x-xsrf-token"; }); services.AddMvc();
تكوين app.UseAuthentication(); app.UseXsrfProtection(antiforgery);
إعداد SPA
api.axios.js import axios from "axios"; import cookie from 'react-cookies'; const api = axios.create({ baseURL: process.env.REACT_APP_API_URL }); api.interceptors.request.use(request => requestInterceptor(request)) const requestInterceptor = (request) => { request.headers['x-xsrf-token'] = cookie.load('.AspNetCore.Xsrf') return request; } export default api;
استخدام
لحماية أساليب API الخاصة بنا ، يجب إضافة السمة
[AutoValidateAntiforgeryToken]
لوحدة التحكم أو
[ValidateAntiForgeryToken]
لهذه الطريقة.