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 Secure Fields Laravel Package

vimatech/laravel-secure-fields

Laravel package that adds transparent encryption for sensitive database fields. Protect PII, tokens, and secrets with automatic encrypt/decrypt through Eloquent casts, no schema changes or query rewrites, plus configurable keys, key rotation, and searchable blind indexes.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. The package intervenes at exactly the right seam: Eloquent attribute casts. Encryption becomes a model-layer concern, so controllers, form requests, and accessors are untouched and adoption cost per field is near zero. The critical architectural caveat is that the guarantee stops precisely at the model boundary — everything below it (query builder, DB::table(), replicas, BI tools, exports) sees ciphertext, and writes that bypass the model write plaintext into a column the rest of the system assumes is encrypted. Fit therefore depends less on the package than on how disciplined the codebase is about routing reads and writes through models. Codebases with heavy bulk-update, importer, or ETL paths on sensitive columns are a poor fit without remediation work first.

Integration feasibility. Mechanically easy: install, publish config, generate two independent keys (encryption + blind-index hash — no APP_KEY fallback, by design), widen target columns to TEXT or generous VARCHAR, add indexed hash columns, declare fields on models. The friction is not code, it is secrets and environments: both keys must exist in every environment that touches the data, including CI, preview deploys, queue workers, and scheduled tasks. Fail-closed behavior is a security strength and an operational tax.

Technical risk. The dominant risk is supply chain, not cryptography. Zero stars, zero dependents, a single maintainer, and a patch release (v2.0.1) that changed user-facing message punctuation are all signals that this is an early-stage, thinly-staffed, security-critical dependency. Mitigants: it delegates actual crypto to Laravel's encrypter (AES-256-GCM) rather than inventing primitives, and it is MIT-licensed so a fork is always available. The second risk tier is silent correctness failure — mass updates writing plaintext, firstOrCreate/updateOrCreate on a secure attribute creating duplicate rows on every call, narrow columns truncating ciphertext that only surfaces as "unreadable values" during a future rotation. None of these throw. The third tier is egress: decrypted values populate the attribute and therefore flow into toArray(), API resources, logs, Telescope, and queue payloads for free.

Key questions.

  • Who owns the two keys, in which secret manager, on what rotation cadence, and what is the recovery story for a backup restored months after the old key was retired?
  • What fraction of reads/writes to the target columns currently bypass Eloquent (raw SQL, DB::table(), upsert, seeders, importers, replicas)? This is the gating question.
  • Does any product requirement need prefix, fuzzy, range, or sorted search on these fields? The package supports exact-match only, via the blind-index hash column.
  • Does our compliance regime accept application-layer field encryption with a deterministic blind index (equality leakage on low-entropy fields like phone and national ID)?
  • What sensitivity tier are we targeting? This is defensible for internal/moderate PII; it is not a substitute for column-level encryption or a KMS-backed design at the highest tier without an independent review or an internal fork.
  • Who reviews every patch diff before merge, and what is the exit plan if the maintainer stops shipping?

Position. Adopt-with-conditions for a bounded, lower-blast-radius PII use case behind an internal adapter, pinned to an exact version. Do not adopt as the sole control for the most sensitive data tier until the fork/pin policy and rotation runbook are demonstrated in a rehearsal.

Integration Approach

Stack fit. Laravel-native by construction: framework encrypter, standard config publishing, an artisan rotation command, and typed exceptions. Should compose cleanly with existing $casts arrays. Validation is required against our specific Laravel version, PHP version, and database matrix — MySQL strict mode, PostgreSQL TEXT defaults, and SQLite (used in tests) all behave differently enough to warrant a test pass before any real data lands.

Migration path (per field, incremental).

  1. Widen the column to TEXT (ciphertext is base64 of an AEAD payload, realistically 2–4× plaintext).
  2. Add the hash column in the same migration — fixed length (typically 64 hex), indexed, and UNIQUE where the field is logically unique. A UNIQUE constraint on the encrypted column itself is worthless because GCM output differs per row.
  3. Deploy code that can read both plaintext and ciphertext.
  4. Backfill encryption through models in chunked artisan commands; never as one transaction.
  5. Backfill hashes in a second chunked pass — no SQL expression reproduces the package's hash, so this must read the decrypted attribute per row.
  6. Verify, then retire the plaintext path.

Key rotation is a separate, rehearsed procedure: deploy code that can decrypt both key versions to every runtime (web, queue, scheduler, replicas) → run secure-fields:rotate throttled off-peak in batches → investigate any non-zero unreadable count before continuing → retire the old key only after confirming restored backups no longer depend on it.

Compatibility work items. Decide once between the model property and the cast and standardize — mixing them destroys grep-ability, which is exactly what audits and incident response rely on. Write a one-off test asserting whether getOriginal() and isDirty() yield plaintext or ciphertext in our installed version before any observer or audit-trail work. Confirm nested JSON behavior if any secured column is also cast to array.

Sequencing.

  • Phase 0 — Spike (1 week). One low-risk field with an existing lookup need (users.phone or an internal external-ref). Prove both directions and one dry-run rotation in a non-production environment.
  • Phase 1 — Seam and pin. Introduce App\Support\SecureSearch as the single call site for hashing and lookups. Pin the exact version in composer.json. Grep for ->update( and DB:: against secured tables and record the offenders.
  • Phase 2 — Guardrails. PR template rules: no firstOrCreate/updateOrCreate on secure attributes; no bulk updates on secured tables; every secured field hidden by default. Negative test per JSON endpoint asserting plaintext is absent from the response body.
  • Phase 3 — Rollout. Field-by-field, ordered by blast radius ascending. Each field ships with the three-way storage assertion (attribute readable, raw column ciphertext, hash present and correct).
  • Phase 4 — Operate. Rotation runbook written and rehearsed off-peak; patch diff review in CI; backup-restore-under-old-key drill scheduled.

Operational Impact

Maintenance. Small surface area, disproportionate operational footprint. Ongoing obligations: two secrets managed per environment, config:clear discipline after every env change (the most common false "missing key" report), exact-version pinning, and manual review of every patch diff — v2.0.1 is direct evidence that a patch bump can alter output text that health checks or scripts may parse. Assert on exception classes and process exit codes, never message strings. The internal adapter seam keeps exit cost to one file if we fork or migrate to native encrypted casts.

Support. There is no vendor. Escalation is reading SecureFieldsException and the source. That means we must name an internal DRI and fund the tribal knowledge: the five-question debug checklist (was the row written through Eloquent? is the column wide enough? is the encrypting key version still configured? is config:cache stale? is this a raw query, replica, or DB::table() call?) answers nearly every reported symptom.

Scaling. Per-write encryption cost is trivial at normal volumes but measurable inside bulk import loops — those should be chunked and throttled rather than run in a single job. The real scaling bottleneck is rotation: batched, throttled, non-transactional, with an unreadable counter that must be read as a signal rather than a nuisance. Blind-index lookups scale like any indexed column and are fine. Analytics do not scale at all — no LIKE, ORDER BY, SUM, AVG, joins, or whereBetween on encrypted columns. Any reporting requirement must be met by exporting decrypted data to a purpose-built store with its own access controls, and that store becomes a new, larger scope item.

Failure modes, ranked by expected cost.

  1. Silent plaintext via bypassed write paths — mass updates, DB::table(), upsert, seeders, importers. Compliance break with no error and no detection until audit.
  2. Duplicate records from firstOrCreate/updateOrCreate on a secure attribute — the lookup clause compares plaintext to ciphertext, never matches, creates a row every call. Fails silently.
  3. Column truncation on an under-sized VARCHAR — discovered months later as unreadable rows mid-rotation.
  4. Plaintext egress through API resources, logs, queue payloads, and exception traces — the most likely actual breach vector, and the least related to the encryption itself.
  5. Key availability incidents — missing or stale-cached config in a new environment, or a retired key still needed by a restored backup or stale replica.
  6. Upstream abandonment or an unreviewed patch altering behavior or output contracts.

Ramp-up. Happy path is a day: install, generate keys, secure one field, verify. Genuine operational safety is one to two weeks for a single field plus one rotation rehearsal, and it never fully stops being a code-review tax on every change touching a secured table. Gate production rollout on four artifacts: a written rotation runbook, a named key owner, the per-endpoint negative test for plaintext leakage, and the three-way storage assertion on every secured field.

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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums