Configuration
This section covers all aspects of configuring Laravel Ingest, from basic setup to advanced security considerations.
Security Best Practices
When dealing with file uploads and data imports, security should be a top priority. Here are the recommended security configurations for production environments.
File Upload Security
1. Restrict File Types
// config/ingest.php
'allowed_file_types' => [
'csv',
'xlsx',
'xls',
'txt',
],
2. File Size Limits
// config/ingest.php
'max_file_size' => 10 * 1024 * 1024, // 10MB in production
3. Virus Scanning (Optional but Recommended)
// In your Importer class
use Illuminate\Http\UploadedFile;
public function getConfig(): IngestConfig
{
return IngestConfig::for(Product::class)
->beforeImport(function(UploadedFile $file) {
// Integrate with virus scanning service
if (app()->environment('production')) {
$this->scanForMalware($file);
}
});
}
private function scanForMalware(UploadedFile $file): void
{
// Example: ClamAV integration
$clamav = new \Xenolope\Quahog\Client(
new \Xenolope\Quahog\Socket('127.0.0.1', 3310)
);
$result = $clamav->scanFile($file->getPathname());
if ($result->isInfected()) {
throw new SecurityException('File contains malware');
}
}
Data Validation Security
1. Input Sanitization
->beforeRow(function(array &$row) {
// Remove potentially dangerous content
foreach ($row as $key => $value) {
if (is_string($value)) {
$row[$key] = strip_tags($value);
$row[$key] = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}
}
})
2. SQL Injection Prevention
// Always use parameter binding in custom queries
->afterRow(function($model, array $row) {
// ❌ BAD: Direct string interpolation
// DB::statement("UPDATE products SET notes = '{$row['notes']}' WHERE id = {$model->id}");
// ✅ GOOD: Parameter binding
DB::statement(
"UPDATE products SET notes = ? WHERE id = ?",
[$row['notes'], $model->id]
);
})
3. Data Type Validation
->validate([
'email' => 'required|email|max:255',
'phone' => 'nullable|string|max:20|regex:/^\+?[1-9]\d{1,14}$/',
'price' => 'required|numeric|min:0|max:999999.99',
'quantity' => 'required|integer|min:0|max:999999',
'website' => 'nullable|url|max:2048',
'ip_address' => 'nullable|ip',
])
Access Control
1. Permission-Based Import Access
// In your ImportController
public function store(Request $request)
{
$this->authorize('import', Product::class);
// Additional role-based checks
if (!auth()->user()->hasRole('data_manager')) {
abort(403, 'Insufficient permissions for data import');
}
// Proceed with import
}
2. Rate Limiting
// routes/web.php or routes/api.php
Route::middleware([
'auth',
'throttle:5,1', // 5 imports per minute
])->group(function () {
Route::post('/import/products', [ProductImportController::class, 'store']);
});
3. Audit Logging
->beforeImport(function(UploadedFile $file) {
// Log import attempt
activity()
->causedBy(auth()->user())
->performedOn(new Product())
->withProperties([
'file_name' => $file->getClientOriginalName(),
'file_size' => $file->getSize(),
'mime_type' => $file->getMimeType(),
'ip_address' => request()->ip(),
])
->log('product_import_started');
})
->afterImport(function(ImportResult $result) {
// Log import completion
activity()
->causedBy(auth()->user())
->performedOn(new Product())
->withProperties([
'total_rows' => $result->totalRows,
'successful_rows' => $result->successfulRows,
'failed_rows' => $result->failedRows,
'duration' => $result->duration,
])
->log('product_import_completed');
})
Environment-Specific Security
1. Production Configuration
// config/ingest.php - Production
'allowed_file_types' => ['csv', 'xlsx'],
'max_file_size' => 10 * 1024 * 1024, // 10MB
'log_rows' => true, // Enable for audit trail
'queue_connection' => 'redis', // Use reliable queue
'transaction_mode' => 'chunk', // Safer imports
'chunk_size' => 100, // Smaller chunks for reliability
2. Development Configuration
// config/ingest.php - Local Development
'allowed_file_types' => ['csv', 'xlsx', 'txt', 'json'],
'max_file_size' => 50 * 1024 * 1024, // 50MB for testing
'log_rows' => true, // Enable for debugging
'queue_connection' => 'database', // Simple queue for dev
'transaction_mode' => 'row', // Maximum safety
'chunk_size' => 10, // Small chunks for debugging
3. Staging Configuration
// config/ingest.php - Staging
'allowed_file_types' => ['csv', 'xlsx'],
'max_file_size' => 20 * 1024 * 1024, // 20MB
'log_rows' => true,
'queue_connection' => 'redis',
'transaction_mode' => 'chunk',
'chunk_size' => 50,
Data Privacy and Compliance
1. GDPR Considerations
->beforeRow(function(array &$row) {
// Remove or anonymize sensitive data if not needed
$sensitiveFields = ['ssn', 'credit_card', 'bank_account'];
foreach ($sensitiveFields as $field) {
if (isset($row[$field])) {
unset($row[$field]);
}
}
// Log data processing for compliance
if (isset($row['email'])) {
Log::info('Processing user data', [
'email_hash' => hash('sha256', $row['email']),
'purpose' => 'product_import',
'legal_basis' => 'legitimate_interest',
]);
}
})
2. Data Retention Policies
// In your Importer class
public function getConfig(): IngestConfig
{
return IngestConfig::for(Product::class)
->afterImport(function(ImportResult $result) {
// Clean up temporary files after processing
$this->cleanupTempFiles();
// Archive import logs after 30 days
$this->archiveImportLogs($result->importId);
});
}
Security Headers and CORS
// In your ImportController
public function store(Request $request)
{
// Set security headers
return response()->json([
'message' => 'Import started',
'import_id' => $importId,
], 202)->withHeaders([
'X-Content-Type-Options' => 'nosniff',
'X-Frame-Options' => 'DENY',
'X-XSS-Protection' => '1; mode=block',
'Strict-Transport-Security' => 'max-age=31536000; includeSubDomains',
]);
}
Monitoring and Alerting
1. Security Event Monitoring
->beforeRow(function(array &$row) {
// Monitor for suspicious patterns
$suspiciousPatterns = [
'/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/mi',
'/javascript:/i',
'/vbscript:/i',
'/onload\s*=/i',
'/onerror\s*=/i',
];
foreach ($row as $key => $value) {
if (is_string($value)) {
foreach ($suspiciousPatterns as $pattern) {
if (preg_match($pattern, $value)) {
// Log security event
Log::alert('Suspicious content detected', [
'field' => $key,
'pattern' => $pattern,
'user_id' => auth()->id(),
'ip_address' => request()->ip(),
]);
// Optionally block the import
throw new SecurityException('Suspicious content detected');
}
}
}
}
})
2. Performance Monitoring
->afterImport(function(ImportResult $result) {
// Alert on unusual import patterns
if ($result->failedRows > $result->totalRows * 0.1) {
// More than 10% failure rate - alert administrators
Notification::route('mail', ['admin@company.com'])
->notify(new HighFailureRateAlert($result));
}
if ($result->duration > 300) { // 5 minutes
// Unusually long import time
Notification::route('slack', config('services.slack.webhook'))
->notify(new SlowImportAlert($result));
}
})
Security Checklist
- File type restrictions configured
- File size limits enforced
- Input validation and sanitization implemented
- SQL injection prevention measures in place
- Access control and permissions configured
- Rate limiting applied to import endpoints
- Comprehensive audit logging enabled
- Environment-specific security configurations
- Data privacy and compliance measures implemented
- Security headers and CORS properly configured
- Monitoring and alerting systems active
- Regular security reviews and penetration testing
Recommended Security Packages
# Additional security packages for Laravel
composer require spatie/laravel-security
composer require laravel/fortify
composer require spatie/laravel-activitylog
Remember: Security is an ongoing process, not a one-time configuration. Regularly review and update your security measures to protect against new threats.