الثلاثاء، 10 نوفمبر 2026 القاهرة 18.7°C

تأمين REST API في ASP.NET Core: الفرق بين JWT وOAuth 2.0 وAPI Keys عمليًا

غلاف دليل تأمين REST API في ASP.NET Core باستخدام JWT وOAuth 2.0 وAPI Keys
غلاف توضيحي لتأمين REST APIs باستخدام JWT وOAuth 2.0 وAPI Keys في ASP.NET Core.

لو بنيت REST API تعمل بنجاح باستخدام HTTP وJSON، فالخطوة التالية ليست إضافة شاشة Login فقط؛ بل تحديد من يستطيع استدعاء كل Endpoint، وما الذي يُسمح له بالوصول إليه، وكيف تتحقق من صحة بيانات الاعتماد. في هذا الدليل نطبّق المبادئ على ASP.NET Core، ونوضح الفرق الحقيقي بين JWT وOAuth 2.0 وAPI Keys، مع مثال لا يعتمد على إنشاء Token غير آمن داخل التطبيق.

إذا كنت جديدًا على Requests وStatus Codes، ابدأ بمقال ما هي REST API؟ شرح HTTP وJSON للمبتدئين، ثم عُد إلى هنا. هذا المقال يركز على الأمان، وليس إعادة شرح REST من البداية.

الخلاصة: أي تقنية تستخدم لتأمين REST API؟

التقنيةما وظيفتها؟متى تناسبك؟
JWTصيغة Token يمكن التحقق من توقيعها وClaims الخاصة بها؛ ليست بروتوكول تسجيل دخول بحد ذاتهاعندما تصدر جهة هوية موثوقة Access Tokens بصيغة JWT
OAuth 2.0إطار لتفويض الوصول إلى الموارد والحصول على Access Tokensعندما تتكامل تطبيقات أو خدمات مع موفر هوية وتحتاج Scopes
OpenID Connect (OIDC)طبقة هوية فوق OAuth 2.0 لتسجيل الدخول والتحقق من المستخدمعندما تحتاج Sign-in موحدًا
API Keyمفتاح سري يُعرّف عميلًا أو تكاملًا حسب تصميم النظامتكاملات Server-to-Server محدودة، مع ضوابط إضافية

قاعدة مهمة: هذه الخيارات ليست بدائل متكافئة. قد تحصل على JWT عبر OAuth 2.0، وقد تختار جهة الهوية Access Token بصيغة أخرى لا يستطيع تطبيقك فكها محليًا. لا تفترض أن مجرد امتلاك Token يعني أن العميل يملك كل الصلاحيات.

Authentication أم Authorization؟

Authentication يثبت هوية الجهة المتصلة أو صلاحية بيانات اعتمادها. Authorization يقرر إن كانت تلك الجهة مسموحًا لها بقراءة مورد أو تعديله. مستخدم مسجل الدخول قد يستطيع قراءة مهامه فقط، ولا يحق له قراءة مهام مستخدم آخر بمجرد تغيير ID داخل URL. هذه مشكلة Broken Object Level Authorization (BOLA) وتحتاج إلى فحص ملكية المورد أو سياسة وصول في كل عملية حساسة.

كيف يعمل JWT Bearer مع API؟

  1. يسجل العميل دخوله أو يحصل على تفويض من موفر هوية موثوق باستخدام التدفق المناسب.
  2. يرسل العميل Access Token في Header مثل Authorization: Bearer <access-token>.
  3. تتحقق API من التوقيع والـIssuer والـAudience وتاريخ الصلاحية، ثم من صلاحيات الوصول المطلوبة.
  4. ترجع API الاستجابة أو ترفض الطلب، دون إعادة توجيه العميل إلى صفحة Login.

وجود ثلاث مقاطع في JWT لا يضمن صلاحيته؛ الـPayload يمكن قراءته عادة وهو غير مشفر افتراضيًا. لا تُرسل أسرارًا أو كلمات مرور أو بيانات خاصة حساسة داخله، ولا تقبل خوارزمية أو مفتاح توقيع يحدده المرسل دون تحقق آمن.

مثال عملي في ASP.NET Core 10: حماية Endpoint باستخدام JWT صادر من موفر هوية

هذا المثال يفترض أن لديك Identity Provider موثوقًا يصدر JWT Access Tokens ويعلن مفاتيح التحقق عبر HTTPS. التطبيق هنا يتحقق من الـToken ولا يصدره. ثبّت الحزمة المطابقة لإصدار .NET المستهدف:

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

في Program.cs:

using Microsoft.AspNetCore.Authentication.JwtBearer;

var builder = WebApplication.CreateBuilder(args);

var authority = builder.Configuration["Auth:Authority"]
    ?? throw new InvalidOperationException("Missing Auth:Authority");
var audience = builder.Configuration["Auth:Audience"]
    ?? throw new InvalidOperationException("Missing Auth:Audience");

builder.Services
    .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = authority;
        options.Audience = audience;
        options.RequireHttpsMetadata = true;
    });

builder.Services.AddAuthorization();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();

app.MapGet("/api/tasks", () => Results.Ok(new[]
{
    new { Id = 1, Title = "Review pull request" }
})).RequireAuthorization();

app.Run();

احفظ Auth:Authority بعنوان موفر الهوية الفعلي الذي تثق به (مثل https://identity.example.com/ كمثال توضيحي فقط) وAuth:Audience بالقيمة المتفق عليها لـAPI. لا تستنسخ قيمًا وهمية وتتوقع أن يعمل المصادقة. إعداد Authority يتيح لمكوّن JWT Bearer اكتشاف مفاتيح التوقيع والـIssuer، ويتحقق كذلك من صلاحية الـToken والـAudience. تذكّر أن نجاح التحقق لا يلغي الحاجة إلى Authorization لكل عملية ومورد.

اختبار Endpoint محمي

جرّب الطلب بدون Header؛ من المتوقع إرجاع 401 Unauthorized. ثم جرّبه بتوكن صالح صادر للـAudience الصحيح:

curl -i https://api.example.com/api/tasks \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

استخدم https://api.example.com فقط كمكان توضيحي، ولا تنشر توكنًا حقيقيًا في Git أو Screenshot أو Logs. اختبار إضافي مهم: جرّب Token منتهي الصلاحية، وآخر بتوقيع غير موثوق، وثالث موجّه إلى API مختلفة (Audience خاطئ). يجب ألا يمنح أي منها وصولًا ناجحًا.

متى أستخدم OAuth 2.0؟ وما علاقة OIDC به؟

إذا كان تطبيق ويب أو موبايل يعمل نيابة عن مستخدم، فعادة يُستخدم Authorization Code مع PKCE ضمن تصميم مناسب لنوع العميل. أما التواصل بين خدمات دون مستخدم، فقد يناسبه Client Credentials عبر مزود هوية موثوق. في الحالتين لا تضع Client Secrets في تطبيقات JavaScript عامة أو تطبيقات موبايل؛ لا يمكن اعتبارها سرية هناك.

OAuth 2.0 يهتم بالتفويض. أما إثبات هوية المستخدم وتسجيل الدخول فغالبًا يُنفذ عبر OpenID Connect. ولا تستخدم ID Token للوصول إلى API بدل Access Token؛ لكل منهما جمهور وغرض مختلفان.

كيف تُنفذ Authorization بشكل صحيح؟

استخدم Policies وRoles أو Scopes بناءً على عقد جهة الهوية، ولا تحوّل مجرد وجود Claim إلى صلاحية عامة. بعض جهات الهوية تضع Scopes في Claim باسم scope وبعضها في scp، وقد تكون قائمة مفصولة بمسافات. لذلك لا تنقل شرط فحص النص كما هو بين مزودين مختلفين دون اختبار.

حتى لو سمحت Policy بقراءة tasks.read، يجب التحقق أن المهمة المطلوبة تخص المستخدم أو المؤسسة المصرح لها. هذه الصلاحية على مستوى المورد تختلف عن السماح باستدعاء Endpoint عام.

متى تناسب API Keys؟ وما حدودها؟

تصلح API Keys أحيانًا لتكاملات Server-to-Server بسيطة، أو لتعريف العميل وتطبيق Quotas. لكنها ليست بديلًا عامًا عن هوية المستخدم، وليست مناسبة لوضعها داخل Frontend عام أو روابط URL. خزّن المفاتيح في Secret Manager مناسب، وادعم التدوير والإلغاء، وراقب الاستخدام وحدد معدل الطلبات. إن كنت تصمم مخزن مفاتيح بنفسك، لا تُخزن النص السري الصريح دون ضرورة؛ استخدم نمط تخزين وتحقق آمنًا ملائمًا لطبيعة المفاتيح.

لكل المفتاحين والـTokens: استخدم HTTPS، وافصل الاختبار عن الإنتاج، ولا تطبع أسرار الاعتماد في Logs أو أخطاء التطبيق.

ما الفرق بين HTTP 401 و403؟

  • 401 Unauthorized: لم تُقدّم بيانات اعتماد صالحة لهذا المورد، أو انتهت صلاحيتها أو فشل التحقق منها.
  • 403 Forbidden: عُرفت هوية العميل لكن ليس لديه التفويض اللازم لتنفيذ العملية.
  • 404 Not Found: قد يُستخدم أحيانًا لتجنب كشف وجود مورد لا يحق للمستخدم معرفته، بشرط ثبات السياسة.

قائمة اختبار أمان قبل نشر API

  1. هل كل Endpoint حساس محمي بـAuthentication وAuthorization مناسبين؟
  2. هل تُرفض Tokens بتوقيع أو Issuer أو Audience خاطئ أو صلاحية منتهية؟
  3. هل تُفحص ملكية الموارد وTenant Isolation لمنع BOLA؟
  4. هل توجد Rate Limits ورصد لمحاولات التخمين وإساءة الاستخدام؟
  5. هل HTTPS مُفعّل وهل تُخزن الأسرار بعيدًا عن الكود والمستودعات؟
  6. هل توجد اختبارات آلية لـ401 و403 والصلاحيات، ولا تظهر التفاصيل الحساسة في Logs؟
  7. هل العمليات الحساسة محمية من إعادة الإرسال (Replay) أو التكرار بحسب المخاطر؟

هذه قائمة بداية وليست اختبار اختراق شاملًا. اربط الاختبارات التلقائية بمسار CI، وراجع مثال GitHub Actions لبناء واختبار مشروع .NET لإنشاء خطوة Build وTest مع كل Pull Request.

أسئلة شائعة

هل JWT أكثر أمانًا من API Key دائمًا؟

ليس بالضرورة. الأمان يعتمد على آلية إصدار الاعتماد وتخزينه والتحقق منه وحدود صلاحيته وإلغائه. JWT يتيح Claims وتحققًا من توقيع موثوق، لكنه ليس بديلًا عن تصميم الصلاحيات.

هل OAuth 2.0 هو نفسه JWT؟

لا. OAuth إطار تفويض، أما JWT فهي صيغة Token قد يستخدمها موفر الهوية أو لا يستخدمها.

هل أحتاج إنشاء Login داخل كل API؟

ليس بالضرورة. يمكن فصل Authentication في Identity Provider مركزي، وجعل كل API تتحقق من Access Token والصلاحيات المطلوبة.

الخلاصة والمراجع

ابدأ من تحديد المستخدمين والعملاء والموارد المسموح بها، ثم اختر نموذج الهوية والتفويض المناسب. في ASP.NET Core يمكن حماية Endpoints والتحقق من JWT صادرة من موفر هوية موثوق، لكن نجاح التحقق من التوكن ليس إذنًا للوصول إلى أي بيانات. أضف سياسات صلاحيات وفحص ملكية الموارد واختبارات وحماية من سوء الاستخدام.

تعليقات
جارٍ التحميل...