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

Ciphersweet Laravel Package

paragonie/ciphersweet

CipherSweet is a PHP library for field-level encryption with searchable encrypted data. It helps you securely encrypt database columns while still supporting safe, blind-index-based search and sorting, with modern cryptography and key management support.

Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require paragonie/ciphersweet
    

    Add to composer.json if using a custom repository.

  2. First Use Case: Encrypt a sensitive field (e.g., email) in a Laravel model:

    use ParagonIE\CipherSweet\CipherSweet;
    use ParagonIE\CipherSweet\KeyProvider\FileKeyProvider;
    
    // Initialize in a service provider (e.g., AppServiceProvider)
    $keyProvider = new FileKeyProvider(storage_path('app/ciphersweet-keys'));
    $cipherSweet = new CipherSweet($keyProvider);
    
    // Encrypt a value
    $encryptedEmail = $cipherSweet->encrypt('user@example.com', 'email');
    
    // Decrypt later
    $decryptedEmail = $cipherSweet->decrypt($encryptedEmail, 'email');
    
  3. Key Management: Generate keys via CLI:

    php vendor/bin/ciphersweet generate-keys storage/app/ciphersweet-keys
    

    Store keys securely (e.g., encrypted in .env or a secrets manager).


Implementation Patterns

Field-Level Encryption in Eloquent

  1. Model Integration: Use accessors/mutators for automatic encryption/decryption:

    class User extends Model {
        protected $casts = [
            'email' => EncryptedAttribute::class,
        ];
    
        // Or manually:
        public function setEmailAttribute($value) {
            $this->attributes['email'] = $cipherSweet->encrypt($value, 'email');
        }
    
        public function getEmailAttribute($value) {
            return $cipherSweet->decrypt($value, 'email');
        }
    }
    
  2. Query Filtering: Leverage CipherSweet’s searchable encryption for filtered queries:

    // Encrypt with a searchable token
    $encryptedEmail = $cipherSweet->encrypt('user@example.com', 'email', ['searchable' => true]);
    
    // Query encrypted fields (requires a search index)
    $users = User::whereEncrypted('email', 'user@example.com')->get();
    
  3. Batch Operations: Use CipherSweet::encryptBatch() for bulk data:

    $encryptedData = $cipherSweet->encryptBatch([
        'email' => 'user@example.com',
        'phone' => '123-456-7890',
    ], ['email', 'phone']);
    

Workflows

  • Data Migration: Encrypt existing data in a migration:

    public function up() {
        User::chunk(100, function ($users) {
            foreach ($users as $user) {
                $user->email = $this->cipherSweet->encrypt($user->email, 'email');
                $user->save();
            }
        });
    }
    
  • API Responses: Decrypt data before sending to clients:

    return User::find($id)->load(['posts' => function ($query) {
        $query->select('id', 'title', 'encrypted_content');
    }])->map(function ($user) {
        $user->posts->each(function ($post) {
            $post->content = $this->cipherSweet->decrypt($post->encrypted_content, 'content');
        });
        return $user;
    });
    

Gotchas and Tips

Pitfalls

  1. Key Rotation:

    • Rotate keys periodically (use KeyProvider::rotateKeys()).
    • Gotcha: Decrypted data may fail if old keys are purged before rotation completes.
    • Fix: Use a key transition period (keep old keys available temporarily).
  2. Searchability:

    • Gotcha: Searchable encryption requires a search index (e.g., PostgreSQL pgcrypto or Elasticsearch).
    • Fix: Configure CipherSweet with a search provider:
      $cipherSweet = new CipherSweet($keyProvider, [
          'search_provider' => new ParagonIE\CipherSweet\SearchProvider\PostgresSearchProvider(
              config('database.connections.pgsql')
          ),
      ]);
      
  3. Performance:

    • Gotcha: Encryption/decryption adds latency (~1-5ms per field).
    • Fix: Cache decrypted values in memory (e.g., LaravelCache) for frequently accessed data.
  4. Plaintext Exposure:

    • Gotcha: Accidentally logging or dumping encrypted data may reveal plaintext if misconfigured.
    • Fix: Use CipherSweet::isEncrypted() to verify data before processing:
      if ($cipherSweet->isEncrypted($value, 'email')) {
          $decrypted = $cipherSweet->decrypt($value, 'email');
      }
      

Debugging

  • Invalid Keys:

    • Error: Key not found for context 'email'.
    • Fix: Verify keys exist in FileKeyProvider storage and permissions are correct (chmod 600).
  • Corrupted Data:

    • Error: Decryption failed.
    • Fix: Check for truncated data or incorrect context labels. Use CipherSweet::validate():
      if (!$cipherSweet->validate($encryptedData, 'email')) {
          throw new \RuntimeException('Invalid encrypted data');
      }
      

Extension Points

  1. Custom Key Providers: Extend KeyProviderInterface for cloud storage (e.g., AWS KMS):

    class AwsKmsKeyProvider implements KeyProviderInterface {
        // Implement getKey(), rotateKeys(), etc.
    }
    
  2. Context-Specific Logic: Override CipherSweet to add field-specific rules:

    $cipherSweet = new class($keyProvider) extends CipherSweet {
        public function encrypt($plaintext, $context, $options = []) {
            if ($context === 'ssn') {
                $options['searchable'] = false; // Never search SSNs
            }
            return parent::encrypt($plaintext, $context, $options);
        }
    };
    
  3. Laravel Integration: Publish config and create a facade for cleaner usage:

    // config/ciphersweet.php
    return [
        'key_path' => storage_path('app/ciphersweet-keys'),
        'search_provider' => env('CIPHERSWEET_SEARCH_PROVIDER', null),
    ];
    
    // Facade
    Facades\CipherSweet::encrypt($value, 'context');
    
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport
twbs/bootstrap4
php-http/client-implementation
phpcr/phpcr-implementation
cucumber/gherkin-monorepo
haydenpierce/class-finder
psr/simple-cache-implementation