Weave 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 Conceal Laravel Package

kielabokkie/laravel-conceal

Laravel Conceal hides sensitive model IDs in your URLs by encrypting route keys, so links reveal opaque strings instead of sequential database IDs. Add the trait, and route model binding keeps working while exposing less.

View on GitHub
Deep Wiki
Context7

Getting Started

kielabokkie/laravel-conceal gives Eloquent models transparent attribute encryption: you assign plaintext, the package encrypts on write using Laravel's APP_KEY, and decrypts on read. v1.4.0 is primarily a compatibility release — it adds PHP 8 installation support and updates the CI matrix — so if you were blocked from upgrading to PHP 8, this unblocks you. There are no breaking changes, no new config, and no migration required from v1.3.0.

Minimal setup:

composer require kielabokkie/laravel-conceal
  1. Ensure the columns you want to conceal are TEXT/LONGTEXT (ciphertext is much longer than the plaintext, and base64-encoded JSON payloads overflow VARCHAR(255) fast).
  2. Add the package trait to the model and declare which attributes should be concealed.
  3. Confirm APP_KEY is set in every environment — the package relies on Laravel's encrypter, and a missing/rotated key means unreadable data.

First use case: a users table with ssn, phone, or api_token columns. Add the trait, list the attributes, and your existing $user->ssn = '...' / echo $user->ssn code keeps working while the database only ever stores ciphertext.

Implementation Patterns

Trait-per-model, explicit attribute list. Apply the trait only to models that need it and keep the concealed attribute list tight. Prefer this over a global/base-model trait — it documents intent and avoids accidental encryption of columns you later want to query or index.

Column migration first. Before enabling the trait, migrate the target columns to text. If you have existing plaintext rows, plan a backfill: iterate with chunkById(), re-assign each concealed attribute from its current value (forcing a dirty write), and save. Do this in a queued job or command so it is idempotent and resumable; wrap in a transaction per batch where the model allows it.

Validate plaintext, store ciphertext. Form Requests and validation rules should operate on the plaintext value before it hits the model. The encryption layer is a persistence concern, not a validation concern — don't try to validate against decrypted DB values in rules.

Keep concealed attributes out of serialization. Add concealed fields to $hidden (or strip them in API Resources). Anything that ends up in toArray()/toJson() is plaintext and will leak through logs, queued payloads, and API responses.

Lookups need a companion hash column. Because each write uses a fresh IV, ciphertext is non-deterministic — where('ssn', $value) will never match. For "find by SSN/email" flows, add a deterministic ssn_hash column (hash('sha256', $normalized)) maintained alongside the concealed attribute, and query that. Same technique for uniqueness constraints.

Key rotation. Laravel's APP_KEY rotation invalidates all previously encrypted values unless you decrypt with the old key first. Build a one-off command that reads with the old key, writes with the new key, then decommission the old key. Keep the previous key in a secure store until the rotation job has fully completed and been verified.

Testing. Set a known APP_KEY in phpunit.xml so ciphertext is deterministic per test run (IV still random, so never assert raw DB strings). Assert behaviour — that the attribute round-trips and that the raw column differs from the plaintext — rather than exact encrypted output.

Gotchas and Tips

  • Column length is the #1 footgun. Laravel's encrypted payload is base64 JSON with an IV, MAC, and tag. A 9-character SSN becomes ~200+ characters. Use text, not varchar(255), and be generous.
  • No where, orderBy, or like on concealed columns. This is the fundamental trade-off. If you need to search or sort, store a separate hash/index column or keep a non-sensitive derivative.
  • Unique indexes won't protect you. A unique constraint on the ciphertext column is meaningless with random IVs. Enforce uniqueness on the hash column instead, and normalise (lowercase/trim) before hashing.
  • Don't double-encrypt. If a column is already using Laravel's encrypted cast or a custom encrypted cast, adding the conceal trait on top produces nested payloads that are painful to unwind. Pick one mechanism per attribute.
  • Silent decryption failures. A corrupted value, a changed APP_KEY, or a truncated varchar column throws a DecryptException on read. Catch it at the boundary (e.g. in an API Resource) if you need graceful degradation, and log loudly — it almost always means a key or column-type problem.
  • isDirty()/getOriginal() behave on ciphertext. Comparison logic that inspects raw attributes will see encrypted strings. Compare via the decrypted accessor, not getRawOriginal().
  • Mass assignment and update() bypass nothing — they go through the model and are encrypted correctly. But DB::table()->update() and raw query builder writes bypass the trait entirely and will store plaintext. Audit any raw writes to those columns.
  • Backups and dumps contain ciphertext only — good for security, but it means you cannot restore usable data without the matching APP_KEY. Store the key separately from the database backup, and document the pairing.
  • PHP 8 note for this release: v1.4.0 is the first version installable on PHP 8.x. If you run a mixed matrix, pin ^1.4 explicitly rather than relying on ^1.3 constraints resolving upward. Check your CI matrix covers PHP 8.0/8.1 as the release updated upstream's own matrix.
  • Extension point: if you need custom serialization (e.g. encrypting JSON structures or arrays), wrap the attribute in a custom cast that calls the package's encrypt/decrypt path rather than adding ad-hoc logic to the model.
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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log