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 File Encryption Laravel Package

ercsctt/laravel-file-encryption

ercsctt/laravel-file-encryption is a Laravel package for securely encrypting and decrypting files. It offers a simple API for protecting uploaded or stored files, helping you add file-level encryption to storage workflows and keep sensitive data safe at rest.

Deep Wiki
Context7

Getting Started

Install and publish

composer require ercsctt/laravel-file-encryption
php artisan vendor:publish --tag=file-encryption-config

Provide a key. This package encrypts files with its own symmetric key rather than piggybacking on APP_KEY (check config/file-encryption.php for the exact env var, typically FILE_ENCRYPTION_KEY). Generate it once and commit it to your secret manager, not to .env.example:

php artisan key:generate  # only for APP_KEY
# then generate a 32-byte key for the package, e.g.:
php -r "echo base64_encode(random_bytes(32));"

Where to look first. Open the published config and the facade class (usually Ercsctt\FileEncryption\Facades\FileEncryption or similar). The config will tell you which cipher, key, and default disk are wired up. Then read the package's own tests — for a small package like this the tests are the most accurate documentation of the public API surface.

First real use case: encrypting user-uploaded documents at rest before they land on S3 or local disk, and decrypting them on download. A minimal end-to-end loop:

use Ercsctt\FileEncryption\Facades\FileEncryption;

// store
$encrypted = FileEncryption::encrypt($request->file('document')->get());
Storage::disk('private')->put("docs/{$id}.enc", $encrypted);

// retrieve
$plain = FileEncryption::decrypt(Storage::disk('private')->get("docs/{$id}.enc"));
return response($plain)->header('Content-Type', 'application/pdf');

If that round-trip works, you have the core of the package in hand.

Implementation Patterns

Encrypt-on-store, decrypt-on-read via a service class. Wrap the facade in an application service so controllers and jobs don't reach for the facade directly. This is the single highest-value pattern for this kind of package — it isolates the encryption key surface and lets you swap ciphers or add audit logging later.

class SecureDocumentStore
{
    public function __construct(private string $disk = 'private') {}

    public function put(string $path, string $plain): void
    {
        Storage::disk($this->disk)->put($path, FileEncryption::encrypt($plain));
    }

    public function get(string $path): string
    {
        return FileEncryption::decrypt(Storage::disk($this->disk)->get($path));
    }
}

Streaming for anything larger than a few MB. Most Laravel file-encryption packages expose a chunked or stream-based API because loading a 500 MB PDF into a PHP string blows memory_limit. Look for encryptStream / decryptStream or a Stream wrapper in the source. If it exists, prefer it for uploads over the simple encrypt() in every path where file size is user-controlled.

Custom filesystem disk / driver. The cleanest integration for this package family is a custom disk that transparently encrypts on write and decrypts on read, so existing Storage::disk('encrypted')->put(...) calls just work. If the package ships an EncryptedFilesystemAdapter (check the src/ tree), wire it in config/filesystems.php:

'disks' => [
    'encrypted' => [
        'driver' => 'encrypted-local',
        'root'   => storage_path('app/encrypted'),
    ],
],

That's a one-line change at the call site and a huge win for codebases already using Storage.

Download controller pattern. Keep the decryption in the controller layer and stream out, rather than decrypting to a temp file:

return response()->streamDownload(function () use ($doc) {
    echo FileEncryption::decrypt(
        Storage::disk('private')->get($doc->path)
    );
}, $doc->original_name);

Batch re-encryption via a queued job. Key rotations happen. Model a job that reads ciphertext, decrypts with the old key, re-encrypts with the new, and writes back — chunked, idempotent, and rate-limited. Never do this in a request.

Eloquent accessor for small blobs. For tiny columns (a stored API token file, a signed config blob), an accessor/cast that decrypts on read and encrypts on write keeps the encryption transparent to the rest of the app. Do not use this for actual files; use it for small text fields.

Gotchas and Tips

Key loss = data loss. There is no recovery path beyond brute force. Back up the encryption key wherever you back up the database, and treat rotation as a planned migration (decrypt-with-old → encrypt-with-new), not a config change.

Base64 vs raw bytes. Many implementations return base64-encoded strings by default. If you write the result to a binary file, you'll bloat it by ~33% and can't tell ciphertext from garbage on disk. Check whether encrypt() returns a string or bytes and whether there's a raw flag; store binary ciphertext and only base64 for transport.

Ciphertext size ≠ plaintext size. Don't validate uploaded file size against the stored filesize() after encryption — IVs, padding, and headers all change the length. Validate on upload before encrypting.

Mime detection breaks after encryption. Storage::mimeType() on an encrypted file returns application/octet-stream or worse. Keep original mime/size in the database row; don't try to sniff the ciphertext.

Config caching. The package reads its config at bootstrap. In production, php artisan config:cache will freeze FILE_ENCRYPTION_KEY at cache time — if you change the key without re-caching, the app keeps using the old one. Always config:clear && config:cache after key changes.

Storage::fake in tests. If you test with Storage::fake('private'), you still need the real encryption key loaded. Set it in phpunit.xml as a fixed test key so tests are deterministic and don't depend on a generated key.

Deterministic vs random IVs. If the package uses random IVs (it should), encrypting the same file twice gives different ciphertext. Don't expect deduplication on encrypted files, and don't cache by ciphertext hash — cache by the original content hash.

Debugging without decrypting. When a decrypt fails, check: (1) is the key the same one that encrypted it, (2) is the ciphertext truncated (common when a text column silently caps at 64 KB), (3) did the file get re-saved with a charset conversion somewhere. A small php artisan tinker script that pulls one file and calls decrypt with try/catch is usually faster than reading the whole stack trace.

Extension points. The package's value is in the cipher/key abstraction. If you need custom key resolution (AWS KMS, Vault), look for a KeyProvider interface or a binding in a service provider before forking — most packages in this niche bind the key resolver in a singleton, which is overridable from your AppServiceProvider's register() method.

Don't encrypt the whole storage/ directory. Laravel's framework caches (compiled views, sessions, cache) live there. Encrypt the specific disks or paths you own, not the framework's scratch space, or you'll get opaque failures in unrelated packages.

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo