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

Crypto Laravel Package

yab/crypto

yab/crypto is a Laravel/PHP package that provides cryptographic helpers for encrypting, decrypting, hashing, and verifying data. It offers a simple, configurable API that integrates with Laravel's service container, making secure crypto operations easier to use across applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Decide first whether you are installing or reading. This package is archived, has 10 stars and zero dependents. In practice there are only two legitimate starting points: (a) a short spike where you composer require it into a throwaway app to read the API shape, or (b) a deliberate MIT fork you own and pin. Do not start by adding it to a production composer.json.

1. Run the compatibility spike before anything else

composer require yab/crypto --dry-run

Open the package's composer.json and read the php and illuminate/* constraints. This repo predates modern Laravel; on Laravel 9+/PHP 8.x you will likely hit a resolution failure or need --ignore-platform-reqs. That result is the assessment — if it does not resolve cleanly, stop. Also grep the source for mcrypt before you go further; anything still touching it is a hard break on PHP 7.2+.

2. Register the provider

Laravel 5.5+ auto-discovers the service provider via extra.laravel.providers. Lumen needs the manual registration:

// bootstrap/app.php
$app->register(Yab\Crypto\CryptoServiceProvider::class);

if (!class_exists('Crypto')) {
    class_alias(Yab\Crypto\Facades\Crypto::class, 'Crypto');
}

3. Publish and actually read the config

php artisan vendor:publish --provider="Yab\Crypto\CryptoServiceProvider"

Every value in that file is a security decision: key source, cipher/mode, IV handling, output encoding. Read it line by line before you write a single test. If the key defaults to something derived from APP_KEY or an env var without a fallback guard, note that as a failure mode.

4. Where to look first, in order

  • README.md — for an archived package this is the only reliable API reference.
  • src/ — locate the facade and the class it proxies to. That class is the real contract; everything else is glue.
  • tests/ — fast track to intended usage and, more usefully, to the Laravel version it was authored against.
  • config/ — key and cipher settings.
  • composer.json — constraints and whether the construction is AEAD or CBC-with-HMAC.

5. First use case

The realistic day-one goal for any crypto helper is "encrypt a column on write, decrypt on read" — API tokens, OAuth refresh secrets, PII fields. Prototype it against a throwaway column before touching a migration:

$crypto = app(\Yab\Crypto\Crypto::class);

$stored = $crypto->encrypt('plain-value');
$plain  = $crypto->decrypt($stored);

// Inspect what you actually persisted:
var_dump(strlen($stored), mb_check_encoding($stored, 'UTF-8'));

Confirm three things before going further: the ciphertext is a string (base64/hex) and not raw binary, the round trip is stable across processes, and the length is bounded. A binary payload will silently corrupt in a varchar column and destroy your ability to decrypt.

Then step back and ask the question the spike exists to answer: does this do anything Crypt::encryptString() and sodium_* don't already do? For almost every team the answer is no, and the correct first use case becomes "borrow the facade ergonomics, write the helpers yourself."

Implementation Patterns

Wrap it behind an application-owned interface. This is the single most important pattern when the dependency is unmaintained. Every call site talks to your boundary, not to the package:

interface SecretBox
{
    public function seal(string $plain): string;   // returns a storable string
    public function open(string $payload): string; // throws on tamper
}

final class YabSecretBox implements SecretBox
{
    public function __construct(private \Yab\Crypto\Crypto $crypto) {}

    public function seal(string $plain): string
    {
        return base64_encode($this->crypto->encrypt($plain));
    }

    public function open(string $payload): string
    {
        return $this->crypto->decrypt(base64_decode($payload, true));
    }
}

Bind it in your AppServiceProvider and inject SecretBox — never the concrete class. When you replace the package (and you will), the swap is one binding.

Cast it at the model layer. Keep encryption out of controllers. An Eloquent cast or a mutator keeps the plaintext out of serialization, logs, and toArray() output by default:

protected $casts = [
    'api_secret' => EncryptedString::class, // wraps SecretBox
];

Do not reach for $appends on encrypted attributes; that reintroduces the plaintext into API responses that previously never contained it.

Envelope the key, don't hardcode it. If the package takes a single static key, you can still layer envelope encryption on top: generate a per-record data key with sodium_crypto_secretbox_keygen(), encrypt the record with it, encrypt the data key with your KMS/master key, and store both. The package becomes a leaf detail rather than your entire key architecture. This is the pattern that survives rotation.

Key IDs from day one. Prefix or column every ciphertext with a key identifier even if the package doesn't support it:

$payload = 'v1:' . $this->crypto->encrypt($plain);

On decrypt, branch on the prefix. This costs nothing now and saves you from a full-table re-encryption sweep later. Without it, rotation is a stop-the-world migration.

Design the dual-read path before you need it. If legacy rows exist, decrypt-with-old / re-encrypt-with-new on read, then backfill:

public function open(string $payload): string
{
    return str_starts_with($payload, 'v2:')
        ? $this->openV2($payload)
        : $this->reencrypt($this->openLegacy($payload));
}

Track the backfill to zero before removing the legacy branch. Never assume "we'll migrate it later."

Use it (or its pattern) for webhook HMAC verification. A Crypto::hash(...) or signing helper maps cleanly onto verifying inbound webhooks. But verify the construction: use hash_hmac('sha256', $raw, $secret) with hash_equals(), comparing against the raw request body — not the parsed JSON. If the package's sign helper is not hash_equals-based, it is timing-attack exposed and you should not use it.

Testing pattern. Never assert against a hardcoded ciphertext — it will differ across machines and OpenSSL builds. Assert the round trip and the failure mode:

$this->assertSame($plain, $box->open($box->seal($plain)));
$this->expectException(\Throwable::class);
$box->open(tamper($box->seal($plain)));

Also add a test that decrypts a fixture produced by the current version, so a future PHP/OpenSSL upgrade that breaks portability fails in CI rather than in front of a customer.

When the capability gap is real, reimplement rather than depend. If you need asymmetric primitives or a KMS abstraction, phpseclib/phpseclib and sodium_* give you the same surface with a maintainer. Copy the facade ergonomics (Crypto::encrypt/decrypt/hash/sign) from this package; do not copy the dependency.

Gotchas and Tips

Archived means no CVE response. There is no advisory channel. Any future vulnerability in the construction or a transitive dependency goes unpatched while your composer.lock stays pinned. If you fork, add a named owner and a security review cadence — an unowned crypto fork is a liability, not an asset.

Verify the construction, not just the round trip. Silent failure is the default failure mode in crypto. Before trusting the output, answer: which random source (random_bytes/sodium_* only), is it AEAD or encrypt-then-MAC, how are nonces/IVs generated and guaranteed unique, and is there any hand-rolled KDF. CBC without a MAC on attacker-supplied ciphertext is a padding oracle — undetectable in functional tests, catastrophic in effect.

The facade is a footgun in long-running processes. Facades resolve through the container on each static call, which is fine, but any state held on the resolved singleton (cached key, cached IV) will be shared across queue workers. Inject the service rather than calling Crypto:: statically inside jobs, and never let a helper retain an IV between calls.

config:cache will break a bad config. If any value in the published config calls env() directly outside the config file, it returns null after php artisan config:cache. The failure is usually a confusing decrypt error, not a clean exception. Ensure the key is read through config() only.

Ciphertext is not searchable, sortable, or unique. Once a column is encrypted, WHERE api_secret = ?, ORDER BY, and unique indexes stop working. If you need lookup, store a blind index: hash_hmac('sha256', $normalized, $indexKey) in a separate indexed column, with the index key distinct from the encryption key.

Model size and column type. Confirm the encoding before writing the migration. Base64 ciphertext is ~1.4× the plaintext plus IV/tag overhead; a varchar(255) will overflow on larger JSON payloads. Use text for anything variable and never char with a fixed length.

Queue and session serialization. If an encrypted value is embedded in a serialized job payload or session, a key change invalidates every in-flight job and every live session. Version the payload and accept a flush on rotation, or keep short-lived encrypted data in a store you can drain.

Do not put keys in the published config or a committed .env. vendor:publish drops a config file into version control in many repos. Keys belong in the environment or a secrets manager; the config file should only reference them.

Extension points are your escape hatch. The service provider and facade binding points are the only parts worth preserving from a fork. Keep them thin: register your SecretBox implementation and let everything else be a swappable detail. That way the package's API shape survives even after its code doesn't.

Read the low score as the answer. A 0.3 health score with 10 stars and zero dependents is not a hidden gem; it is a tidy, unmaintained wrapper around primitives Laravel already ships. Spend the day on the spike, extract the ergonomics, and build on Illuminate\Encryption\Encrypter and sodium_* with key rotation designed in from the start.

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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource