#
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.