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

Laravel Ciphersweet Laravel Package

spatie/laravel-ciphersweet

Laravel wrapper for Paragonie CipherSweet that adds searchable field-level encryption to Eloquent models. Encrypt/decrypt sensitive attributes and generate blind indexes so you can query encrypted data securely without exposing readable values in your database.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require spatie/laravel-ciphersweet
    php artisan vendor:publish --tag="ciphersweet-migrations"
    php artisan migrate
    

    Publish config (optional):

    php artisan vendor:publish --tag="ciphersweet-config"
    
  2. Configure Model Add UsesCipherSweet trait and implement CipherSweetEncrypted:

    use Spatie\LaravelCipherSweet\Concerns\UsesCipherSweet;
    use Spatie\LaravelCipherSweet\Contracts\CipherSweetEncrypted;
    
    class User implements CipherSweetEncrypted {
        use UsesCipherSweet;
    
        public static function configureCipherSweet(EncryptedRow $encryptedRow): void {
            $encryptedRow
                ->addField('email')
                ->addBlindIndex('email', new BlindIndex('email_index'));
        }
    }
    
  3. Generate & Set Key

    php artisan ciphersweet:generate-key
    

    Add to .env:

    CIPHERSWEET_KEY=<generated-key>
    
  4. Encrypt Existing Data

    php artisan ciphersweet:encrypt App\Models\User
    

First Use Case

Encrypt a new user's email:

$user = User::create(['email' => 'user@example.com']);
// Automatically encrypted via model events

Implementation Patterns

Core Workflow

  1. Model Setup

    • Define encrypted fields in configureCipherSweet():
      $encryptedRow
          ->addField('ssn') // Encrypts as text
          ->addBooleanField('is_active')
          ->addBlindIndex('ssn', new BlindIndex('ssn_index', 128));
      
  2. Database Schema

    • Migrate with text columns for encrypted fields:
      Schema::table('users', function (Blueprint $table) {
          $table->text('ssn')->nullable();
      });
      
  3. Searching

    • Use blind indexes for encrypted searches:
      $users = User::whereBlind('ssn', 'ssn_index', '123-45-6789')->get();
      
  4. Validation

    • Enforce uniqueness via blind indexes:
      $request->validate([
          'email' => [Rule::encryptedUnique(User::class, 'email_index')],
      ]);
      

Integration Tips

  • Laravel Events: Leverage saving, updating events to auto-encrypt:

    protected static function booted(): void {
        static::saving(function ($model) {
            $model->encrypt();
        });
    }
    
  • API Responses: Decrypt before returning:

    $user = User::find(1);
    return response()->json(['email' => $user->email]); // Auto-decrypted
    
  • Batch Processing: Use chunking for large datasets:

    User::chunk(100, function ($users) {
        foreach ($users as $user) {
            $user->encrypt();
            $user->save();
        }
    });
    
  • Key Rotation:

    php artisan ciphersweet:generate-key
    php artisan ciphersweet:encrypt App\Models\User --new-key=<new-key>
    

Gotchas and Tips

Pitfalls

  1. Migration Order

    • Run ciphersweet:encrypt after migrations. The package adds blind_indexes table automatically.
  2. Field Types

    • Mismatch: Ensure DB columns match field types (e.g., text for addField()).
    • Optional Fields: Use addOptional*Field() to skip encryption for NULL values.
  3. Key Management

    • Backup Keys: Store keys securely (e.g., AWS KMS, HashiCorp Vault). Losing the key means permanent data loss.
    • Environment Switches: Update keys in all environments (dev/staging/prod) during rotation.
  4. Performance

    • Blind Index Size: Larger bit lengths (e.g., 256) improve search accuracy but increase storage.
    • Index Queries: Blind indexes are exact-match only. Avoid partial searches.
  5. Validation Quirks

    • EncryptedUniqueRule requires the model to implement CipherSweetEncrypted and define the blind index.

Debugging

  • Encryption Failures:

    • Check CIPHERSWEET_KEY in .env is correct.
    • Verify the backend (nacl, boring, etc.) is supported by your PHP version.
  • Search Issues:

    • Ensure the blind index name matches in configureCipherSweet() and queries:
      // Wrong: Non-existent index
      User::whereBlind('email', 'wrong_index', 'test@example.com');
      
      // Correct:
      User::whereBlind('email', 'email_index', 'test@example.com');
      
  • Key Rotation Errors:

    • Run php artisan ciphersweet:encrypt with --force to re-encrypt all data:
      php artisan ciphersweet:encrypt App\Models\User --force
      

Extension Points

  1. Custom Backends

    • Override encryption logic (e.g., for compliance):
      config([
          'ciphersweet.backend' => 'custom',
          'ciphersweet.backend.custom' => \App\Providers\CustomBackendFactory::class,
      ]);
      
  2. Key Providers

    • Fetch keys from external sources (e.g., API):
      config([
          'ciphersweet.provider' => 'custom',
          'ciphersweet.providers.custom' => \App\Providers\ApiKeyProvider::class,
      ]);
      
  3. Field Maps for JSON

    • Encrypt nested JSON structures:
      $fieldMap = [
          'address.city' => 'text',
          'address.zip' => 'text',
      ];
      $encryptedRow->addJsonField('metadata', $fieldMap);
      
  4. Query Scopes

    • Extend search functionality:
      public function scopeWhereEmailContains($query, string $term) {
          // Note: Blind indexes don't support partial matches.
          // This is a placeholder for custom logic.
      }
      

Pro Tips

  • Testing:

    • Use CIPHERSWEET_PROVIDER=random in .env for ephemeral keys in tests.
    • Mock UsesCipherSweet trait for unit tests:
      $model->shouldReceive('encrypt')->once();
      
  • Monitoring:

    • Log key rotation events:
      event(new KeyRotated($oldKey, $newKey));
      
  • Legacy Data:

    • Use --skip-existing to avoid re-encrypting already-processed records:
      php artisan ciphersweet:encrypt App\Models\User --skip-existing
      
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.
codraw/framework-extra-bundle
codraw/messenger
codraw/security
codraw/mailer
codraw/contracts
codraw/profiling
codraw/dependency-injection
codraw/tester
codraw/core
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony