laravelps maintained by payloadshield
Description
Pluggable Laravel middleware for encrypting/decrypting request and response payloads.
Author
Last update
2026/09/28 19:24
(dev-main)
License
Downloads
1
Tags
LaravelPS
Pluggable Laravel middleware for encrypting/decrypting request and response payloads — the PHP equivalent of FastAPIPS.
Key Features
- Pluggable encryption: base64, Fernet, AES-GCM-256, ChaCha20-Poly1305,
Hybrid RSA+AES, ECDH+AES-GCM, ECIES, and HPKE (RFC 9180) ship out of the
box; register your own with
Crypto::registerHandler(...). - One-time key configuration: set keys in
config/payloadshield.phpor.env— they initialize automatically via the service provider. - Route-agnostic: no changes needed to your controller logic besides adding a middleware.
- Three middleware modes:
encrypt(response only),decrypt(request only),crypt(both).
Installation
composer require payloadshield/laravelps
Publish the config file:
php artisan vendor:publish --tag=payloadshield-config
Configuration
Add the keys you need to your .env:
# Default handler (used when no handler name is passed to middleware)
PAYLOADSHIELD_DEFAULT=aes-gcm-256
# Symmetric key — used by fernet, aes-gcm-256, chacha20-poly1305
PAYLOADSHIELD_KEY=your-32-byte-symmetric-key......
# RSA keys — used by rsa-hybrid (file path or raw PEM)
PAYLOADSHIELD_PRIVATE_KEY=/path/to/rsa_private.pem
PAYLOADSHIELD_PUBLIC_KEY=/path/to/rsa_public.pem
# EC P-256 keys — used by ecdh-aes-gcm, ecies (file path or raw PEM)
PAYLOADSHIELD_EC_PRIVATE_KEY=/path/to/ec_private.pem
PAYLOADSHIELD_EC_PUBLIC_KEY=/path/to/ec_public.pem
# X25519 keys — used by hpke (file path or raw PEM)
PAYLOADSHIELD_HPKE_PRIVATE_KEY=/path/to/x25519_private.pem
PAYLOADSHIELD_HPKE_PUBLIC_KEY=/path/to/x25519_public.pem
Only set the keys required by the handlers you actually use.
Or configure directly in config/payloadshield.php:
return [
'default' => 'aes-gcm-256',
'key' => env('PAYLOADSHIELD_KEY'),
'private_key' => env('PAYLOADSHIELD_PRIVATE_KEY'),
'public_key' => env('PAYLOADSHIELD_PUBLIC_KEY'),
'ec_private_key' => env('PAYLOADSHIELD_EC_PRIVATE_KEY'),
'ec_public_key' => env('PAYLOADSHIELD_EC_PUBLIC_KEY'),
'hpke_private_key' => env('PAYLOADSHIELD_HPKE_PRIVATE_KEY'),
'hpke_public_key' => env('PAYLOADSHIELD_HPKE_PUBLIC_KEY'),
];
Quick Start
Using the static PayloadShield helper (recommended)
use PayloadShield\LaravelPS\PayloadShield;
// Encrypt response only
Route::get('/api/data', function () {
return response()->json(['message' => 'Hello, PayloadShield!']);
})->middleware(PayloadShield::encrypt('aes-gcm-256'));
// Decrypt request only
Route::post('/api/process', function (Request $request) {
return response()->json(['received' => $request->all()]);
})->middleware(PayloadShield::decrypt('aes-gcm-256'));
// Decrypt request + encrypt response
Route::post('/api/secure', function (Request $request) {
return response()->json(['echo' => $request->all()]);
})->middleware(PayloadShield::crypt('aes-gcm-256'));
Using middleware aliases
The middleware aliases are registered automatically:
// In routes/api.php
Route::get('/api/data', [DataController::class, 'index'])
->middleware('payloadshield.encrypt:aes-gcm-256');
Route::post('/api/process', [DataController::class, 'store'])
->middleware('payloadshield.decrypt:aes-gcm-256');
Route::post('/api/secure', [DataController::class, 'update'])
->middleware('payloadshield.crypt:aes-gcm-256');
Applying to route groups
Route::middleware(PayloadShield::crypt('aes-gcm-256'))->group(function () {
Route::post('/api/users', [UserController::class, 'store']);
Route::post('/api/orders', [OrderController::class, 'store']);
});
Middleware Reference
payloadshield.encrypt / PayloadShield::encrypt($type)
Encrypts the response payload only.
- Input: Normal request
- Output:
{"encrypted": "<encoded-data>"}
payloadshield.decrypt / PayloadShield::decrypt($type)
Decrypts the incoming request payload only.
- Input:
{"encrypted": "<encoded-data>"} - Output: Normal response (controller receives decrypted data via
$request->all())
payloadshield.crypt / PayloadShield::crypt($type)
Decrypts the request AND encrypts the response.
- Input:
{"encrypted": "<encoded-data>"} - Output:
{"encrypted": "<encoded-data>"}
Supported Encryption Handlers
| Handler Name | Algorithm | Key Config Required |
|---|---|---|
base64 |
Base64 encoding (obfuscation only) | None |
fernet |
Fernet (AES-128-CBC + HMAC-SHA256) | key |
aes-gcm-256 |
AES-256-GCM | key |
chacha20-poly1305 |
ChaCha20-Poly1305 | key |
rsa-hybrid |
RSA-OAEP-SHA256 + AES-256-GCM | public_key / private_key |
ecdh-aes-gcm |
Ephemeral ECDH (P-256) + HKDF-SHA256 + AES-256-GCM | ec_public_key / ec_private_key |
ecies |
ECIES (P-256, HKDF, AES-256-CTR, HMAC-SHA256) | ec_public_key / ec_private_key |
hpke |
RFC 9180 HPKE (X25519 + ChaCha20-Poly1305) | hpke_public_key / hpke_private_key |
Using Crypto Directly
You can also use the Crypto facade directly in your code (bypassing middleware):
use PayloadShield\ComPHPPS\Crypto;
// Encrypt
$encoded = Crypto::encode('aes-gcm-256', ['user' => 'alice', 'role' => 'admin']);
// Decrypt
$decoded = Crypto::decode('aes-gcm-256', $encoded);
// => ['user' => 'alice', 'role' => 'admin']
Custom Handlers
Implement EncryptionHandlerInterface from ComPHPPS and register it:
use PayloadShield\ComPHPPS\Crypto;
use PayloadShield\ComPHPPS\EncryptionHandlerInterface;
use PayloadShield\LaravelPS\PayloadShield;
class MyCustomHandler implements EncryptionHandlerInterface
{
public function encode(mixed $data, array $config = []): string
{
// your encryption logic
}
public function decode(string $encodedData, array $config = []): mixed
{
// your decryption logic
}
}
// Register in a service provider's boot() method
Crypto::registerHandler('my-custom', new MyCustomHandler());
// Then use in routes
Route::post('/api/custom', [Controller::class, 'action'])
->middleware(PayloadShield::crypt('my-custom'));
Error Handling
| Situation | Behavior |
|---|---|
| Request decryption fails | 400 JSON response: {"error": "Failed to decrypt request: ..."} |
| Unknown handler name | RuntimeException: Encryption handler '<name>' not found. Available handlers: ... |
| Missing required key | RuntimeException: ... requires 'Key' to be set via PayloadShieldEnc::init(...) |
FastAPIPS Comparison
This package is the Laravel equivalent of FastAPIPS:
| FastAPIPS (Python) | LaravelPS (PHP) |
|---|---|
@PayloadShield.encrypt("aes-gcm-256") |
->middleware(PayloadShield::encrypt('aes-gcm-256')) |
@PayloadShield.decrypt("aes-gcm-256") |
->middleware(PayloadShield::decrypt('aes-gcm-256')) |
@PayloadShield.crypt("aes-gcm-256") |
->middleware(PayloadShield::crypt('aes-gcm-256')) |
PayloadShieldEnc.init({...}) |
config/payloadshield.php + .env |
compyps dependency |
payloadshield/comphpps dependency |
Cross-language encrypt/decrypt is fully supported:
# Python (FastAPIPS) — encrypt
from fastapi_payloadshield import PayloadShieldEnc, get_handler
PayloadShieldEnc.init({"Key": "your-32-byte-symmetric-key......"})
encoded = get_handler("aes-gcm-256").encode({"user": "alice"}, PayloadShieldEnc.get_config())
// PHP (LaravelPS) — decrypt
// Config in .env: PAYLOADSHIELD_KEY=your-32-byte-symmetric-key......
$decoded = Crypto::decode('aes-gcm-256', $encoded);
// => ['user' => 'alice']
Project Layout
├── config/
│ └── payloadshield.php # Publishable Laravel config
├── src/
│ ├── PayloadShieldServiceProvider.php # Auto-discovered service provider
│ ├── PayloadShield.php # Static helper (encrypt/decrypt/crypt)
│ ├── Facades/
│ │ └── PayloadShield.php # Laravel Facade
│ └── Middleware/
│ ├── EncryptResponse.php # Encrypts response only
│ ├── DecryptRequest.php # Decrypts request only
│ └── CryptPayload.php # Decrypts request + encrypts response
├── tests/
├── composer.json
├── LICENSE
└── README.md
Requirements
- PHP >= 8.1
- Laravel 10, 11, or 12
ext-openssl— required for all handlers exceptbase64ext-json— required for payload serializationext-sodium— required only for thehpkehandler