# تجهيز نشر إصلاحات أمن API

هذه القائمة تخص إصلاحات 16 أكتوبر 2026. الإصلاحات محلية حتى يُنشر إصدارها ويُفحص السيرفر. لا تتضمن الخطة تشغيل migrations أو تعديل بيانات التينانتات تلقائيًا.

## تغييرات العقد التي تحتاج توافق المستهلكين

| العملية | السلوك الجديد | الإجراء قبل إطلاق التدفق |
| --- | --- | --- |
| دعوة موظف موجود | تعيين متجر فقط؛ إرسال name/password لتغيير هويته يُرفض 422 | أرسل user_id أو email مع الدور والمتاجر دون بيانات الدخول؛ استخدم تعديل الحساب المأذون منفصلًا |
| بيانات العميل الشخصية | تغيير الاسم والبريد والهاتف متاح بجلسة عميل صالحة، دون كلمة المرور أو إعادة دخول أو تأكيد OTP إضافي. صيغ نفس الرقم/البريد تحفظ الهوية وعلامة توثيقها | التوكنات الحالية والقديمة تظل قابلة للاستخدام حتى انتهائها أو إبطالها المعتاد؛ لا حاجة لإعادة الدخول بعد رفع التحديث |
| تغيير كلمة المرور وprofile الإدارة | يحتاج current_password الصحيح عند تغيير بيانات الدخول | أضف الحقل للنماذج المعنية ولا تحفظه محليًا أو تسجله |
| تغيير كلمة مرور المستخدم نفسه من شاشة الموظفين | يُرفض؛ المسار الموثق لتغيير كلمة مرور الإدارة هو /api/v1/admin/auth/password | استخدم نافذة تغيير كلمة المرور الحالية |
| تعديل دور مشترك | تغيير الاسم أو الصلاحيات يحتاج إدارة كل متجر عُيّن فيه الدور، ويُرفض نزع صلاحية آخر مدير | استخدم مديرًا مأذونًا بكل المتاجر المتأثرة أو Super Admin؛ الأدوار مشتركة داخل التينانت |
| قسائم المحفظة الجديدة | عند غياب code يُولَّد كود 24 حرفًا؛ اليدوي الجديد لا يقل عن 16 | اعرض الكود الذي أعاده API؛ القسائم القديمة تبقى صالحة |
| مكتبة الوسائط | JPEG/PNG/WebP/GIF/PDF فقط؛ رفض HTML/SVG وملفات التنفيذ | يمكن تحسين accept في منتقي admin بإزالة SVG؛ رفض API نافذ حتى قبل هذا التحسين |
| تقديم الوسائط القديمة | المحتوى غير الصوري الآمن ينزل كـ attachment مع sandbox | اختبر روابط PDF والتنزيل والصور على نطاق الوسائط الفعلي |

فُحصت قراءةً مسارات profile في `front/3lcost/src/app/api/auth/profile/route.ts` و`front/yaboow/src/app/api/auth/profile/route.ts`، وواجهات `lib/auth/laravel.ts` في 3lcost وkalemat وyaboow؛ لا تمرر `current_password` حاليًا. تغيير كلمة المرور من الواجهات يحتاج تمرير هذا الحقل. ملفات `front/` لم تُعدّل. سياسة API الحالية بتاريخ 20 أكتوبر تتيح لعميل مسجل الدخول تعديل الاسم والبريد والهاتف طوال صلاحية جلسته، مهما كان عمرها أو طريقة الدخول، دون طلب إعادة مصادقة. تستمر حماية `auth:sanctum` وعزل العميل عن باقي الحسابات والتينانتات والتحقق من عدم تكرار البريد/الهاتف. عند تغيير جهة التواصل تُزال علامة توثيقها وتُبطل الجلسات الأخرى، وتظل الجلسة الحالية صالحة للتعديلات المتتابعة. تغيير كلمة المرور وحسابات الموظفين والإدارة يحتفظ بشرط كلمة المرور الحالية.

## الإعدادات الجديدة

كل القيم أدناه افتراضية بالدقيقة، عدا مكان حفظ العدادات. اضبطها في بيئة العملية ثم أعِد بناء config cache ضمن إجراء النشر المعتاد.

| المتغير | القيمة | الغرض |
| --- | --- | --- |
| ECOMMERCE_RATE_LIMIT_API | 300 | سقف API العام لكل IP داخل tenant، مع استثناء probes وcallbacks ذات ميزانية مستقلة |
| ECOMMERCE_RATE_LIMIT_AUTH | 10 | سقف هوية الدخول لكل عملية؛ تغيير IP وحده لا يصفّر العداد |
| ECOMMERCE_RATE_LIMIT_AUTH_IP | 60 | سقف IP مستقل عبر عمليات المصادقة في tenant |
| ECOMMERCE_RATE_LIMIT_CONTACT | 8 | رسائل التواصل لكل متجر وIP |
| ECOMMERCE_RATE_LIMIT_CONTACT_IP | 30 | ميزانية تواصل IP عبر متاجر tenant |
| ECOMMERCE_RATE_LIMIT_REDEEM | 5 | محاولات استرداد قسيمة لكل مستخدم |
| ECOMMERCE_RATE_LIMIT_REDEEM_IP | 30 | سقف استرداد مستقل لكل IP |
| ECOMMERCE_RATE_LIMIT_SEARCH | 60 | البحث من جميع مداخله لنفس tenant وIP |
| ECOMMERCE_RATE_LIMIT_CHECKOUT / PAYMENT / REVIEWS | 20 لكل متغير | حدود العمليات المتخصصة |
| ECOMMERCE_RATE_LIMIT_WEBHOOK | 120 | ميزانية callbacks/webhooks مجمعة لكل IP، منفصلة عن التصفح |
| RATE_LIMIT_CACHE_STORE | CACHE_STORE عند غيابه | اسم cache store دائم للعدادات؛ لا تستخدم array أو null في الإنتاج |
| ADMIN_API_TOKEN_TTL_MINUTES | 720 | انتهاء صريح لتوكنات الإدارة الجديدة بعد 12 ساعة |
| CUSTOMER_API_TOKEN_TTL_MINUTES | 43200 | انتهاء صريح لتوكنات العملاء الجديدة بعد 30 يومًا، ومنها OTP وguest checkout |

سقف 300 للتصفح نقطة بداية تسمح تقريبًا بعشر زيارات صفحة ذات 30 طلبًا في الدقيقة. هذا افتراض ضبط أولي، وليس قياسًا لزيارات الإنتاج. افحص صفحات الكتالوج ولوحة التحكم وتزامن العملاء خلف NAT قبل اعتماد القيم. كذلك راجع حجم callbacks الصحيحة من IP مشترك لمزود الدفع؛ قد تحتاج الميزانية 120 إلى رفع مبني على قياس.

عند تعدد instances، يجب أن تتشارك العدادات backend واحدًا. `file` محلي لكل مضيف ولا يجمع عدة خوادم. إذا استخدمت database، راجع connection وتوفر جدول cache وترتيب تبديل tenant، واختبر استمرار العداد بين HTTP requests وworkers مختلفين. المفاتيح تشمل namespace للتينانت ولا تعرض البريد أو OTP أو كلمة المرور. اختُبر التطبيق على array وعلى file cache مشترك مع إعادة إنشاء limiter؛ لم يُختبر اتصال database/Redis الإنتاجي أو تزامنه الفعلي.

## انتقال التوكنات والأدوار القائمة

- إصدار توكن جديد له `expires_at` محدد. لم يُضبط global Sanctum expiration على سقف يسجل خروج المستخدمين القدامى تلقائيًا، ولم تُحذف أي توكنات من قواعد التشغيل. التوكن القديم عديم الانتهاء يظل كذلك حتى يُبطل أو تُنفذ سياسة انتقال معتمدة.
- قبل النشر اجمع أعدادًا فقط للتوكنات ذات `expires_at IS NULL` لكل تينانت من بيئة إدارة مأذونة؛ لا تعرض قيم token أو secrets. اعتمد مدة سماح وإبلاغًا مناسبًا للمستخدمين قبل أي إجراء يقطع جلساتهم.
- جهّز إجراءً منفصلًا ومحدودًا لكل تينانت لإضافة انتهاء للتوكنات القديمة أو إبطالها بعد مهلة معلومة. راجع أثره على التطبيقات والموبايل وguest checkout. لا تنفّذه ضمن إصلاحات الكود أو migration جماعية؛ لا تعتبر خطر التوكن القديم مغلقًا قبل تنفيذ الانتقال.
- بعد تغيير كلمة المرور أو بيانات الدخول بإعادة تحقق، يبقى PAT الحالي وتُبطل التوكنات الأخرى للمستخدم فقط. reset كلمة المرور يبطل الجميع. تغيير مدير مأذون كلمة مرور موظف آخر يبطل جلسات الموظف وحده.
- مزامنة الأدوار تحفظ الصلاحيات القائمة، بما فيها القائمة الفارغة؛ غياب الصلاحيات لا يمنح الكتالوج ضمنيًا. راجع أي أدوار قديمة لم تُهيأ: يلزم تعيين صلاحياتها بعملية إدارة صريحة ومراجعة، لا seed يمحو تخصيصات الأدوار. `roles.is_system` ليس شرط تشغيل للإصلاح.

## تحقق الاستضافة

- افحص PHP وامتداداته بـ `composer check-platform-reqs` على الاستضافة. الفحص المحلي نجح على PHP 8.4.15؛ ليس إثباتًا لتوافق cPanel. المسار الأساسي المعروف للتطبيق `/home/pixipinestore/public_html/ecommerce`؛ تحقق من document root الفعلي بدل افتراضه.
- ثبّت الاعتماديات من `composer.lock` المحدّث وفق إجراء النشر المعتاد. التحديث المحلي تم مع `--no-scripts` ولم يشغل migrations. راجع scripts قبل تفعيلها على السيرفر، ولا تشغّل setup.
- أعد `composer audit --locked --no-interaction` و`composer validate --no-check-publish`. نتيجة 16 أكتوبر كانت صفر تنبيهات معروفة بعد الإصلاح؛ أعد الفحص وقت النشر لأن قاعدة التنبيهات تتغير.
- تأكد من `APP_DEBUG=false`. الوضع المعتاد هو ترك `OTP_DEFAULT_CODE` فارغًا. يمكن السماح به كـ Master OTP للتحقق من تسجيل دخول العملاء في staging/production بإعداد `OTP_ALLOW_DEFAULT_IN_PRODUCTION=true` صراحةً؛ حينها فحص `app:validate-environment` يعطي WARN، لأن معرفة الكود تسمح بالدخول لحساب عميل. دون التفعيل يتجاهله التحقق ويعلن الفحص FAIL. الأكواد المُرسلة عشوائية دائمًا ولا تساوي الماستر المفعّل، ولا يُعرض الماستر في أي API أو يُستخدم لحذف الحساب. تظل صلاحية التحدي وحدود المحاولات ونطاق المتجر والتينانت مطلوبة عند استخدامه. بعد تعديل إعدادات السيرفر حدّث config cache بالإجراء المعتاد (`php artisan config:cache`). لا تشغّل أي أمر على tenant production قبل فهم اتصاله ونطاقه.
- افحص HTTPS فعليًا وروابط callbacks والاستعادة. اضبط HSTS وإعادة التوجيه في طبقة الاستضافة الملائمة بعد التأكد من TLS؛ لا تفرض تحويلًا يكسر callbacks خلف proxy.
- TrustProxies غير مُهيأ بعناوين الإنتاج في هذا التغيير. حدّد عناوين أو CIDRs البروكسي الموثوق فعلًا، ولا تثق بـ `*` أو `X-Forwarded-For` من عميل مباشر. اختبارات HTTP أثبتت أن الهيدر غير الموثوق لا يصفر عداد IP، وأن بروكسي اختبار محدد يقرأ IP العميل الصحيح.
- أضف حد ingress في CDN/WAF أو reverse proxy قبل PHP لحماية تهيئة Request وحل tenant، مع استثناءات مدروسة للمراقبة والبوابات. محدد التطبيق لا يحمي العمل الذي يحدث قبل وصول الطلب إليه.
- افحص أن `.env` ومجلدات private والمصدر والأرشيفات ليست قابلة للتنزيل من web root. امنع تنفيذ PHP وأي handlers للسكربتات في مجلدات uploads/storage/الوسائط، وامنع directory listing.
- اختبر aliases وsymlinks وCDN أو static server التي تقدم الوسائط مباشرة. حماية `PublicMediaController` لا تنطبق على مسار يتجاوزه؛ امنع HTML/SVG النشط من origin التطبيق أو قدّمه كتنزيل مع عزل، وامسح نسخ cache القديمة للروابط المتأثرة عند النشر دون حذف بيانات أصلية.
- راجع CORS origins المطلوبة فعلًا. مصادقة bearer لا تجعل wildcard وحده اختراقًا، لكنه لا يحل محل المصادقة أو الصلاحيات. `Retry-After` وترويسات المعدل مكشوفة للمستهلكين عبر CORS.

## تحقق الإصدار والرجوع

أُقفل التحقق المحلي في 17 أكتوبر 2026: أُصلح تجهيز BackendQa بربط tenant اختبار صريح بعد تأكيد SQLite داخل الذاكرة، واكتمل التوثيق المطلوب، وثُبّت زمن سيناريو production OTP مع تشديد توقع الرفض. المجموعة الشاملة النهائية نجحت: **1639 حالة، 1637 ناجحة، 8203 assertion، بلا فشل أو أخطاء، وحالتان MySQL متخطاتان**. التفاصيل وأوامر الإعادة في [سجل الخطة](SECURITY_REMEDIATION_PLAN.md). لم يتغير guard الخاص بالتينانت أو كود seeding التشغيلي. اختبارات MySQL تحتاج قواعد release مستقلة، ونجاح الاختبارات المحلية لا يثبت تنفيذ فحوص الإنتاج أو الواجهة.

Paymob أصبح يقرأ `obj` ويتحقق من حقول Transaction HMAC الموثقة، ويربط الدفع بـ `order.id` الذي يغطيه التوقيع، مع فحص المبلغ والعملة وحالات الانتظار والتفويض/refund/void قبل اعتبار الدفع مدفوعًا. الصيغة المبسطة القديمة التي كانت الاختبارات تصنعها ليست عقد Paymob الرسمي، واستُبدلت بعينة موثقة. المرجعان: [شكل callback](https://developers.paymob.com/paymob-docs/developers/transaction-callbacks) و[حساب HMAC](https://developers.paymob.com/paymob-docs/developers/webhook-callbacks-and-hmac/hmac/hmac-transaction-callback). تحقق في sandbox من طرق الدفع المفعلة على حساب التاجر، خصوصًا nullable/missing source_data؛ الأشكال غير المعروفة تفشل مغلقة ولا تُخمن صيغة التوقيع. Card-token callbacks ليست تحويلًا إلى paid ولا تعالجها هذه الدالة. مسار التحقق عبر API المزود يفحص أيضًا provider order وtransaction ID والمبلغ والعملة والحالة، ويحتفظ بمرجع الطلب للتكرارات اللاحقة؛ لا يكفي success منفردًا أو pending=false لتحويل الدفع إلى paid.

نفّذ اختبارًا عاديًا محدودًا بعد النشر: دخول الإدارة والعميل، قراءة كتالوج، رفض إجراء غير مأذون، تغيير بيانات دخول بحساب اختبار مخصص، وتنزيل صورة/PDF. استخدم sandbox الدفع المصرح به للتحقق من callback دون تفعيل بوابات على متجر الكويت المحلي. افحص 429 وRetry-After دون اختبار ضغط على الإنتاج، وراقب 401 و403 و429 والأحداث المالية المرفوضة دون تسجيل بيانات الدخول.

احتفظ بإصدار سابق معروف وإعداداته قبل النشر. العودة لإصدار قديم قد تعيد عيوب الصلاحيات والدفع، فتحتاج تقييمًا قبل التنفيذ. لا تستخدم reset لملفات المستخدم ولا rollback لبيانات المتاجر. لا يوجد deploy أو commit أو push ضمن هذا التنفيذ المحلي.
