# Backend Audit Report

تاريخ المراجعة: 28 أبريل 2026

## ملخص تنفيذي

تمت مراجعة مبدئية للـ Laravel backend مع التركيز على المخاطر التي قد تسبب مشكلة كبيرة في الإنتاج: security، performance، data integrity، وoperational readiness.

أخطر ما ظهر في المراجعة هو وجود secrets داخل الكود أو داخل API responses، ثم وجود مسارات admin/logger/API تعتمد على حماية غير موحدة، ثم مشاكل payment/checkout قد تترك order state غير صحيح عند حدوث failure أو callback غير موثوق. من ناحية الأداء، أكبر hotspots موجودة في course details، home/categories APIs، certificate checker jobs، وحسابات completion التي تعمل queries كثيرة داخل loops.

الأولوية الأولى يجب أن تكون: تدوير كل الأسرار المكشوفة، منع عرض Zoom/Bunny/EasyKash secrets نهائيا، تشديد حماية admin/logger/API routes، ثم إصلاح checkout/payment داخل transactions وبـ webhook verification.

## نطاق المراجعة

الملفات التي تمت مراجعتها شملت:

- `routes/api.php`
- `routes/logger.php`
- `app/Providers/RouteServiceProvider.php`
- `app/Http/Controllers/API/CheckoutController.php`
- `app/Http/Controllers/EasyKashController.php`
- `app/Http/Controllers/API/CourseController.php`
- `app/Http/Controllers/API/ExamController.php`
- `app/Http/Controllers/API/ViewVideoController.php`
- `app/Http/Controllers/API/UploadAssignmentController.php`
- `app/Http/Controllers/API/HomeController.php`
- `app/Http/Resources/Api/CourseDetailsResource.php`
- `app/Http/Resources/Api/CategoryResource.php`
- `app/Transformers/V1/ZoomTransformer.php`
- `app/Models/Course/CourseAccessor.php`
- `app/Models/Video/VideoAccessor.php`
- `app/Models/User/User.php`
- `app/Exceptions/Handler.php`
- `app/Jobs/StudentCourseCertificateChecker.php`
- `app/Helpers/Helpers.php`
- `config/queue.php`
- `config/l5-swagger.php`
- `.env.example`
- `composer.json`

## Critical Findings

### 1. Secrets مكشوفة داخل API response الخاص بـ Zoom

الدليل:

- `app/Transformers/V1/ZoomTransformer.php`

المشكلة:

`ZoomTransformer` يرجع قيم مثل Zoom API key/secret/email/password/SDK secret داخل response. أي مستخدم يقدر يوصل للـ endpoint الذي يستخدم هذا transformer قد يحصل على credentials حساسة.

الأثر:

- اختراق Zoom account أو استخدامه خارج النظام.
- تسريب credentials في browser/devtools/logs/proxies.
- صعوبة التحكم في الوصول لأن السر خرج من backend إلى client.

أفضل حل:

- إزالة كل الحقول السرية من transformer.
- إرجاع بيانات آمنة فقط مثل `join_url`, `topic`, `start_time`, `duration`.
- نقل Zoom config إلى `config/services.php` واستخدام `config()` بدل `env()` داخل التطبيق.
- تدوير Zoom credentials فورا لأن الكود الحالي يعتبرها compromised.

### 2. EasyKash authorization token hardcoded في الكود

الدليل:

- `app/Http/Controllers/API/CheckoutController.php`
- `app/Http/Controllers/EasyKashController.php`

المشكلة:

EasyKash authorization header مكتوب مباشرة داخل source code.

الأثر:

- أي شخص لديه نسخة من repository أو build artifacts يقدر يستخدم integration token.
- لو repository اتسرب أو اتشارك مع مطورين/سيرفرات متعددة، token يعتبر مكشوف.
- صعب تدوير وتغيير credentials بدون deploy.

أفضل حل:

- نقل token إلى `.env` عبر `config/services.php`.
- تدوير EasyKash token عند مزود الدفع فورا.
- منع طباعة response/errors التي تحتوي على payment details في logs.
- إضافة integration test يضمن أن الكود لا يحتوي على authorization token hardcoded.

### 3. Bunny/CDN signing secret hardcoded

الدليل:

- `app/Models/Course/CourseAccessor.php`
- `app/Models/Video/VideoAccessor.php`

المشكلة:

مفتاح توقيع روابط Bunny/CDN مكتوب مباشرة داخل accessors.

الأثر:

- أي شخص يحصل على الكود قد يوقع روابط فيديوهات بنفسه.
- إمكانية إساءة استخدام مكتبة الفيديو أو تجاوز آلية التحكم في الوصول.
- لا يوجد فصل بين environments ولا يمكن تدوير السر بسهولة.

أفضل حل:

- نقل secret إلى `config/services.php`.
- تدوير secret الحالي.
- تقليل مدة صلاحية الروابط حسب الحاجة.
- التأكد أن URLs لا يتم توليدها إلا بعد authorization واضح للمستخدم.

### 4. إعدادات Composer تسمح باتصالات غير آمنة

الدليل:

- `composer.json`

المشكلة:

الإعدادات تحتوي على `disable-tls: true` و `secure-http: false`.

الأثر:

- تثبيت packages عبر اتصال غير آمن قد يفتح باب supply-chain attacks.
- خطر كبير عند تشغيل `composer install/update` على بيئات CI أو production.

أفضل حل:

- إزالة `disable-tls: true`.
- ضبط `secure-http: true`.
- تشغيل `composer validate`.
- تثبيت dependencies من مصادر موثوقة فقط.

## High Findings

### 5. Admin routes ليست محمية globally

الدليل:

- `app/Providers/RouteServiceProvider.php`
- `app/Http/Controllers/Dashboard/ExamController.php`
- `app/Http/Controllers/Dashboard/QuestionController.php`

المشكلة:

`/admin` route group يستخدم middleware `web` فقط، والحماية متروكة لكل controller. في `ExamController` و `QuestionController` الـ constructor الخاص بـ `auth` و `permission` معمول له comment.

الأثر:

- أي controller ينسى middleware قد يفتح CRUD حساس.
- خطر تعديل exams/questions أو قراءة results بدون authorization.
- صعب التأكد من حماية النظام مع نمو عدد controllers.

أفضل حل:

- إضافة حماية موحدة على admin route group: `web`, `auth`, وربما middleware خاص بـ role/admin.
- إعادة تفعيل permission middleware داخل controllers أو استخدام policies/gates.
- عمل route audit باستخدام `php artisan route:list` للتأكد أن كل admin endpoints محمية.

### 6. Logger/activity routes بدون middleware واضح

الدليل:

- `routes/logger.php`
- `app/Providers/RouteServiceProvider.php`

المشكلة:

logger routes تعمل داخل `web` فقط وتشمل عرض logs وdelete/clear/restore actions.

الأثر:

- تسريب access logs، IPs، users، routes، أو بيانات تشغيل حساسة.
- إمكانية حذف أو تعديل audit trail لو لم يكن package يفرض auth داخليا.

أفضل حل:

- لف routes بـ `auth` و admin permission.
- تفعيل roles/permissions في إعدادات package إن كانت مدعومة.
- أو تعطيل package routes وإعادة تعريفها داخل admin محمي.

### 7. Payment callback trust model يحتاج تقوية

الدليل:

- `app/Http/Controllers/EasyKashController.php`

المشكلة:

callback يقرأ `customerReference` من query string، يعمل inquiry، ثم يحدث order status. لا يوجد signature/HMAC verification واضح، ولا idempotency handling، والredirect URLs hardcoded على frontend domain معين.

الأثر:

- replay أو forged callbacks قد تسبب تحديثات خاطئة لو inquiry provider misconfigured أو حصل race.
- صعوبة تشغيل staging/production domains بدون تعديل code.
- احتمالية تغيير order state أكثر من مرة بدون audit واضح.

أفضل حل:

- استخدام server-to-server webhook مع signature verification من مزود الدفع.
- تخزين payment transaction/reference وتطبيق idempotency.
- التحقق من amount/currency/order ownership قبل اعتماد order.
- نقل redirect base URL إلى config.

### 8. Checkout flow غير محاط بـ database transaction

الدليل:

- `app/Http/Controllers/API/CheckoutController.php`

المشكلة:

الكود ينشئ `Order` ثم `OrderItem` ثم يحدث السعر، وبعدها يبدأ payment request. لا يوجد `DB::transaction`، ويوجد `Course::find()` و `ProfessionalCertificate::find()` بدون فشل واضح لو ID غير موجود.

الأثر:

- partial orders أو order items ناقصة عند حدوث exception.
- أخطاء null dereference قد تظهر للمستخدم أو تترك بيانات غير مكتملة.
- price/discount consistency قد تتأثر أثناء concurrency.

أفضل حل:

- لف إنشاء order/items/price update داخل `DB::transaction`.
- استخدام `findOrFail` أو validation مسبق لكل IDs.
- حساب الأسعار والخصومات من DB فقط وليس من client.
- إضافة tests لحالات: invalid course, invalid coupon, payment provider failure.

### 9. API authorization gaps على exam/video/assignment

الدليل:

- `routes/api.php`
- `app/Http/Controllers/API/ExamController.php`
- `app/Http/Controllers/API/ViewVideoController.php`
- `app/Http/Controllers/API/UploadAssignmentController.php`

المشكلة:

المسارات محمية بـ `auth:api` فقط، لكن لا يظهر تحقق واضح أن المستخدم اشترى course أو له حق الوصول إلى video/section/exam المطلوب.

الأثر:

- Authenticated user قد يسجل مشاهدة فيديو في course غير مشتراه.
- قد يرفع assignment على section لا يخصه.
- قد يرسل إجابات exam غير مصرح له.
- هذا يؤثر على progress، certificates، وdata integrity.

أفضل حل:

- تطبيق policies أو middleware يربط video/section/exam بالـ course ويؤكد purchase/approval.
- إضافة checks داخل FormRequest أو controller.
- إضافة tests لـ unauthorized course access.

### 10. Exam answer validation لا يثبت أن answer يتبع question

الدليل:

- `app/Http/Controllers/API/ExamController.php`

المشكلة:

الكود يعمل `Question::find($key)` و `Answer::find($value)` ثم يحسب الدرجة من `answer->is_correct` بدون تحقق ظاهر أن answer فعلا تابع لنفس question.

الأثر:

- احتمال cheating أو scoring غلط لو user أرسل answer ID صحيح من سؤال آخر.
- integrity issue في نتائج exams وcertificates.

أفضل حل:

- أثناء التقييم، تحقق أن `answer.question_id === question.id`.
- تحميل كل questions/answers مرة واحدة بـ `whereIn` لتقليل queries.
- جعل validation يضمن أن questions تتبع exam وأن answers تتبع questions.

### 11. Error handling قد يسرّب تفاصيل داخلية

الدليل:

- `app/Exceptions/Handler.php`

المشكلة:

لكثير من exceptions، response يعيد `$exception->getMessage()` بعد translation. هذا قد يحتوي على SQL details، class names، paths، relation names، أو رسائل provider داخلية.

الأثر:

- Information disclosure يساعد المهاجم يفهم البنية الداخلية.
- رسائل أخطاء غير مستقرة تظهر للمستخدم.
- `AuthorizationException` ترجع 401 بدل 403، ما يخلط بين authentication وauthorization.

أفضل حل:

- في production، أرجع رسائل عامة حسب نوع الخطأ.
- سجل التفاصيل كاملة في logs فقط.
- استخدم 401 للـ unauthenticated و403 للـ unauthorized.
- انقل renderable handlers إلى `register()` أو تعامل معها مباشرة بدون تسجيل داخل `render()`.

### 12. Swagger documentation غالبا public

الدليل:

- `config/l5-swagger.php`
- `.env.example`

المشكلة:

Swagger middleware arrays فارغة، و `.env.example` يشجع `L5_SWAGGER_GENERATE_ALWAYS=true`.

الأثر:

- كشف API surface بالكامل يسهل reconnaissance.
- generation في production قد يستهلك موارد ويكشف endpoints حديثة.

أفضل حل:

- حماية docs بـ auth + admin permission أو IP allowlist.
- تعطيل Swagger generation في production.
- مراجعة محتوى annotations حتى لا تحتوي على أسرار أو أمثلة حساسة.

## Medium Findings

### 13. Mass assignment واسع على User model

الدليل:

- `app/Models/User/User.php`

المشكلة:

`$guarded = ['id']` يترك كل الحقول الأخرى mass assignable.

الأثر:

- أي code path يستخدم `$request->all()` أو input واسع قد يعدل fields حساسة مثل type/status/email/role-related fields لو موجودة.

أفضل حل:

- استخدام `$fillable` whitelist للحقول المسموحة فقط.
- فصل user update DTO/FormRequest حسب نوع المستخدم.
- مراجعة كل create/update على User وOrder والنماذج الحساسة.

### 14. Course details API ينتج payload كبير وqueries كثيرة

الدليل:

- `app/Http/Controllers/API/CourseController.php`
- `app/Http/Resources/Api/CourseDetailsResource.php`

المشكلة:

`show()` يحمل sections و exams with questions، والresource يحسب ratings/counts/videos/zoom/orders بqueries إضافية أو relations غير محملة بوضوح.

الأثر:

- استجابة كبيرة جدا للكورسات الضخمة.
- memory عالي وlatency عند فتح صفحة course details.
- احتمال N+1 queries مع sections/resources/accessors.

أفضل حل:

- فصل course overview عن exam content endpoint.
- استخدام `withCount`, `withAvg`, eager loading محسوب.
- عدم إرسال questions/answers إلا عند بدء الامتحان وبعد authorization.
- قياس query count وresponse size على أكبر course.

### 15. Home API فيه N+1 queries وaggregates غير cached

الدليل:

- `app/Http/Controllers/API/HomeController.php`

المشكلة:

الكود يعمل `Category::get()` ثم query لكل category لجلب آخر courses، ويحسب statistics في كل request.

الأثر:

- home endpoint يصبح bottleneck مع زيادة categories/courses/users.
- ضغط متكرر على DB لبيانات غالبا لا تتغير كل ثانية.

أفضل حل:

- استخدام eager relationships أو query واحدة منظمة لجلب latest courses per category.
- cache للـ homepage blocks وstatistics مع TTL أو invalidation عند تغيير data.
- مراقبة query count في Telescope/Debugbar.

### 16. CategoryResource يعمل query count وget لكل category

الدليل:

- `app/Http/Resources/Api/CategoryResource.php`

المشكلة:

داخل resource يتم استدعاء `courses()->count()` ثم `courses()->get()` لكل category.

الأثر:

- 2 queries على الأقل لكل category.
- response يكبر لأنه يضع courses داخل كل category.

أفضل حل:

- استخدم `withCount` في controller/query.
- اجعل courses endpoint منفصل أو paginated.
- لا تنفذ database queries داخل resource إن أمكن.

### 17. Queue default هو sync

الدليل:

- `config/queue.php`
- `.env.example`

المشكلة:

default queue connection هو `sync`، و `.env.example` أيضا يستخدم `QUEUE_CONNECTION=sync`.

الأثر:

- jobs مثل `StudentCourseCertificateChecker` ستعمل داخل request إذا production لم يغير الإعداد.
- exam submit/video view قد يصبح بطيئا أو يفشل timeout.

أفضل حل:

- في production استخدم `redis` أو `database` queue.
- تشغيل worker تحت Supervisor أو Horizon.
- تفعيل failed jobs monitoring.
- اجعل `.env.example` يوضح أن `sync` للـ local فقط.

### 18. Certificate checker job فيه queries داخل loops

الدليل:

- `app/Jobs/StudentCourseCertificateChecker.php`

المشكلة:

الjob يجلب assignments/exams ثم داخل loops يعمل queries إضافية للأسئلة والإجابات والدرجات.

الأثر:

- مع كثرة الطلاب والكورسات، queue workers قد تتأخر.
- عند sync queue، نفس المشكلة تضرب request مباشرة.

أفضل حل:

- eager load sections/exams/questions مرة واحدة.
- جلب AnswerUser aggregates دفعة واحدة.
- جعل job idempotent وربما `ShouldBeUnique` حسب business rule.
- إضافة indexes مناسبة على `views`, `answer_users`, وforeign keys المستخدمة.

### 19. حساب completion percentage مكلف

الدليل:

- `app/Helpers/Helpers.php`

المشكلة:

`getCourseAverageCompletionPercentage()` يجلب كل viewers ثم يعمل count لكل visitor داخل loop.

الأثر:

- admin pages أو reports قد تصبح بطيئة جدا مع كثرة views.
- زيادة ضغط DB بسبب O(number of viewers) queries لكل course.

أفضل حل:

- حسابها بـ aggregate SQL واحد أو materialized summary table.
- تحديث summary عبر job مجدول.
- عدم حسابها داخل views أو loops لكل course.

### 20. File ZIP download synchronous داخل public path

الدليل:

- `app/Support/HasFiles.php`

المشكلة:

`download()` ينشئ zip في `public_path` بشكل synchronous وباسم عام مثل table name.

الأثر:

- request blocking مع الملفات الكبيرة.
- race condition لو أكثر من مستخدم طلب نفس zip في نفس الوقت.
- تخزين مؤقت في public قد يترك files قابلة للوصول.

أفضل حل:

- stream zip مباشرة أو queue generation مع signed download.
- استخدم اسم ملف unique لكل request/user.
- خزّن الملفات المؤقتة خارج public وامسحها بعد التحميل.

### 21. TrustProxies غير مضبوط

الدليل:

- `app/Http/Middleware/TrustProxies.php`

المشكلة:

`$proxies` غير محدد.

الأثر:

- خلف load balancer/CDN، Laravel قد يقرأ IP أو scheme غلط.
- rate limiting وsecure URL generation وHTTPS detection قد تتأثر.

أفضل حل:

- ضبط trusted proxies حسب البنية الحقيقية أو استخدام `*` فقط إذا السيرفر خلف proxy موثوق.
- مراجعة forwarded headers مع Nginx/Cloudflare/load balancer.

### 22. Policies قليلة مقارنة بحجم الدومين

الدليل:

- `app/Providers/AuthServiceProvider.php`

المشكلة:

المسجل فقط `CoursePolicy` و `VideoPolicy`، بينما النظام يحتوي exams, assignments, orders, certificates, answers وغيرها.

الأثر:

- authorization قد يكون ad hoc داخل controllers أو middleware فقط.
- صعوبة إثبات أن كل resource access محمي.

أفضل حل:

- إضافة policies للـ Exam, Section, Assignment, Order, Certificate.
- استخدام `$this->authorize()` في controllers أو FormRequests.
- إضافة tests لكل authorization rule مهم.

### 23. `env()` مستخدم داخل runtime code

الدليل:

- `app/Transformers/V1/ZoomTransformer.php`
- ملفات داعمة أخرى تحتاج مراجعة أوسع مثل Zoom/password reset integrations.

المشكلة:

استخدام `env()` خارج config files يتعارض مع `php artisan config:cache` وقد يرجع null في production.

الأثر:

- integrations تفشل بعد config caching.
- صعوبة تتبع config المطلوب لكل service.

أفضل حل:

- نقل كل env reads إلى config files.
- استخدام `config('services.zoom.key')` وما شابه داخل التطبيق.
- إضافة static check أو review rule تمنع `env()` خارج `config/`.

## Low / Hygiene Findings

### 24. `.env.example` يحتوي production-unsafe defaults

الدليل:

- `.env.example`

المشكلة:

`APP_DEBUG=true`, `LOG_LEVEL=debug`, `L5_SWAGGER_GENERATE_ALWAYS=true`, و `QUEUE_CONNECTION=sync`.

الأثر:

- أي deploy يعتمد على copy/paste قد يبدأ بإعدادات غير آمنة أو بطيئة.

أفضل حل:

- توضيح أن هذه القيم local فقط.
- إضافة `.env.production.example` آمن:
  - `APP_DEBUG=false`
  - `LOG_LEVEL=warning`
  - `QUEUE_CONNECTION=redis`
  - `L5_SWAGGER_GENERATE_ALWAYS=false`

### 25. QueryFilterMiddleware يسمح بـ `with` كسلسلة حرة

الدليل:

- `app/Http/Middleware/QueryFilterMiddleware.php`

المشكلة:

`with` يتم التحقق أنه string فقط، بدون whitelist للعلاقات المسموح تحميلها.

الأثر:

- endpoints التي تستخدم هذا input قد تحمل relations مكلفة أو غير مقصودة.
- تضخم response أو query load.

أفضل حل:

- whitelist للعلاقات المسموحة لكل endpoint.
- رفض أي relation غير مصرح به.

### 26. Tests غير كافية للمخاطر العالية

الدليل:

- من نمط المشروع والملفات التي تمت مراجعتها، لا يظهر coverage كاف لمسارات checkout/payment/authorization/exam scoring.

الأثر:

- إصلاحات security/payment قد تسبب regressions بدون اكتشاف.
- صعب إعادة هيكلة checkout أو authorization بأمان.

أفضل حل:

- Feature tests لـ checkout success/failure/invalid IDs/coupon/payment provider failure.
- Authorization tests للـ video/assignment/exam access.
- Tests للتأكد أن Zoom transformer لا يرجع secrets.

## خطة علاج مقترحة حسب الأولوية

### خلال 24 ساعة

1. تدوير EasyKash, Zoom, Bunny/CDN secrets.
2. إزالة أي secrets من API responses والكود.
3. تعديل `composer.json` لإجبار TLS/secure HTTP.
4. حماية logger routes وadmin route group بـ auth/admin middleware.
5. تعطيل أو حماية Swagger في production.

### خلال أسبوع

1. إعادة بناء checkout/payment داخل transactions.
2. إضافة webhook/signature verification وidempotency للمدفوعات.
3. إضافة authorization policies للـ exam/video/assignment/order/certificate.
4. إصلاح exam answer validation حتى answer تتبع question.
5. نقل كل `env()` runtime إلى config.

### خلال أسبوعين إلى شهر

1. تحسين CourseDetailsResource وتقسيم payloads الكبيرة.
2. إضافة caching للـ Home API وstatistics.
3. إصلاح CategoryResource لتستخدم `withCount` وpagination.
4. تشغيل production queue workers بدل `sync`.
5. تحسين CertificateChecker job وcompletion percentage aggregation.

### تحسينات مستمرة

1. بناء regression test suite للمسارات الحساسة.
2. إضافة route/security audit كجزء من CI.
3. مراقبة slow queries وqueue failures.
4. مراجعة indexing على الجداول ذات الاستخدام العالي مثل `views`, `answer_users`, `orders`, `order_items`.

## Checklist للتحقق بعد الإصلاح

- تشغيل `php artisan route:list` والتأكد أن كل `/admin`, `/activity`, payment callbacks, وAPI الحساسة محمية كما يجب.
- تشغيل بحث في الكود عن `env(` خارج `config/`.
- تشغيل بحث عن tokens/secrets hardcoded.
- اختبار `APP_DEBUG=false` و`config:cache` على staging.
- قياس query count لـ home, categories, course details, exam submit.
- اختبار checkout failure paths والتأكد من عدم وجود partial orders.
- اختبار أن مستخدم غير مشترك لا يستطيع view video أو upload assignment أو answer exam.
- اختبار أن Zoom responses لا تحتوي على keys/secrets/passwords.
- مراقبة queue workers وfailed jobs بعد نقل jobs من sync إلى redis/database.

## ملاحظات إيجابية

- يوجد استخدام لـ FormRequests في بعض المسارات مثل checkout/exam/upload assignment، وهذا أساس جيد للتوسع في validation.
- CSRF يبدو غير مستثنى بشكل واسع في web middleware.
- استخدام Laravel Passport للـ API auth موجود، لكن يحتاج authorization checks فوق authentication.
- استخدام cache في `Helpers::settings()` جيد ويمكن تطبيق نفس الفكرة على home/statistics.

## الخلاصة

المشروع قابل للتحسين بدون إعادة كتابة كاملة، لكن توجد عدة نقاط يجب التعامل معها كـ urgent security work. أهم مبدأ في الإصلاح هو فصل الأسرار عن الكود، توحيد authorization على مستوى routes/policies، وجعل العمليات المالية atomic ومثبتة بـ provider verification. بعد ذلك تأتي تحسينات الأداء: تقليل payloads الكبيرة، منع queries داخل resources/loops، وتشغيل queue حقيقي في production.
