Security & Performance
Data Privacy and GDPR
Data Privacy and GDPR
The General Data Protection Regulation (GDPR) sets standards for data protection and privacy in the EU, with global implications for web applications.
GDPR Overview
Key GDPR Principles:
- Lawfulness: Legal basis for processing data
- Data Minimization: Collect only necessary data
- Purpose Limitation: Use data only for stated purposes
- Accuracy: Keep data accurate and up-to-date
- Storage Limitation: Delete data when no longer needed
- Integrity & Confidentiality: Secure data processing
- Accountability: Demonstrate compliance
Data Minimization
Collect only essential information:
// ❌ BAD: Collecting unnecessary data
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('email');
$table->string('password');
$table->string('social_security_number'); // Not needed!
$table->string('mother_maiden_name'); // Not needed!
$table->date('birth_date'); // Use age range instead
});
// ✅ GOOD: Minimal data collection
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('email');
$table->string('password');
$table->string('age_range'); // '18-24', '25-34', etc.
});
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('email');
$table->string('password');
$table->string('social_security_number'); // Not needed!
$table->string('mother_maiden_name'); // Not needed!
$table->date('birth_date'); // Use age range instead
});
// ✅ GOOD: Minimal data collection
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('email');
$table->string('password');
$table->string('age_range'); // '18-24', '25-34', etc.
});
Consent Management
Implement explicit consent mechanisms:
// database/migrations/xxxx_add_consent_to_users.php
Schema::table('users', function (Blueprint $table) {
$table->boolean('marketing_consent')->default(false);
$table->boolean('analytics_consent')->default(false);
$table->timestamp('consent_date')->nullable();
$table->string('consent_ip')->nullable();
});
// app/Http/Controllers/ConsentController.php
public function updateConsent(Request $request)
{
$request->validate([
'marketing_consent' => 'boolean',
'analytics_consent' => 'boolean',
]);
auth()->user()->update([
'marketing_consent' => $request->marketing_consent,
'analytics_consent' => $request->analytics_consent,
'consent_date' => now(),
'consent_ip' => $request->ip(),
]);
return response()->json(['message' => 'Consent updated']);
}
Schema::table('users', function (Blueprint $table) {
$table->boolean('marketing_consent')->default(false);
$table->boolean('analytics_consent')->default(false);
$table->timestamp('consent_date')->nullable();
$table->string('consent_ip')->nullable();
});
// app/Http/Controllers/ConsentController.php
public function updateConsent(Request $request)
{
$request->validate([
'marketing_consent' => 'boolean',
'analytics_consent' => 'boolean',
]);
auth()->user()->update([
'marketing_consent' => $request->marketing_consent,
'analytics_consent' => $request->analytics_consent,
'consent_date' => now(),
'consent_ip' => $request->ip(),
]);
return response()->json(['message' => 'Consent updated']);
}
Right to Deletion (Right to be Forgotten)
Allow users to request data deletion:
// app/Http/Controllers/DataDeletionController.php
public function requestDeletion(Request $request)
{
$user = auth()->user();
// Create deletion request
DeletionRequest::create([
'user_id' => $user->id,
'status' => 'pending',
'requested_at' => now(),
]);
// Notify admins
Mail::to(config('mail.admin'))
->send(new DataDeletionRequested($user));
return response()->json([
'message' => 'Deletion request received. Will be processed within 30 days.',
]);
}
public function processDeleteUser(User $user)
{
// Anonymize instead of hard delete for audit trail
$user->update([
'email' => 'deleted_' . $user->id . '@deleted.local',
'name' => '[Deleted User]',
'password' => Hash::make(Str::random(64)),
'deleted_at' => now(),
]);
// Delete personal data from related tables
$user->addresses()->delete();
$user->phoneNumbers()->delete();
$user->preferences()->delete();
}
public function requestDeletion(Request $request)
{
$user = auth()->user();
// Create deletion request
DeletionRequest::create([
'user_id' => $user->id,
'status' => 'pending',
'requested_at' => now(),
]);
// Notify admins
Mail::to(config('mail.admin'))
->send(new DataDeletionRequested($user));
return response()->json([
'message' => 'Deletion request received. Will be processed within 30 days.',
]);
}
public function processDeleteUser(User $user)
{
// Anonymize instead of hard delete for audit trail
$user->update([
'email' => 'deleted_' . $user->id . '@deleted.local',
'name' => '[Deleted User]',
'password' => Hash::make(Str::random(64)),
'deleted_at' => now(),
]);
// Delete personal data from related tables
$user->addresses()->delete();
$user->phoneNumbers()->delete();
$user->preferences()->delete();
}
Important: Some data must be retained for legal/financial reasons (e.g., transaction records for 7 years). Anonymize this data instead of deleting it.
Data Encryption
Encrypt sensitive data at rest:
use Illuminate\Support\Facades\Crypt;
// Encrypting data
$encrypted = Crypt::encryptString($user->social_security_number);
DB::table('users')->where('id', $user->id)
->update(['ssn_encrypted' => $encrypted]);
// Decrypting data
$decrypted = Crypt::decryptString($user->ssn_encrypted);
// Using Laravel's encrypted casting
class User extends Model
{
protected $casts = [
'ssn' => 'encrypted',
'credit_card' => 'encrypted',
];
}
// Encrypting data
$encrypted = Crypt::encryptString($user->social_security_number);
DB::table('users')->where('id', $user->id)
->update(['ssn_encrypted' => $encrypted]);
// Decrypting data
$decrypted = Crypt::decryptString($user->ssn_encrypted);
// Using Laravel's encrypted casting
class User extends Model
{
protected $casts = [
'ssn' => 'encrypted',
'credit_card' => 'encrypted',
];
}
Privacy by Design
Build privacy into your application architecture:
// Example: Automatic data expiration
// app/Console/Commands/DeleteExpiredData.php
class DeleteExpiredData extends Command
{
protected $signature = 'privacy:cleanup';
public function handle()
{
// Delete logs older than 90 days
ActivityLog::where('created_at', '<', now()->subDays(90))
->delete();
// Delete unverified accounts after 7 days
User::whereNull('email_verified_at')
->where('created_at', '<', now()->subDays(7))
->delete();
// Anonymize inactive accounts after 3 years
User::where('last_login_at', '<', now()->subYears(3))
->each(fn($user) => $this->anonymizeUser($user));
}
}
// app/Console/Commands/DeleteExpiredData.php
class DeleteExpiredData extends Command
{
protected $signature = 'privacy:cleanup';
public function handle()
{
// Delete logs older than 90 days
ActivityLog::where('created_at', '<', now()->subDays(90))
->delete();
// Delete unverified accounts after 7 days
User::whereNull('email_verified_at')
->where('created_at', '<', now()->subDays(7))
->delete();
// Anonymize inactive accounts after 3 years
User::where('last_login_at', '<', now()->subYears(3))
->each(fn($user) => $this->anonymizeUser($user));
}
}
Cookie Compliance
Implement cookie consent banner:
<!-- Cookie Consent Banner -->
<div id="cookie-banner" class="cookie-banner">
<p>We use cookies to improve your experience.
<a href="/privacy-policy">Learn more</a></p>
<div class="cookie-buttons">
<button onclick="acceptAllCookies()">Accept All</button>
<button onclick="acceptEssentialOnly()">Essential Only</button>
<button onclick="showCookieSettings()">Customize</button>
</div>
</div>
<script>
function acceptAllCookies() {
setCookie('cookie_consent', 'all', 365);
enableAnalytics();
enableMarketing();
hideCookieBanner();
}
function acceptEssentialOnly() {
setCookie('cookie_consent', 'essential', 365);
hideCookieBanner();
}
</script>
<div id="cookie-banner" class="cookie-banner">
<p>We use cookies to improve your experience.
<a href="/privacy-policy">Learn more</a></p>
<div class="cookie-buttons">
<button onclick="acceptAllCookies()">Accept All</button>
<button onclick="acceptEssentialOnly()">Essential Only</button>
<button onclick="showCookieSettings()">Customize</button>
</div>
</div>
<script>
function acceptAllCookies() {
setCookie('cookie_consent', 'all', 365);
enableAnalytics();
enableMarketing();
hideCookieBanner();
}
function acceptEssentialOnly() {
setCookie('cookie_consent', 'essential', 365);
hideCookieBanner();
}
</script>
Data Export (Right to Portability)
Allow users to download their data:
// app/Http/Controllers/DataExportController.php
public function export(Request $request)
{
$user = auth()->user();
$data = [
'personal_info' => [
'name' => $user->name,
'email' => $user->email,
'created_at' => $user->created_at,
],
'orders' => $user->orders->toArray(),
'addresses' => $user->addresses->toArray(),
'preferences' => $user->preferences->toArray(),
];
return response()->json($data)
->header('Content-Disposition', 'attachment; filename="my_data.json"')
->header('Content-Type', 'application/json');
}
public function export(Request $request)
{
$user = auth()->user();
$data = [
'personal_info' => [
'name' => $user->name,
'email' => $user->email,
'created_at' => $user->created_at,
],
'orders' => $user->orders->toArray(),
'addresses' => $user->addresses->toArray(),
'preferences' => $user->preferences->toArray(),
];
return response()->json($data)
->header('Content-Disposition', 'attachment; filename="my_data.json"')
->header('Content-Type', 'application/json');
}
Best Practice: Appoint a Data Protection Officer (DPO), conduct regular privacy audits, maintain records of processing activities, and implement breach notification procedures.
Exercise: Audit your application for GDPR compliance. Implement consent management, data export functionality, and a privacy policy page. Test the right to deletion workflow.