Files
xamxam/apps/admin/SECURITY.md
Théophile Gervreau-Mercier 467aced734 Restructure repository and implement secure search feature
Phase 1: Consolidate shared infrastructure
- Create shared/ directory for common code
- Consolidate Database.php from front-backend and formulaire into unified shared/Database.php
  - Smart path detection for test.db vs posterg.db
  - Secure search with wildcard escaping and input validation
  - Support both singleton and direct instantiation patterns
  - Full CRUD methods for admin functionality
- Move RateLimit.php to shared/ (30 requests/min)
- Update all require paths across apps to use shared/

Phase 2: Reorganize directory structure
- Rename front-backend/ → apps/public/
- Rename formulaire/ → apps/admin/
- Rename db/ → database/
- Update all file paths for new structure
- Create root .gitignore excluding databases, cache, logs

Implement secure search feature
- Add apps/public/search.php with full-text search across theses
- Search filters: query, year, orientation, AP program, keywords
- Security features:
  - SQL injection prevention (prepared statements)
  - Wildcard injection prevention (escape % and _)
  - Input validation (max 200 chars, year range 1900-2100)
  - Rate limiting (30 req/min per IP)
  - Pagination limited to 100 results/page
  - XSS protection (htmlspecialchars on output)

Add comprehensive test suite
- Create apps/public/tests/ with proper structure
  - tests/Integration/SearchTest.php - 12 search scenarios
  - tests/Security/SecurityTest.php - vulnerability testing
  - tests/Unit/RateLimitTest.php - rate limit behavior
- Create database/fixtures/CreateTestDatabase.php
- Add apps/public/run-tests.php test runner
- All tests passing (4/4 suites)

Update deployment configuration
- Rename justfile 'sync' recipe to 'deploy'
- Create deploy group with separate deploy-public and deploy-admin
- Add test-deploy recipe for test database
- Exclude *.db, tests/, cache/, *.md from production deploy
- Deploy shared/ to both public and admin locations

Stats: +4482 insertions, -654 deletions across 72 files
2026-02-02 18:53:58 +01:00

164 lines
4.8 KiB
Markdown

# Security Improvements
## Changes Made
### 1. Critical Vulnerability Fixes
#### Path Traversal in thanks.php (CRITICAL)
- **Before**: User could access ANY file on the system via `?file=../../../../etc/passwd`
- **After**:
- Validates file path using `realpath()` to resolve symlinks
- Ensures file is within allowed `data/yaml/` directory
- Verifies file extension is `.yaml`
- Proper error handling without exposing system paths
#### CSRF Protection
- **Before**: Form could be submitted from any website
- **After**:
- Session-based CSRF tokens generated for each form load
- Token validated on submission using timing-safe comparison (`hash_equals()`)
- Token cleared after successful submission
### 2. Input Validation & Sanitization
#### Deprecated Functions Replaced
- **Before**: Used `FILTER_SANITIZE_STRING` (deprecated in PHP 8.1+)
- **After**: Custom `sanitize_string()` function using `htmlspecialchars()` and `strip_tags()`
#### Enhanced Validation
- Required fields properly validated with custom `validate_required()` function
- Email validation using `FILTER_VALIDATE_EMAIL`
- URL validation using `FILTER_VALIDATE_URL`
- Year validation with reasonable range checking (2000 to current year + 1)
- Comprehensive error messages for validation failures
### 3. File Upload Security
#### Random Filenames
- **Before**: Used original or predictable filenames (author + timestamp)
- **After**:
- Generates cryptographically secure random filenames using `random_bytes()`
- Prevents file overwrites
- Prevents path traversal attacks via malicious filenames
- Stores mapping to original filename for reference
#### Enhanced File Validation
- MIME type checking using `finfo`
- File extension whitelist
- File size limits (50MB max)
- Proper error handling for upload errors
- Cover image restricted to JPEG/PNG only
### 4. Bug Fixes
- Fixed undefined variable `$memoireFolder` (used before definition)
- Fixed undefined variable `$resume` (should be `$description`)
- Fixed variable ordering (generate `$uniqueId` before using it)
- Added proper `__DIR__` prefix for absolute paths
### 5. Error Handling
- Try-catch block wraps entire form processing
- Detailed error logging (not exposed to users)
- User-friendly error messages
- Proper exit after redirect
- No system path exposure in error messages
## Nginx Configuration Notes
Since this form is behind nginx password authentication, additional security layers:
### Recommended nginx config:
```nginx
location /formulaire {
auth_basic "Restricted Access";
auth_basic_user_file /etc/nginx/.htpasswd;
# Rate limiting
limit_req zone=form_limit burst=5 nodelay;
# File upload size
client_max_body_size 100M;
# Timeout settings
client_body_timeout 60s;
# Prevent access to sensitive files
location ~ /\. {
deny all;
}
location ~ /(vendor|composer\.(json|lock)|error\.log)$ {
deny all;
}
}
```
## Additional Recommendations
### 1. Database Migration (In Progress)
Moving to SQLite will provide:
- Structured data storage
- Better query capabilities
- Easier data management
- Prepared statements for SQL injection prevention
### 2. File Storage
- Consider moving uploaded files outside web root
- Serve files through PHP script with access control
- Implement file scanning for malware if possible
### 3. Monitoring
- Regularly review `error.log` for suspicious activity
- Monitor file upload patterns
- Set up alerts for failed CSRF validations
### 4. Backup Strategy
- Regular backups of `data/` directory
- Version control for code changes
- Test restore procedures
### 5. PHP Configuration
Ensure these settings in php.ini:
```ini
file_uploads = On
upload_max_filesize = 100M
post_max_size = 100M
max_execution_time = 60
max_input_time = 60
memory_limit = 256M
# Security
expose_php = Off
allow_url_fopen = Off
allow_url_include = Off
display_errors = Off
log_errors = On
```
## Testing Checklist
- [ ] Form submission with all fields
- [ ] Form submission with minimal required fields
- [ ] Invalid email format
- [ ] Invalid URL format
- [ ] Invalid year
- [ ] File upload (various formats)
- [ ] Large file upload (>50MB, should fail)
- [ ] Invalid file types
- [ ] Multiple file uploads
- [ ] Cover image upload
- [ ] CSRF token validation (try submitting with wrong token)
- [ ] Path traversal attempt in thanks.php
- [ ] Error handling for missing directories
## Known Limitations
1. **No atomic transactions**: File operations and YAML save not atomic
2. **No rollback**: Failed submissions may leave partial files
3. **Session storage**: CSRF tokens in default PHP session (consider database sessions)
4. **No upload progress**: Large files have no progress indicator
5. **No duplicate detection**: Same submission can be made multiple times
These limitations will be addressed in the SQLite migration.