Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Encryptable Laravel Package

gregoryduckworth/encryptable

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation Add the package via Composer:

    composer require gregoryduckworth/encryptable:^1.0
    

    No additional configuration is required.

  2. First Use Case Apply the trait to an Eloquent model and define which attributes to encrypt:

    use GregoryDuckworth\Encryptable\EncryptableTrait;
    
    class User extends Model
    {
        use EncryptableTrait;
    
        protected $encryptable = ['ssn', 'credit_card'];
    }
    

    Now, any interaction with ssn or credit_card (e.g., User::create(), user->ssn = '123') will automatically encrypt the data before saving and decrypt it upon retrieval.

  3. Where to Look First

    • Model Definition: Check the $encryptable array to confirm which fields are encrypted.
    • Database: Verify encrypted values in the database (they’ll appear as gibberish).
    • Logs: Use dd($user->ssn) to confirm decryption works during retrieval.

Implementation Patterns

Usage Patterns

  1. Basic Encryption/Decryption

    // Automatically encrypts on save, decrypts on retrieval
    $user = User::create(['ssn' => '123-45-6789']);
    echo $user->ssn; // Decrypted value: '123-45-6789'
    
  2. Mass Assignment

    // Encrypts all encryptable fields in bulk
    User::create([
        'ssn' => '123-45-6789',
        'credit_card' => '4111-1111-1111-1111',
        'name' => 'John Doe' // Not encrypted (not in $encryptable)
    ]);
    
  3. Manual Encryption/Decryption Use accessors/mutators for custom logic:

    public function getSsnAttribute($value)
    {
        return $this->encryptableDecrypt($value);
    }
    
    public function setSsnAttribute($value)
    {
        $this->attributes['ssn'] = $this->encryptableEncrypt($value);
    }
    
  4. Querying Encrypted Fields Avoid querying encrypted fields directly. Instead, use whereRaw with decrypted values:

    // ❌ Avoid (encrypts the query value)
    User::where('ssn', '123-45-6789')->get();
    
    // ✅ Use raw SQL for equality checks
    User::whereRaw("ssn = ?", [$this->encryptableEncrypt('123-45-6789')])->get();
    
  5. API Responses Ensure encrypted fields are decrypted in API responses by default (Laravel’s JSON serialization handles this automatically).

Workflows

  • Sensitive Data Handling: Use for PII (e.g., SSNs, credit cards) or compliance requirements (e.g., GDPR).
  • Partial Encryption: Encrypt only specific fields while leaving others plaintext.
  • Testing: Mock the trait’s encryption methods or use config(['encryptable.key' => 'test-key']) for consistent test data.

Integration Tips

  • Laravel Policies/Authorization: Encrypted fields can be used in policies, but ensure decryption happens before checks:
    public function authorize(User $user)
    {
        return $user->ssn === $this->encryptableEncrypt('123-45-6789');
    }
    
  • Form Requests: Validate encrypted fields by decrypting them first:
    public function rules()
    {
        return [
            'ssn' => ['required', function ($attribute, $value, $fail) {
                $decrypted = $this->user()->encryptableDecrypt($value);
                if (!preg_match('/^\d{3}-\d{2}-\d{4}$/', $decrypted)) {
                    $fail('Invalid SSN format.');
                }
            }]
        ];
    }
    
  • Caching: Avoid caching models with encrypted fields, as decrypted values may change.

Gotchas and Tips

Pitfalls

  1. Performance Overhead: Encryption/decryption adds latency. Avoid encrypting frequently queried or large fields.

    • Mitigation: Use only for truly sensitive data.
  2. Query Limitations:

    • No LIKE on Encrypted Fields: Encrypted values cannot be searched with LIKE or ILIKE.
      • Workaround: Use full-text search on decrypted values in application logic.
    • No ORDER BY: Sorting by encrypted fields is unsupported.
      • Workaround: Decrypt values in memory after retrieval.
  3. Key Management: The package uses Laravel’s default encryption key (config('app.key')). If this changes, encrypted data becomes unreadable.

    • Tip: Document key rotation procedures and backup encrypted data before changes.
  4. Serialization Issues: Encrypted fields may cause problems with Laravel’s serialization (e.g., replicate(), fresh()).

    • Fix: Exclude encrypted fields from replication:
      protected $encryptable = ['ssn'];
      protected $dontReplicate = ['ssn'];
      
  5. Mass Assignment Risks: Encrypted fields are still vulnerable to mass assignment attacks if not protected.

    • Tip: Use $guarded or $fillable to restrict which fields can be mass-assigned.
  6. Database Indexes: Encrypted fields cannot be indexed effectively. Avoid indexing them.

    • Tip: Create a separate, non-encrypted column for indexing if needed (e.g., ssn_hash).

Debugging

  1. Encryption/Decryption Failures:

    • Check config('app.key') matches expectations.
    • Verify the field is listed in $encryptable.
    • Test with a hardcoded value:
      $encrypted = $user->encryptableEncrypt('test');
      $decrypted = $user->encryptableDecrypt($encrypted);
      dd($decrypted === 'test'); // Should be true
      
  2. Silent Failures: The trait may silently fail if encryption keys are mismatched. Log decryption attempts:

    protected function encryptableDecrypt($value)
    {
        try {
            return parent::encryptableDecrypt($value);
        } catch (\Exception $e) {
            \Log::error("Decryption failed for {$this->getKey()}: " . $e->getMessage());
            return null;
        }
    }
    
  3. Database Corruption: If the database contains corrupted encrypted data (e.g., due to key changes), the trait will throw exceptions.

    • Recovery: Re-encrypt data manually or restore from backups.

Tips

  1. Configuration: Customize the encryption algorithm or key via config:

    // config/encryptable.php
    return [
        'key' => env('ENCRYPTABLE_KEY', config('app.key')),
        'algorithm' => 'AES-256-CBC',
    ];
    
  2. Partial Updates: Use update() carefully—it may re-encrypt all encryptable fields:

    // Re-encrypts ALL encryptable fields, even unchanged ones
    $user->update(['ssn' => 'new-value']);
    
    // Better: Update only the encrypted field
    $user->ssn = 'new-value';
    $user->save();
    
  3. Testing: Use a dedicated test key to avoid polluting production data:

    // In tests
    config(['encryptable.key' => 'test-key-for-encryptable']);
    
  4. Migration Safety: If adding encryption to an existing table, ensure backward compatibility:

    • Add a new column (e.g., ssn_encrypted) and migrate data gradually.
    • Use a nullable column until all data is encrypted.
  5. Audit Logs: Track changes to encrypted fields by overriding saving():

    protected static function bootEncryptable()
    {
        static::saving(function ($model) {
            foreach ($model->encryptable as $field) {
                if ($model->wasChanged($field)) {
                    \Log::audit("Encrypted field {$field} updated for ID {$model->id}");
                }
            }
        });
    }
    
  6. Fallback for Missing Key: Handle cases where the encryption key might be missing:

    protected function encryptableEncrypt($value)
    {
        if (empty(config('encryptable.key'))) {
            return $value; // Fallback to plaintext (or throw an exception)
        }
        return parent::encryptableEncrypt($value);
    }
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle