# مدل امنیتی و سخت‌سازی

این سند توضیح می‌دهد هر تهدید چگونه پوشش داده شده و کدام بخش‌ها نیازمند بررسی روی سرور واقعی است.

---

## ۱. محرمانگی داده‌ها و چیدمان

| پوشه/فایل | در دسترس وب؟ | محتوا |
|---|---|---|
| `public/` | ✅ فقط این | `index.php`، `install.php`، `assets/` |
| `config/config.php` | ❌ (`.htaccess` + `index.html` + DocumentRoot) | کلید برنامه، مشخصات دیتابیس، `merchant_id`، رمز RCON، کلید Bridge |
| `storage/` | ❌ | پایگاه داده، نشست‌ها، لاگ‌ها، فایل قفل نصب |
| `app/`, `views/`, `schema/`, `bin/`, `tests/` | ❌ | کد برنامه |

* هیچ رازی در `config/app.php` (که همراه کد است) وجود ندارد.
* `.env.example` فقط نام متغیرها را دارد و مقدار واقعی ندارد.
* `storage/installed.lock` نشانهٔ پایان نصب است؛ نصاب با دیدن آن ۴۰۳ برمی‌گرداند
  و اجازهٔ ساخت مدیر اولیهٔ جدید نمی‌دهد.

---

## ۲. احراز هویت و نشست

* رمز عبور با `password_hash(..., PASSWORD_ARGON2ID)` (در صورت نبود، `PASSWORD_BCRYPT`) ذخیره می‌شود؛
  **هیچ رمزی به‌صورت متن ساده ذخیره یا لاگ نمی‌شود.**
* ورود با `password_verify` و مقایسهٔ زمان‌ثابت.
* پس از ورود موفق، شناسهٔ نشست بازسازی می‌شود (`session_regenerate_id`).
* کوکی نشست: `HttpOnly`، `SameSite=Lax`، مسیر محدود، و `Secure` در صورت HTTPS.
* خروج، نشست را کاملاً پاک می‌کند.
* **محدودسازی ورود:** ۵ تلاش ناموفق در ۱۵ دقیقه → قفل موقت؛ پیام خطا عمومی است
  («نام کاربری یا رمز عبور اشتباه است») و تفاوت بین نبود کاربر و رمز اشتباه را فاش نمی‌کند.
* بیکاری بیش از ۱۲۰ دقیقه → خروج خودکار.
* مدیران AquaMC توسط ادمین قفل/فعال می‌شوند و می‌توان رمزشان را بازنشانی کرد.

---

## ۳. کنترل دسترسی (Authorization)

* هر روتِ حساس با میان‌افزار `auth` و `role:winix_admin` یا `role:aquamc_manager` محافظت می‌شود.
* بررسی نقش در **سرور** انجام می‌شود؛ مخفی کردن منو در رابط کاربری فقط یک لایهٔ راحتی است.
* پرس‌وجوهای مدیر AquaMC با شرط مالکیت (`user_id = :id`) محدود شده‌اند:
  تسویه‌ها، آمار و پروفایل فقط داده‌های خود او را برمی‌گردانند.
* مدیر AquaMC **نمی‌تواند**:
  * به مسیرهای `/admin/*` دسترسی پیدا کند (۴۰۳)
  * کاربران/نقش‌ها را ببیند یا تغییر دهد
  * تنظیمات قیمت یا درگاه را تغییر دهد
  * تسویهٔ خود یا دیگری را تأیید/پرداخت کند
  * به سفارش‌ها، پرداخت‌ها یا مشتریان دسترسی داشته باشد
* تلاش برای دسترسی غیرمجاز در **لاگ حسابرسی** ثبت می‌شود.

---

## ۴. یکپارچگی مالی و جلوگیری از دوباره‌خوری

* همهٔ مبالغ با **عدد صحیح** و به **تومان** محاسبه می‌شوند.
* قیمت/تعداد/تخفیفِ ارسالی مرورگر **بی‌اعتبار** است؛ سرور دوباره محاسبه می‌کند.
* هر سفارش `authority` یکتا دارد؛ ستون `orders.authority` یونیک است.
* پرداخت فقط پس از **بررسی (Verify) سمت سرور** معتبر می‌شود، هرگز با بازگشت کاربر از درگاه.
* مبلغ تأییدشده باید با مبلغ ذخیره‌شده برابر باشد؛ در غیر این صورت سفارش به
  «پرداخت ناموفق» یا «بررسی دستی» می‌رود و تحویلی انجام نمی‌شود.
* callbackها به‌عنوان **ورودی نامعتبر** تلقی می‌شوند: تطبیق `authority` با سفارش،
  پذیرش فقط از وضعیت‌های مجاز، و قفل تراکنش برای جلوگیری از همزمانی.
* کلید یکتای تحویل (`order-{id}-v{n}`) در ستون یونیک `delivery_jobs.idempotency_key` ذخیره می‌شود؛
  تلاش مجدد همان کلید را می‌فرستد و افزونه/دریافت‌کننده تکرار را رد می‌کند.
* کارهای تحویلِ گیرکرده (بیش از ۱۵ دقیقه در وضعیت «در حال اجرا») به **نامشخص** تبدیل می‌شوند؛
  بازارسال خودکار انجام **نمی‌شود** و تصمیم با ادمین است.

---

## ۵. ورودی، خروجی و پایگاه داده

* تمام دسترسی‌ها به دیتابیس با **PDO + Prepared Statements** است؛ هیچ رشته‌ای مستقیماً در SQL درج نمی‌شود.
* خروجی با `htmlspecialchars(..., ENT_QUOTES, 'UTF-8')` (تابع `e()`) چاپ می‌شود.
* اعتبارسنجی در `Validator` برای همهٔ فرم‌ها: نوع، طول، الگو، بازه، یکتایی.
* نام کاربری ماینکرفت فقط `[A-Za-z0-9_]{3,16}`؛ فرمان از **قالب ثابت** ساخته می‌شود و
  هیچ متنی از کاربر وارد فرمان نمی‌شود.
* آپلود فایل وجود ندارد (نیازی به بررسی نوع/حجم فایل نیست).

---

## ۶. CSRF، هدرها و CSP

* هر فرم تغییردهنده توکن CSRF دارد که با `hash_equals` و در زمان ثابت مقایسه می‌شود؛
  نبود/انقضا → صفحهٔ ۴۱۹ فارسی.
* هدرها: `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`,
  `Permissions-Policy`, `Cross-Origin-Opener-Policy` و در صورت HTTPS، `Strict-Transport-Security`.
* `Content-Security-Policy` با **nonce برای هر درخواست**؛ اسکریپت‌های داخلیِ بدون nonce اجرا نمی‌شوند
  (به همین دلیل همهٔ رفتارهای تعاملی در `public/assets/js/app.js` و از طریق `data-*` متصل شده‌اند).
* منابع خارجی فقط فونت Vazirmatn از `fonts.googleapis.com` و پیش‌فرض سیستم در صورت عدم دسترسی.

---

## ۷. مدیریت خطا و لاگ

* در تولید، کاربر فقط صفحات خطای فارسی (۴۰۳/۴۰۴/۴۱۹/۴۲۹/۵۰۰) را می‌بیند؛
  هیچ ردیابی پشته (stack trace) یا مسیر فایل نمایش داده نمی‌شود.
* خطاها در `storage/logs/app-YYYY-MM-DD.log` ثبت می‌شوند.
* لاگ‌ها هرگز شامل: رمز عبور، `merchant_id` کامل، رمز RCON، کلید Bridge یا شمارت کارت کامل نیستند.
* لاگ حسابرسی (`audit_events`) شامل: چه کسی، چه کاری، روی چه چیزی، چه زمانی، از چه IPای.

---

## ۸. تسویه‌حساب

* شمارت کارت با AES-256-CBC (کلید مشتق از `app.key`) ذخیره می‌شود.
* مدیر AquaMC فقط ۴ رقم آخر (`****-۱۲۳۴`) را می‌بیند؛ ادمینِ مجاز شمارت کامل را.
* فقط ادمین می‌تواند وضعیت را تغییر دهد (Pending → Approved/Rejected → Paid).
* ثبت «پرداخت شد» فقط توسط ادمینِ دارای نقش و با ثبت یادداشت و زمان انجام می‌شود.
* جلوگیری از درخواست تکراریِ همزمان و ویرایش درخواستِ مدیر دیگر (بررسی مالکیت).

---

## ۹. محدودیت‌های شناخته‌شده (شفاف‌سازی)

1. **RCON بدون کلید یکتا** — تلاش مجددِ خودکار برای وضعیت‌های نامشخص انجام نمی‌شود (عمداً).
   برای تحویل کاملاً ایمن از درایور Bridge استفاده کنید.
2. **سندباکس زرین‌پال** گاهی در دسترس نیست و رفتار آن با محیط تولید تفاوت دارد.
3. **همزمانی روی MySQL** — آزمون‌های خودکار با SQLite اجرا شده‌اند؛ روی MySQL یک بار سناریوی
   callback همزمان را به‌صورت دستی بررسی کنید (قفل تراکنش پیاده شده است).
4. **ارسال ایمیل/پیامک** پیاده‌سازی نشده (نیازمند سرویس خارجی).
5. **احراز هویت دو مرحله‌ای (2FA)** برای ادمین پیاده‌سازی نشده؛ در صورت نیاز قابل افزودن است.

---

## ۱۰. چک‌لیست سخت‌سازی سرور

- [ ] DocumentRoot روی `public/` است
- [ ] `public/install.php` حذف شده است
- [ ] `config/config.php` مجوز ۰۶۴۰ دارد و مالک آن کاربر وب نیست
- [ ] HTTPS فعال و `app.url` با `https://` تنظیم شده است
- [ ] دسترسی وب به `https://example.com/config/config.php` و `/storage/...` → ۴۰۳/۴۰۴
- [ ] پورت RCON/Bridge فقط از IP وب‌سرور یا شبکه خصوصی قابل دسترسی است (فایروال)
- [ ] `display_errors` در php.ini خاموش است (`Off`)
- [ ] بکاپ روزانه از دیتابیس و `config/config.php`
- [ ] cron برای `worker.php` فعال است
- [ ] رمز مدیران قوی و یکتا است
