Security Overview
MailTrixy implements multiple layers of security to protect your data, prevent unauthorized access, and ensure compliance with industry standards.
Multi-Tenant Workspace Isolation
Every piece of data in MailTrixy belongs to a workspace. Workspace isolation is enforced at the database query level through global scopes (automatic filters that ensure each workspace can only see its own data), ensuring that users can never access data from a workspace they do not belong to.
- Global query scopes automatically filter all database queries by
workspace_id. - Middleware validation confirms the authenticated user belongs to the requested workspace on every request.
- Foreign key constraints at the database level prevent orphaned or cross-workspace references.
- API tokens are scoped to a single workspace and cannot access data across workspaces.
Data Encryption
Sensitive data is encrypted at rest using Laravel's built-in encryption with AES-256-CBC. The following data types are encrypted:
| Data Type | Model | Method |
|---|---|---|
| OAuth access tokens | EmailAccount | AES-256-CBC (EncryptsAttributes trait) |
| OAuth refresh tokens | EmailAccount | AES-256-CBC (EncryptsAttributes trait) |
| AI provider API keys | AiProvider | AES-256-CBC (EncryptsAttributes trait) |
| SMTP/IMAP passwords | EmailAccount | AES-256-CBC (EncryptsAttributes trait) |
| User passwords | User | Bcrypt hashing (one-way) |
| Webhook signing secrets | Webhook | AES-256-CBC (EncryptsAttributes trait) |
| 2FA recovery codes | User | AES-256-CBC encryption |
CSRF Protection
All state-changing web requests are protected against Cross-Site Request Forgery (CSRF) attacks using Laravel's built-in CSRF token verification. Every form includes a unique token that is validated on submission. Livewire components automatically handle CSRF tokens for all wire:click and wire:submit actions.
<!-- CSRF token is automatically included in Blade forms -->
<form method="POST" action="/contacts">
@csrf
<!-- form fields -->
</form>
XSS Prevention
MailTrixy protects against Cross-Site Scripting (XSS) through multiple mechanisms:
- Blade auto-escaping — All output rendered with
{{ }}is automatically HTML-encoded. - Content Security Policy — Restrictive CSP headers limit script execution sources.
- HTML Purifier — Email content and rich text inputs are sanitized with HTMLPurifier before storage.
- Livewire escaping — All Livewire component data bindings are escaped by default.
SQL Injection Prevention
SQL injection attacks are prevented through Laravel's Eloquent ORM and query builder (Laravel's built-in tools for safely communicating with the database), which use parameterized queries — a technique that separates user input from database commands, making injection attacks impossible for all database interactions. Raw queries are avoided in the codebase, and any necessary raw expressions use parameter binding.
File Upload Security
File uploads are protected with multiple validation layers:
- MIME type validation — Files are validated by their actual content type, not just the extension.
- File size limits — Configurable maximum file size (default 10MB for attachments, 50MB for KB documents).
- Extension whitelist — Only allowed file types can be uploaded (pdf, doc, docx, txt, csv, jpg, png, gif).
- Randomized filenames — Uploaded files are stored with UUID-based names to prevent path traversal attacks.
- Non-public storage — Files are stored outside the web root and served through authenticated controller routes.
Rate Limiting
MailTrixy implements rate limiting at multiple levels to prevent abuse:
- Login attempts — 5 attempts per minute per IP address, with lockout after exceeding the limit.
- API requests — Per-scope rate limits (see API Overview).
- Password reset — 3 requests per hour per email address.
- Email sending — Per-account sending limits enforced to prevent spam abuse.
- 2FA verification — 5 attempts per 10 minutes to prevent brute-force attacks on TOTP codes.
CORS Configuration
Cross-Origin Resource Sharing (CORS) is configured to restrict API access to authorized origins only. The default configuration allows requests from the application's own domain.
// config/cors.php
'paths' => ['api/*'],
'allowed_methods' => ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
'allowed_origins' => [env('APP_URL')],
'allowed_headers' => ['Content-Type', 'Authorization', 'Accept', 'X-Requested-With'],
'exposed_headers' => ['X-RateLimit-Limit', 'X-RateLimit-Remaining'],
'max_age' => 86400,
'supports_credentials' => true,
Session Security
Session cookies are configured with security-hardened settings:
| Setting | Value | Purpose |
|---|---|---|
| HttpOnly | true | Prevents JavaScript from accessing session cookies |
| Secure | true (production) | Cookies are only sent over HTTPS connections |
| SameSite | lax | Prevents cookies from being sent in cross-site requests |
| Session Lifetime | 120 minutes | Automatically logs out inactive users |
| Session Driver | database | Sessions stored server-side in encrypted database records |