Security Features
This document describes all security features implemented in the Identity Service.
Authentication Security
Token-Based Authentication
The Identity Service uses Laravel Sanctum for token-based authentication:
- Bearer tokens sent via Authorization header
- Token hashing - Tokens are stored as hashes in the database
- Token expiration - Default 24 hours, extended to 30 days with "remember me"
- Single token invalidation on logout
Password Security
Password Requirements
All passwords must meet these requirements:
| Requirement | Description |
|---|---|
| Minimum length | 12 characters |
| Uppercase | At least one uppercase letter (A-Z) |
| Lowercase | At least one lowercase letter (a-z) |
| Numbers | At least one digit (0-9) |
| Special characters | At least one special character (!@#$%^&*) |
Password History
- Last 5 passwords are stored (hashed)
- Users cannot reuse any of their last 5 passwords
- History is updated on every password change
Password Hashing
- Algorithm: bcrypt
- Cost factor: 12 (configurable)
- Passwords are never stored in plain text
Force Password Change
Administrators can force users to change their password:
- On first login
- After a security incident
- As part of regular rotation policy
Brute Force Protection
Rate Limiting
| Endpoint | Limit | Window |
|---|---|---|
| Login | 5 attempts | 1 minute |
| Forgot Password | 5 attempts | 1 hour |
| 2FA Verify | 5 attempts | 1 minute |
| PIN Verify | 5 attempts | 1 minute |
After exceeding the limit, requests receive HTTP 429 (Too Many Requests).
Account Lockout
After 5 failed login attempts:
- Account is temporarily locked for 15 minutes
- User receives email notification
- Failed attempts are logged
IP-Based Throttling
- Rate limits are applied per IP address
- Prevents distributed attacks from single source
Two-Factor Authentication (2FA)
TOTP Implementation
- Algorithm: TOTP (Time-based One-Time Password)
- Standard: RFC 6238
- Code length: 6 digits
- Time step: 30 seconds
- Hash algorithm: SHA-1 (compatible with Google Authenticator)
2FA Secret Storage
Secret → encrypt() → Database
- Secrets are encrypted using Laravel's encryption (AES-256-CBC)
- Decrypted only when generating/verifying codes
- Never exposed in API responses (except during setup)
Recovery Codes
- 8 recovery codes generated on 2FA enable
- Each code is single use
- Codes are stored hashed
- Regeneration invalidates all previous codes
2FA Flow
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Login │────▶│ 2FA Check │────▶│ 2FA Verify │
│ (email/pw) │ │ (has 2FA?) │ │ (TOTP) │
└─────────────┘ └─────────────┘ └─────────────┘
│ │
▼ ▼
No 2FA: Grant Verified: Grant
Full Access Full Access
Security PIN
PIN Requirements
| Requirement | Description |
|---|---|
| Length | Exactly 6 digits |
| No sequences | Cannot be 123456, 654321, etc. |
| No repetition | Cannot be 000000, 111111, etc. |
PIN Storage
PIN → Hash::make() → Database
- PINs are hashed using bcrypt
- Same security as password hashing
- Cannot be recovered, only reset
PIN Lockout
After 5 failed PIN attempts:
- PIN is locked for 15 minutes
- Notification sent to user
- Requires password to unlock early
Session Security
Token Management
- Each login creates a new token
- Tokens are invalidated on logout
- "Logout all devices" invalidates all user tokens
Session Tracking
For each session, we track:
- Device information (browser, OS)
- IP address
- Last activity time
- Created timestamp
Trusted Devices
- Users can trust devices for 30 days
- Trusted devices skip 2FA verification
- Devices can be revoked at any time
Multi-Tenant Security
Tenant Isolation
-- Every user query includes tenant_id
SELECT * FROM users
WHERE tenant_id = ? AND id = ?
- All data is scoped to tenant_id
- Cross-tenant access is impossible at the database level
- UUID-based IDs prevent ID enumeration
Tenant Validation
- Tenant ID is extracted from authenticated user
- Cannot be overridden via request parameters
- All operations validate tenant ownership
Input Validation & Sanitization
Email Validation
- RFC-compliant email validation
- Case-insensitive storage (lowercase)
- Duplicate prevention per tenant
XSS Prevention
- All input is validated before processing
- Output is escaped in API responses
- Content-Type headers enforce JSON
SQL Injection Prevention
- Eloquent ORM with prepared statements
- No raw SQL queries with user input
- Parameter binding for all queries
Audit Logging
Logged Events
| Event | Description |
|---|---|
user.login | Successful login |
user.login_failed | Failed login attempt |
user.logout | User logout |
user.password_changed | Password change |
user.profile_updated | Profile update |
2fa.enabled | 2FA enabled |
2fa.disabled | 2FA disabled |
2fa.verified | 2FA code verified |
2fa.failed | 2FA verification failed |
pin.set | PIN created |
pin.changed | PIN changed |
pin.verified | PIN verified |
pin.failed | PIN verification failed |
device.added | New device trusted |
device.revoked | Device revoked |
Log Data
Each log entry includes:
{
"id": "uuid",
"user_id": "uuid",
"tenant_id": "uuid",
"action": "user.login",
"ip_address": "192.168.1.1",
"user_agent": "Mozilla/5.0...",
"metadata": {},
"created_at": "2026-01-29T10:00:00.000Z"
}
Log Retention
- Audit logs are retained for 365 days
- Configurable per tenant
- Exportable for compliance
API Security
CORS Configuration
'allowed_origins' => [
'https://identity.vecton.hu',
'https://*.vecton.hu',
],
'allowed_methods' => ['GET', 'POST', 'PUT', 'DELETE'],
'allowed_headers' => ['Authorization', 'Content-Type'],
'max_age' => 7200,
Security Headers
| Header | Value |
|---|---|
| X-Content-Type-Options | nosniff |
| X-Frame-Options | DENY |
| X-XSS-Protection | 1; mode=block |
| Strict-Transport-Security | max-age=31536000 |
| Content-Security-Policy | default-src 'self' |
Request Validation
- All requests are validated against defined rules
- Invalid requests receive 422 with error details
- Validation errors never expose sensitive data
Cryptographic Security
Encryption
- Algorithm: AES-256-CBC
- Key: APP_KEY (base64 encoded)
- Used for: 2FA secrets, sensitive data
Hashing
- Algorithm: bcrypt
- Cost factor: 12
- Used for: passwords, PINs, tokens
Random Generation
- CSPRNG: random_bytes() / Str::random()
- Used for: tokens, recovery codes, device IDs
Security Best Practices
For Users
- ✅ Use strong, unique passwords
- ✅ Enable 2FA
- ✅ Set up a security PIN
- ✅ Review active devices regularly
- ✅ Check activity logs for suspicious activity
- ✅ Don't share credentials
For Developers
- ✅ Never log sensitive data
- ✅ Use prepared statements
- ✅ Validate all input
- ✅ Use HTTPS only
- ✅ Keep dependencies updated
- ✅ Follow OWASP guidelines
For Administrators
- ✅ Enforce password policies
- ✅ Require 2FA for sensitive roles
- ✅ Review audit logs regularly
- ✅ Rotate API keys periodically
- ✅ Monitor for anomalies
- ✅ Have incident response plan
Compliance
GDPR Considerations
- User data can be exported
- User data can be deleted (right to be forgotten)
- Consent is tracked
- Data minimization principles applied
Security Standards
The Identity Service is designed following:
- OWASP Top 10 - Security best practices
- NIST 800-63B - Digital identity guidelines
- ISO 27001 - Information security management
Incident Response
Security Incident Procedure
- Detect - Anomaly detection, user reports
- Assess - Determine scope and impact
- Contain - Lock affected accounts
- Eradicate - Remove threat
- Recover - Restore normal operation
- Learn - Post-incident review
Emergency Contacts
- Security Team: security@vecton.hu
- On-call: +36-1-XXX-XXXX