Encrypted JSON
On this page
Crumbls\Sealcraft\Casts\EncryptedJson encrypts string leaves inside a JSON column while preserving the tree shape. Useful when admin tools, analytics, or schema validators need to inspect keys and structure, but the values themselves are sensitive.
use Crumbls\Sealcraft\Casts\Encrypted;
use Crumbls\Sealcraft\Casts\EncryptedJson;
use Crumbls\Sealcraft\Concerns\HasEncryptedAttributes;
class Patient extends Model
{
use HasEncryptedAttributes;
protected $casts = [
'ssn' => Encrypted::class,
'history' => EncryptedJson::class,
];
}
$patient->history = [
'conditions' => ['asthma', 'hypertension'],
'allergies' => [
['substance' => 'penicillin', 'severity' => 'severe'],
],
'notes' => 'no recent flares',
];
What gets encrypted
On disk the column is still valid JSON. Every non-empty string leaf is individually encrypted under the same DEK as the row's scalar Encrypted columns. Keys, nesting, empty strings, and non-string scalars (ints, floats, bools, nulls) stay readable.
On read, string leaves without a Sealcraft cipher prefix raise DecryptionFailedException by default. During a controlled migration of legacy JSON, set SEALCRAFT_JSON_ALLOW_LEGACY_PLAINTEXT=true temporarily to permit those strings, then rewrite the rows through the cast and turn the setting off.
Keys, empty strings, and non-string scalars remain visible and are not authenticated by this cast. If the entire document needs confidentiality or integrity, serialize it and use the scalar Encrypted cast instead.
When to use it
- You need PHI values protected but your tooling queries the JSON by key (Postgres
->>, MySQLJSON_EXTRACT) - You have a legacy column that already contains JSON and you want to opt individual values into encryption without restructuring the column
- You accept that JSON keys and non-string scalar values remain visible in the database
When not to use it
- The entire column is sensitive -- use scalar
Encryptedand serialize the whole JSON document yourself; it is simpler, hides keys and non-string values, and has less overhead - You need to query on the encrypted values -- the plaintext is never in the column, so indexes on leaf values see only ciphertext
- You need leaf-level crypto-shred --
EncryptedJsonuses the same DEK for every encrypted leaf in the context