CVE-2021-4268: Cross-Site Request Forgery (CSRF) (CWE-352)
value: A vulnerability, which was classified as problematic, was found in phpRedisAdmin up to 1.17.3. This affects an unknown part. The manipulation leads to cross-site request forgery. It is possible to initiate the attack remotely. Upgrading to version 1.18.0 is able to address this issue. The name of the patch is b9039adbb264c81333328faa9575ecf8e0d2be94. It is recommended to upgrade the affected component. The associated identifier of this vulnerability is VDB-216471.
CWE description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
CVSS v3 base score: 8.8
Published: 2022-12-21T19:15Z
Repository: https://github.com/erikdubbelboer/phpRedisAdmin
Language: PHP
Files changed: edit.php, export.php, import.php, includes/common.inc.php, login.php, rename.php, ttl.php
Commit date: 2021-08-26 18:19:02 +0000
Commit message: Prevent Cross-Site Request Forgery

Diff: diff --git a/edit.php b/edit.php
index 0940503..9642171 100644
--- a/edit.php
+++ b/edit.php
@@ -143,10 +143,11 @@ $page['js'][]  = 'frame';
 require 'includes/header.inc.php';
 
 ?>
 <h2><?php echo $edit ? 'Edit' : 'Add'?></h2>
 <form action="<?php echo format_html(getRelativePath('edit.php'))?>" method="post">
+<input type="hidden" name="csrf" value="<?php echo $csrfToken; ?>" />
 
 <p>
 <label for="type">Type:</label>
 <select name="type" id="type">
 <option value="string" <?php echo (isset($_GET['type']) && ($_GET['type'] == 'string')) ? 'selected="selected"' : ''?>>String</option>
diff --git a/export.php b/export.php
index c886e46..5d88b11 100644
--- a/export.php
+++ b/export.php
@@ -185,10 +185,11 @@ require 'includes/header.inc.php';
 
 ?>
 <h2>Export <?php echo isset($_GET['key']) ? format_html($_GET['key']) : ''?></h2>
 
 <form action="<?php echo format_html(getRelativePath('export.php'))?>" method="post">
+<input type="hidden" name="csrf" value="<?php echo $csrfToken; ?>" />
 
 <p>
 <label for="type">Type:</label>
 <select name="type" id="type">
 <option value="redis" <?php echo (isset($_GET['type']) && ($_GET['type'] == 'redis')) ? 'selected="selected"' : ''?>>Redis</option>
diff --git a/import.php b/import.php
index 1786164..590467e 100644
--- a/import.php
+++ b/import.php
@@ -90,10 +90,11 @@ $page['js'][]  = 'frame';
 require 'includes/header.inc.php';
 
 ?>
 <h2>Import</h2>
 <form action="<?php echo format_html(getRelativePath('import.php'))?>" method="post">
+<input type="hidden" name="csrf" value="<?php echo $csrfToken; ?>" />
 
 <p>
 <label for="commands">Commands:<br>
 <br>
 <span class="info">
diff --git a/includes/common.inc.php b/includes/common.inc.php
index 85dfe76..cfa5059 100644
--- a/includes/common.inc.php
+++ b/includes/common.inc.php
@@ -2,10 +2,28 @@
 require dirname(__FILE__) . '/../vendor/autoload.php';
 
 define('PHPREDIS_ADMIN_PATH', dirname(__DIR__));
 
 
+if (session_status() !== PHP_SESSION_DISABLED) {
+  session_start();
+
+  if (isset($_SESSION['phpredisadmin_csrf'])) {
+    $csrfToken = $_SESSION['phpredisadmin_csrf'];
+  } else {
+    $csrfToken = bin2hex(random_bytes(16));
+    $_SESSION['phpredisadmin_csrf'] = $csrfToken;
+  }
+} else {
+  $csrfToken = 'nosession';
+}
+
+if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+  if ($_POST['csrf'] !== $csrfToken) {
+    die('bad csrf token');
+  }
+}
 
 
 // These includes are needed by each script.
 if(file_exists(PHPREDIS_ADMIN_PATH . '/includes/config.inc.php')){
   require_once PHPREDIS_ADMIN_PATH . '/includes/config.inc.php';
diff --git a/login.php b/login.php
index 82c5514..298b32f 100644
--- a/login.php
+++ b/login.php
@@ -11,10 +11,11 @@ require 'includes/header.inc.php';
 ?>
 
 <h1 class="logo">phpRedisAdmin</h1>
 
 <form class="form-signin" method="post" action="login.php">
+<input type="hidden" name="csrf" value="<?php echo $csrfToken; ?>" />
     <h2 class="form-signin-heading">Please log in</h2>
 
     <?php if (isset($_POST['username']) || isset($_POST['password'])): ?>
         <div class="invalid-credentials">
             <h3>Invalid username/password</h3>
diff --git a/rename.php b/rename.php
index e5460a1..4dbddcd 100644
--- a/rename.php
+++ b/rename.php
@@ -34,10 +34,11 @@ $page['js'][]  = 'frame';
 require 'includes/header.inc.php';
 
 ?>
 <h2>Edit Name of <?php echo format_html($_GET['key'])?></h2>
 <form action="<?php echo format_html(getRelativePath('rename.php'))?>" method="post">
+<input type="hidden" name="csrf" value="<?php echo $csrfToken; ?>" />
 
 <input type="hidden" name="old" value="<?php echo format_html($_GET['key'])?>">
 
 <p>
 <label for="key">Key:</label>
diff --git a/ttl.php b/ttl.php
index 6db0cb2..d6bdc07 100644
--- a/ttl.php
+++ b/ttl.php
@@ -25,10 +25,11 @@ $page['js'][]  = 'frame';
 require 'includes/header.inc.php';
 
 ?>
 <h2>Edit TTL</h2>
 <form action="<?php echo format_html(getRelativePath('ttl.php'))?>" method="post">
+<input type="hidden" name="csrf" value="<?php echo $csrfToken; ?>" />
 
 <p>
 <label for="key">Key:</label>
 <input type="text" name="key" id="key" size="30" <?php echo isset($_GET['key']) ? 'value="'.format_html($_GET['key']).'"' : ''?>>
 </p>

Fixed code: <input type="hidden" name="csrf" value="<?php echo $csrfToken; ?>" />
<input type="hidden" name="csrf" value="<?php echo $csrfToken; ?>" />
<input type="hidden" name="csrf" value="<?php echo $csrfToken; ?>" />
if (session_status() !== PHP_SESSION_DISABLED) {
  session_start();

  if (isset($_SESSION['phpredisadmin_csrf'])) {
    $csrfToken = $_SESSION['phpredisadmin_csrf'];
  } else {
    $csrfToken = bin2hex(random_bytes(16));
    $_SESSION['phpredisadmin_csrf'] = $csrfToken;
  }
} else {
  $csrfToken = 'nosession';
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  if ($_POST['csrf'] !== $csrfToken) {
    die('bad csrf token');
  }
}
<input type="hidden" name="csrf" value="<?php echo $csrfToken; ?>" />
<input type="hidden" name="csrf" value="<?php echo $csrfToken; ?>" />
<input type="hidden" name="csrf" value="<?php echo $csrfToken; ?>" />