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

Encryptor Laravel Package

nmure/encryptor

nmure/encryptor is a lightweight Laravel/PHP package for encrypting and decrypting sensitive data through a simple API, helping you secure strings and application values without adding complexity to your codebase.

View on GitHub
Deep Wiki
Context7

Getting Started

nmure/encryptor is a thin, framework-agnostic wrapper around PHP's openssl_encrypt/openssl_decrypt. There is no Laravel service provider, facade, or config file shipped — you wire it up yourself. Treat it as a small utility class, not a framework package.

1. Install

composer require nmure/encryptor

Check the resolved version — the last tagged release is from December 2017, so you will almost certainly be pulling 1.x from a frozen history.

2. Read these two things first

  • README.md in the repo root — the only API documentation that exists.
  • src/Encryptor.php (and anything it composes, e.g. a config/options object). The class is small; read all of it before trusting it. Pay attention to:
    • the constructor signature (key, cipher, and whether an IV is passed in or generated),
    • whether output is raw binary or base64,
    • what encrypt()/decrypt() return on failure (false vs. an exception).

3. First use case — encrypt one column, decrypt on read

The realistic day-one use is protecting a single sensitive value at rest (an OAuth refresh token, a webhook signing secret, a customer's tax ID) rather than encrypting an entire model.

use Nmure\Encryptor\Encryptor;

$encryptor = new Encryptor(
    config('app.encryption_key'), // 32 raw bytes or a base64 key, depending on the class
    'aes-256-cbc'
);

$ciphertext = $encryptor->encrypt($plaintext);   // store this
$plaintext  = $encryptor->decrypt($ciphertext);  // read it back

Verify the exact constructor/method names against the source before committing — this package predates the current PHP type-declaration conventions and the README may lag the code.

4. Prove the round-trip before you build on it

Write a throwaway test that encrypts a known string, decrypts it, and asserts equality. Do this before writing any migration or model code. Because the package is unmaintained and has zero dependents, you cannot assume anyone else has exercised the edge cases you are about to rely on.

Implementation Patterns

Bind it once in the container. Do not new Encryptor(...) in controllers. Register a singleton in a service provider so the key/cipher configuration lives in one place, and so you can swap the implementation later without touching call sites.

// AppServiceProvider::register()
$this->app->singleton(Encryptor::class, function ($app) {
    return new Encryptor(
        base64_decode($app['config']['app.data_key']),
        $app['config']['app.data_cipher'] ?? 'aes-256-cbc'
    );
});

Wrap it behind your own interface. The single highest-value pattern with an abandoned dependency is an anti-corruption layer:

interface DataEncryptor
{
    public function encrypt(string $plaintext): string;
    public function decrypt(string $ciphertext): string;
}

Bind NmureEncryptorAdapter to it now. When you migrate to Illuminate\Encryption\Encrypter or a maintained alternative, you change one binding instead of grepping the codebase.

Eloquent attribute casting / accessors. The clean integration point is an accessor + mutator pair (or a custom cast) so encryption is invisible to the rest of the app:

public function setApiSecretAttribute(?string $value): void
{
    $this->attributes['api_secret'] = $value === null
        ? null
        : app(DataEncryptor::class)->encrypt($value);
}

public function getApiSecretAttribute(?string $value): ?string
{
    return $value === null
        ? null
        : app(DataEncryptor::class)->decrypt($value);
}

Two consequences to plan for: the column is no longer searchable, sortable, or comparable in SQL, and you must cast the column to text/longText/blob — not varchar(255).

Encrypting payloads at the boundary. A second legitimate pattern: sign/encrypt a payload passed to a partner or queued job so it cannot be read from the queue table or logs. Encrypt JSON, base64 the result, and you have a transport-safe token.

Key rotation with a version prefix. Since the format is your own choice, store a scheme tag in front of the ciphertext:

v1:<base64 ciphertext>

On decrypt, read the tag and dispatch to the matching key/cipher. Build a re-encryption command (php artisan secrets:reencrypt) that decrypts with the old key and writes with the new one, run before you retire the old key. Do this on day one — retrofitting it after you have data is painful.

Testing. Because encryption uses a random IV, ciphertext is non-deterministic. Assert decrypt(encrypt($x)) === $x, never encrypt($x) === $expected. Use a fixed, obviously-fake key in phpunit.xml, and add a test that decrypting tampered ciphertext fails rather than silently returning garbage.

Gotchas and Tips

It is unmaintained — budget for that. Last release 2017-12-02, 7 stars, 0 dependents, no security advisories pipeline. Pin the exact version in composer.json (no ^), commit composer.lock, and keep the wrapper interface above so a replacement is a one-file change. If the code does anything you would not accept, vendor the single class into app/Support/ rather than depending on the remote package.

Key length must match the cipher exactly. AES-256 needs 32 bytes, AES-128 needs 16. A passphrase shorter or longer than that will fail or be silently zero-padded depending on how the class calls OpenSSL. Store a raw binary key from base64_encode(random_bytes(32)) in an env var, and decode it once at the container binding — never pass the base64 string straight into the constructor.

CBC has no authentication. If the package defaults to aes-*-cbc without appending an HMAC, your ciphertext is malleable: an attacker who can write to the column can flip bits in the plaintext. Check the source for an HMAC; if there isn't one, add encrypt-then-MAC yourself, or pick aes-256-gcm if openssl_get_cipher_methods() lists it (it will, on any modern PHP/OpenSSL). GCM also gives you the tag parameter — confirm the class actually handles it, since a 2017-era wrapper may not.

IV handling is the sharpest edge. Reusing an IV with CBC or GCM is catastrophic. Verify in the source that the IV is generated per encryption with random_bytes, and that it is prepended to (or stored alongside) the ciphertext. If the class takes an IV in the constructor and reuses it, stop and use it only as a wrapper you control, or replace it.

openssl_* returns false on failure. Many wrappers propagate that false upward. Always guard:

$plain = app(DataEncryptor::class)->decrypt($cipher);
if ($plain === false) {
    // wrong key, truncated column, or tampered data — log and fail closed
}

This is the most common production symptom: a wrong or rotated key makes decryption return false, the accessor returns false, and it gets written to a JSON response as an empty property. Fail loudly instead.

Column and transport encoding. If the class returns raw binary, do not put it in a varchar with a UTF-8 charset — you will get mojibake and silent truncation. Either use a text/blob column, or base64-encode before persisting. Base64 also makes the value safe for JSON, URLs, and cookies; raw bytes are not.

Watch for double encryption. Because there is no framework integration, there is no "already encrypted" guard. Migrating existing data through a mutator will encrypt plaintext correctly but re-encrypt anything you read and save back. Write the migration to read raw attributes ($model->getRawOriginal('api_secret')), not the decrypted accessor.

Debugging tools. When it "just doesn't work": openssl_error_string() right after the failing call, openssl_get_cipher_methods() to confirm the cipher name spelling (aes-256-cbc, lowercase, hyphens), and bin2hex() on both key and IV to confirm the lengths are what you think. Nine times out of ten it is a key length mismatch or an IV that was not persisted with the ciphertext.

Extension points are minimal by design. There is no driver, no event system, no config. Your extension points are (a) the wrapper interface you write, (b) a decorator that adds the version prefix and key rotation, and (c) a custom cast for Eloquent. Plan for these three and the package's limitations stop mattering.

Have an exit strategy. Laravel already ships Illuminate\Encryption\Encrypter (AES-256-CBC with HMAC by default, GCM optional), maintained, with Crypt::encryptString(). Write your DataEncryptor interface now with that as the intended target, and treat nmure/encryptor as an implementation detail you may retire.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin