Bleichenbacher Attack in Laravel
How Bleichenbacher Attack Manifests in Laravel
The Bleichenbacher attack exploits RSA padding oracle vulnerabilities, specifically targeting PKCS#1 v1.5 padding. In Laravel applications, this manifests through several common patterns that create timing side channels or error message leaks.
Laravel's default encryption configuration uses OpenSSL with AES-256-CBC, but many applications implement RSA-based features that inherit this vulnerability. The attack works by exploiting the fact that RSA decryption implementations often take different amounts of time to process valid versus invalid padding, allowing attackers to gradually decrypt messages through repeated queries.
A typical Laravel-specific scenario involves API endpoints that decrypt incoming data using RSA. Consider this common pattern:
class PaymentController extends Controller
{
public function process(Request $request)
{
$encryptedData = $request->input('payment_data');
$decrypted =
$encryptedData);
return response()->json(['status' => 'success']);
}
}The vulnerability appears when decryption fails with different timing or error messages. Laravel's default exception handling might return a 500 error for decryption failures, while successful decryption proceeds to business logic. This timing difference creates an oracle.
Another Laravel-specific manifestation occurs in JWT token handling. Many applications use RSA-signed JWTs for authentication:
class AuthController extends Controller
{
public function verifyToken(Request $request)
{
$token = $request->bearerToken();
$publicKey = file_get_contents(storage_path('keys/public.pem'));
try {
$decoded = JWT::decode($token, $publicKey, ['RS256']);
return response()->json(['authenticated' => true]);
} catch (Exception $e) {
return response()->json(['error' => 'Invalid token'], 401);
}
}
}The critical issue is that JWT libraries often don't implement constant-time verification for RSA signatures. The catch block's execution time varies based on whether the token was structurally valid versus having a bad signature, creating a timing oracle.
Laravel's queue system can also introduce Bleichenbacher vulnerabilities when processing RSA-encrypted job payloads. Failed decryption attempts might be retried with different timing patterns, leaking information through retry intervals.
Database encryption is another vector. Applications using RSA to encrypt database credentials or sensitive data might expose decryption endpoints that process user-supplied encrypted values, creating oracle opportunities through database query timing variations.
Laravel-Specific Detection
Detecting Bleichenbacher vulnerabilities in Laravel requires examining both code patterns and runtime behavior. Start by auditing your codebase for RSA usage patterns.
Search for these Laravel-specific patterns:
php artisan route:list | grep -i rsa
php artisan route:list | grep -i decrypt
php artisan route:list | grep -i jwt
find app/ -name '*.php' -exec grep -l 'openssl_public_decrypt' {} \;
find app/ -name '*.php' -exec grep -l 'JWT::decode' {} \;
find app/ -name '*.php' -exec grep -l 'RS256' {} \;Examine your composer.json for vulnerable dependencies:
composer show | grep -E '(firebase/php-jwt|lcobucci/jwt|paragonie)'
# Check for known vulnerable versions
composer auditmiddleBrick's API security scanner specifically tests for Bleichenbacher vulnerabilities in Laravel applications. It identifies endpoints that process encrypted data and analyzes their response timing patterns. The scanner tests for:
- Endpoints accepting encrypted parameters in URLs, headers, or request bodies
- API routes that decrypt JWT tokens without constant-time verification
- Queue job processing endpoints that handle encrypted payloads
- Database encryption endpoints that process user-supplied encrypted data
- Middleware that performs RSA operations without timing attack protections
- Third-party service integrations that use RSA without proper padding validation
middleBrick's LLM/AI security module also detects if your Laravel application uses AI services that might be vulnerable to prompt injection attacks that could exploit RSA-encrypted communication channels.
For manual testing, use timing analysis tools against your Laravel endpoints:
#!/bin/bash
# Simple timing oracle test
URL="http://your-laravel-app.test/api/endpoint"
PAYLOAD="valid_encrypted_payload_here"
for i in {1..100}; do
start=$(date +%s%N)
curl -s -o /dev/null -w "%{http_code}" "$URL?data=$PAYLOAD"
end=$(date +%s%N)
echo "Request $i: $((end-start)) ns"
doneLook for statistically significant timing variations between different input types. Laravel's debug mode (APP_DEBUG=true) can exacerbate this by providing detailed error messages that vary based on decryption success.
Laravel-Specific Remediation
Remediating Bleichenbacher vulnerabilities in Laravel requires both code changes and architectural decisions. The most effective approach is to eliminate RSA padding oracles entirely.
First, migrate away from PKCS#1 v1.5 padding to OAEP padding, which is specifically designed to prevent oracle attacks:
// config/app.php - Add to existing providers array
'providers' => [
// ... existing providers
App\Providers\SecurityProvider::class,
],Create a security provider with OAEP implementation:
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Crypt;
class SecurityProvider extends ServiceProvider
{
public function boot()
{
// Override default encryption to use OAEP
Crypt::extend('rsa-oaep', function ($app, $config) {
return new class($config) {
private $publicKey;
private $privateKey;
public function __construct($config)
{
$this->publicKey = openssl_pkey_get_public(
file_get_contents($config['public'])
);
$this->privateKey = openssl_pkey_get_private(
file_get_contents($config['private'])
);
}
public function encrypt($data)
{
$encrypted = '';
openssl_public_encrypt(
$data,
$encrypted,
$this->publicKey,
OPENSSL_PKCS1_OAEP_PADDING
);
return base64_encode($encrypted);
}
public function decrypt($data)
{
$decoded = base64_decode($data);
$decrypted = '';
// Constant-time decryption attempt
$result = openssl_private_decrypt(
$decoded,
$decrypted,
$this->privateKey,
OPENSSL_PKCS1_OAEP_PADDING
);
if (!$result) {
// Uniform response regardless of failure reason
throw new \RuntimeException('Decryption failed');
}
return $decrypted;
}
};
});
}
}For JWT handling, use libraries that implement constant-time verification:
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class SecureAuthController extends Controller
{
public function verifyToken(Request $request)
{
$token = $request->bearerToken();
try {
$key = new Key(file_get_contents(storage_path('keys/public.pem')), 'RS256');
// Use constant-time comparison
$decoded = JWT::decode($token, $key);
if (!$this->constantTimeEquals($token, $decoded->signature)) {
throw new \InvalidArgumentException('Invalid signature');
}
return response()->json(['authenticated' => true]);
} catch (\Exception $e) {
// Uniform response time using sleep
usleep(50000); // 50ms constant delay
return response()->json(['error' => 'Authentication failed'], 401);
}
}
private function constantTimeEquals($a, $b)
{
$result = 0;
$len = max(strlen($a), strlen($b));
for ($i = 0; $i < $len; $i++) {
$result |= ord($a[$i] ?? "\0") ^ ord($b[$i] ?? "\0");
}
return $result === 0;
}
}For queue processing, implement uniform error handling:
class SecureJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function handle()
{
try {
$decrypted = $this->decryptPayload($this->payload);
$this->processData($decrypted);
} catch (\Exception $e) {
// Log but don't expose details
\Log::error('Job processing failed: ' . $e->getMessage());
// Uniform retry delay regardless of failure type
$this->release(30); // 30 seconds uniform delay
}
}
private function decryptPayload($payload)
{
$result = openssl_private_decrypt(
base64_decode($payload),
$decrypted,
$this->getPrivateKey(),
OPENSSL_PKCS1_OAEP_PADDING
);
if (!$result) {
throw new \RuntimeException('Decryption failed');
}
return $decrypted;
}
}Finally, use middleBrick's continuous monitoring to verify your fixes. The Pro plan can scan your Laravel APIs on a schedule, alerting you if new Bleichenbacher vulnerabilities appear in your codebase.