HIGH command injectionlaraveljwt tokens

Command Injection in Laravel with Jwt Tokens

Command Injection in Laravel with Jwt Tokens — how this specific combination creates or exposes the vulnerability

Command injection occurs when an attacker can inject and execute arbitrary system commands. In Laravel, this risk can intersect with JWT token handling when token data is used to build shell commands. Consider a scenario where a decoded JWT claim (for example, a username or hostname) is passed to a system call without validation or escaping:

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

class ReportController extends Controller
{
    public function export(Request $request)
    {
        $token = $request->header('Authorization');
        if (!$token) {
            return response('Unauthorized', 401);
        }
        // Insecure: assumes payload is safe
        $decoded = JWT::decode($token, new Key(env('JWT_SECRET'), 'HS256'));
        $username = $decoded->username; // attacker-controlled if token is compromised or algorithm is weak
        // Dangerous: user input embedded in shell command
        $output = shell_exec("tar -czf /tmp/report-{$username}.tar.gz /var/reports");
        return response($output);
    }
}
</code>

In this example, if an attacker can craft a valid JWT (by stealing the secret, exploiting a weak algorithm like none, or brute-forcing the secret), they can control the username claim. If the application trusts the token’s structure, the attacker can inject shell metacharacters (e.g., ;, &, |, `, or $()) into the username to achieve command injection. The JWT itself becomes a vector because the token’s payload is used to construct a shell command without sanitization or strict allowlisting.

Additionally, JWT tokens may carry roles or permissions that, if improperly trusted, can lead to privilege escalation when combined with command execution. For instance, an attacker who modifies a role claim to an administrative value might trigger a code path that runs high-privilege shell commands. Even if the token is cryptographically valid, the application must treat all decoded data as untrusted input. The risk is not only in direct command construction but also in logging or monitoring features that invoke shell utilities on token-derived values.

Other indirect patterns include using JWT data to select files or directories for archival or diagnostic commands. If a filename or path derived from a JWT claim is used in functions like exec, system, or backtick operators, and the input is not rigorously validated, command injection can occur. Attackers may also exploit weak secret management to forge tokens that enable these dangerous code paths.

Jwt Tokens-Specific Remediation in Laravel — concrete code fixes

Remediation focuses on never passing untrusted JWT claims directly to shell commands. Use strict allowlisting, avoid shell execution where possible, and apply defense-in-depth validation.

  • Validate and sanitize all JWT-derived data: Treat decoded claims as untrusted. Use allowlists for known values instead of blacklists.
  • Avoid shell execution entirely: Prefer PHP-native operations (e.g., ZipArchive for archives) over shell commands.
  • Use escapeshellarg: If shell commands are unavoidable, escape arguments properly.

Example 1: Safe filename construction

Instead of injecting a raw claim into a shell command, validate the username against a safe pattern and use PHP to create the archive:

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use ZipArchive;

class ReportController extends Controller
{
    public function export(Request $request)
    {
        $token = $request->header('Authorization');
        if (!$token) {
            return response('Unauthorized', 401);
        }
        $decoded = JWT::decode($token, new Key(env('JWT_SECRET'), 'HS256'));
        $username = $decoded->username;

        // Strict allowlist: only alphanumeric, underscore, hyphen (adjust as needed)
        if (!preg_match('/^[a-zA-Z0-9_-]+$/D', $username)) {
            return response('Invalid username', 400);
        }

        // Safe: use PHP to create archive, no shell involvement
        $zip = new ZipArchive();
        $filename = storage_path("app/reports/report-{$username}.zip");
        if ($zip->open($filename, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
            return response('Cannot create archive', 500);
        }
        $zip->addFile('/var/reports/data.txt', 'report.txt');
        $zip->close();

        return response()->download($filename);
    }
}
</code>

Example 2: Escaping if shell commands are unavoidable

If you must use shell commands for a legacy workflow, always escape user input with escapeshellarg:

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

class ReportController extends Controller
{
    public function generate(Request $request)
    {
        $token = $request->header('Authorization');
        if (!$token) {
            return response('Unauthorized', 401);
        }
        $decoded = JWT::decode($token, new Key(env('JWT_SECRET'), 'HS256'));
        $username = $decoded->username;

        if (!preg_match('/^[a-zA-Z0-9_-]+$/D', $username)) {
            return response('Invalid username', 400);
        }

        // Proper escaping; note: better to avoid shell_exec entirely
        $safeUsername = escapeshellarg($username);
        $output = shell_exec("tar -czf /tmp/report-{$safeUsername}.tar.gz /var/reports 2&& echo OK");
        return response($output);
    }
}
</code>

Additional defenses include rotating JWT secrets, using asymmetric algorithms (RS256) to prevent secret brute-forcing, and applying principle of least privilege to the runtime environment so that any compromised command execution is limited in impact. Monitor logs for unusual token usage patterns and validate algorithm settings to prevent none algorithm attacks.

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

Can a valid JWT token still lead to command injection?
Yes. If the application trusts claims within a valid token (e.g., username, role) and uses them to construct shell commands without validation or escaping, command injection can occur even with a correctly signed JWT.
Does using JWT eliminate the need for input validation?
No. JWTs ensure integrity and optionally authenticity, but they do not guarantee that the payload is safe for system commands. Always validate and sanitize any data derived from tokens before use.