CVE-2023-47350: Cross-Site Request Forgery (CSRF) (CWE-352)
value: Cross-Site Request Forgery (CSRF) vulnerability in SwiftyEdit Content Management System prior to v1.2.0, allows remote attackers to escalate privileges via the user password update functionality.
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: 2023-11-22T16:15Z
Repository: https://github.com/SwiftyEdit/SwiftyEdit
Language: Other
Files changed: core/functions/func_userdata.php, index.php, styles/default/templates/comment_form.tpl, styles/default/templates/download.tpl, styles/default/templates/loginbox.tpl, styles/default/templates/navigation.tpl, styles/default/templates/orders.tpl, styles/default/templates/page_psw_input.tpl, styles/default/templates/password.tpl, styles/default/templates/products-display.tpl, styles/default/templates/products-list.tpl, styles/default/templates/profile_main.tpl, styles/default/templates/registerform.tpl, styles/default/templates/search.tpl, styles/default/templates/shopping_cart_form.tpl, styles/default/templates/shopping_cart_table.tpl, styles/default/templates/sidebar-filter.tpl, styles/default/templates/statusbox.tpl
Commit date: 2023-11-02 19:30:50 +0100
Commit message: Add a hidden CSRF token to all forms

This update fixes a vulnerability that allows a potential attacker to make changes to the database via forms. In the worst case, for example, changing passwords.
@mechaneus thank you for finding and reporting this vulnerability.

Diff: diff --git a/core/functions/func_userdata.php b/core/functions/func_userdata.php
index c8380c0..9698b11 100644
--- a/core/functions/func_userdata.php
+++ b/core/functions/func_userdata.php
@@ -254,13 +254,15 @@ function se_start_user_session($ud) {
 	$_SESSION['user_firstname'] = $ud['user_firstname'];
 	$_SESSION['user_lastname'] = $ud['user_lastname'];
 	$_SESSION['user_hash'] = md5($ud['user_nick']);
 	
 	/* CSRF Protection */
-	$token = md5(uniqid(rand(), TRUE));
-	$_SESSION['token']      = $token;
-	$_SESSION['token_time'] = time();
+    if(empty($_SESSION['token'])) {
+        $token = md5(uniqid(rand(), TRUE));
+        $_SESSION['token'] = $token;
+        $_SESSION['token_time'] = time();
+    }
 	
 	$arr_drm = explode("|", $ud['user_drm']);
 	
 	if($arr_drm[0] == "drm_acp_pages")	{  $_SESSION['acp_pages'] = "allowed";  }
 	if($arr_drm[1] == "drm_acp_files")	{  $_SESSION['acp_files'] = "allowed";  }
diff --git a/index.php b/index.php
index af9f0a9..8668971 100644
--- a/index.php
+++ b/index.php
@@ -31,14 +31,26 @@ $prepend_body_code = '';
 $append_body_code = '';
 $mod_slug = '';
 
 const SE_SECTION = "frontend";
 
-if(empty($_SESSION['visitor_csrf_token'])) {
-	$_SESSION['visitor_csrf_token'] = md5(uniqid(rand(), TRUE));
+if(empty($_SESSION['token'])) {
+	$_SESSION['token'] = md5(uniqid(rand(), TRUE));
+    $_SESSION['token_time'] = time();
 }
 
+/* stop all $_POST actions if csrf token is empty or invalid */
+if(!empty($_POST)) {
+    if(empty($_POST['csrf_token'])) {
+        die('Error: CSRF Token is empty');
+    }
+    if($_POST['csrf_token'] !== $_SESSION['token']) {
+        die('Error: CSRF Token is invalid');
+    }
+}
+
+
 /**
  * if there is no database config we start the installer
  * @var string $se_db_content SQLite file from config.php or /content/config.php
  * @var string $database_host is set in config_database
  */
@@ -334,10 +346,13 @@ if($se_prefs['prefs_smarty_compile_check'] == 1) {
 	$smarty->compile_check = true;
 } else {
 	$smarty->compile_check = false;
 }
 
+$hidden_csrf_token = '<input type="hidden" name="csrf_token" value="'.$_SESSION['token'].'">';
+$smarty->assign('hidden_csrf_token', "$hidden_csrf_token", true);
+
 
 /* reset of the user-defined theme */
 if(isset($_POST['reset_theme'])) {
 	unset($_SESSION['prefs_template'],$_SESSION['prefs_template_stylesheet']);
 }
diff --git a/styles/default/templates/comment_form.tpl b/styles/default/templates/comment_form.tpl
index 7c17d5f..72d2831 100644
--- a/styles/default/templates/comment_form.tpl
+++ b/styles/default/templates/comment_form.tpl
@@ -22,10 +22,11 @@
 	  <input class="btn btn-primary" type="submit" name="send_user_comment" value="{$btn_send_comment}">
 	  
 	  <input type="hidden" name="page_id" value="{$page_id}">
 	  <input type="hidden" name="post_id" value="{$post_id}">
 		<input type="hidden" name="parent_id" value="{$parent_id}">
+		{$hidden_csrf_token}
 	</form>
 	
 	<div class="m-3">
 	{$form_response}
 	</div>
diff --git a/styles/default/templates/download.tpl b/styles/default/templates/download.tpl
index 5cd8461..baf6156 100644
--- a/styles/default/templates/download.tpl
+++ b/styles/default/templates/download.tpl
@@ -4,6 +4,7 @@
 	<input type="hidden" name="file" value="{$file_src}">
 	<input type="hidden" name="csrf_token" value="{$csrf_token}">
 	<button class="btn btn-success" type="submit" name="download" value="{$file_src}">
 		<i class="bi-arrow-down-circle"></i> Download
 	</button>
+	{$hidden_csrf_token}
 </form>
\ No newline at end of file
diff --git a/styles/default/templates/loginbox.tpl b/styles/default/templates/loginbox.tpl
index d80a939..491de50 100644
--- a/styles/default/templates/loginbox.tpl
+++ b/styles/default/templates/loginbox.tpl
@@ -30,10 +30,11 @@
                         {$failed_login}
                     </div>
                 {/if}
 
                 <p>{$show_forgotten_psw_link}</p>
+                {$hidden_csrf_token}
             </form>
 
 			{if $show_register_link != ''}
             	<p>{$msg_register}<br>{$show_register_link}</p>
 			{/if}
diff --git a/styles/default/templates/navigation.tpl b/styles/default/templates/navigation.tpl
index d8e8396..aeea933 100644
--- a/styles/default/templates/navigation.tpl
+++ b/styles/default/templates/navigation.tpl
@@ -16,10 +16,11 @@
 			</ul>
 		</div>
 		
 		<form class="form-inline" action="{$search_uri}" method="POST">
     		<input class="form-control mr-sm-2 searchbox" name="s" type="search" aria-label="Search" value="{$search_string}">
+			{$hidden_csrf_token}
   		</form>
 		
 	</div>
 </nav>
 {/nocache}
\ No newline at end of file
diff --git a/styles/default/templates/orders.tpl b/styles/default/templates/orders.tpl
index e1db9dd..6871099 100644
--- a/styles/default/templates/orders.tpl
+++ b/styles/default/templates/orders.tpl
@@ -59,18 +59,20 @@
 													<button type="submit" name="startUpload" class="btn btn-primary">Upload</button>
 												</div>
 											</div>
 											<input type="hidden" name="order" value="{$order.nbr}">
 											<input type="hidden" name="pos" value="{$product.pos}">
+											{$hidden_csrf_token}
 										</form>
 										{/if}
 										{* we provide no preview, but user can download the file  *}
 										{if $product.user_upload_status == 'uploaded'}
 											<form action="{$order_page_uri}" method="POST" class="d-inline">
 												<button type="submit" class="btn btn-sm btn-primary" name="download_user_file">Download</button>
 												<input type="hidden" name="order" value="{$order.nbr}">
 												<input type="hidden" name="pos" value="{$product.pos}">
+												{$hidden_csrf_token}
 											</form>
 										{/if}
 									</td>
 									<td>{$product.amount}</td>
 									<td>{$product.price_gross} {$order.currency}</td>
@@ -78,16 +80,18 @@
 										
 										{if $product.dl_file != '' AND $order.status_payment == '2'}
 											<form action="{$order_page_uri}" method="POST" class="d-inline">
 												<button class="btn btn-primary" type="submit" name="dl_p_file" value="{$product.post_id}"><i class="bi bi-download"></i> DOWNLOAD</button>
 												<input type="hidden" name="order_id" value="{$order.nbr}">
+												{$hidden_csrf_token}
 											</form>
 										{/if}
 										{if $product.dl_file_ext != '' AND $order.status_payment == '2'}
 											<form action="{$order_page_uri}" method="POST" class="d-inline">
 												<button class="btn btn-primary" type="submit" name="dl_p_file_ext" value="{$product.post_id}"><i class="bi bi-cloud-download"></i> DOWNLOAD</button>
 												<input type="hidden" name="order_id" value="{$order.nbr}">
+												{$hidden_csrf_token}
 											</form>
 										{/if}
 										
 									</td>
 								</tr>
diff --git a/styles/default/templates/page_psw_input.tpl b/styles/default/templates/page_psw_input.tpl
index 6395215..331d46e 100644
--- a/styles/default/templates/page_psw_input.tpl
+++ b/styles/default/templates/page_psw_input.tpl
@@ -7,7 +7,8 @@
 			</div>
 			<div class="col-md-3">
 				<input type="submit" name="send" class="btn btn-success btn-block" value="{$button_send}">
 			</div>
 		</div>
+		{$hidden_csrf_token}
 	</form>
 </fieldset>
\ No newline at end of file
diff --git a/styles/default/templates/password.tpl b/styles/default/templates/password.tpl
index c71597e..dc1288c 100644
--- a/styles/default/templates/password.tpl
+++ b/styles/default/templates/password.tpl
@@ -16,8 +16,8 @@
 					<input type="text" class="form-control" name="mail">
 				</div>	
 		
 				<input class="btn btn-success" type="submit" name="ask_for_psw" value="{$button_send}">
 			</div>
-	
+		{$hidden_csrf_token}
 	</fieldset>
 </form>
\ No newline at end of file
diff --git a/styles/default/templates/products-display.tpl b/styles/default/templates/products-display.tpl
index f8502a6..42b97bd 100644
--- a/styles/default/templates/products-display.tpl
+++ b/styles/default/templates/products-display.tpl
@@ -72,11 +72,11 @@
                     {/if}
                         <div class="mt-2">
                             <hr>
                             <button class="btn btn-outline-success btn-lg" name="add_to_cart" value="{$product_id}">{$btn_add_to_cart}</button>
                         </div>
-                        <input type="hidden" name="csrf_token" value="{$csrf_token}">
+                        {$hidden_csrf_token}
 
                     </form>
                 </div>
                 {/if}
             {/if}
@@ -271,10 +271,11 @@
                 <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#startDownloadModal"><i class="bi bi-download"></i> Download</button>
                 <input type="hidden" name="get_attachment" value="{$product_id}">
                 <input type="hidden" name="csrf_token" value="{$csrf_token}">
             </div>
         </div>
+            {$hidden_csrf_token}
         </form>
     {/if}
 
     {if $product_snippet_price != ""}
     <div class="card post-snippet-price mb-3">
diff --git a/styles/default/templates/products-list.tpl b/styles/default/templates/products-list.tpl
index f462144..87d81f6 100644
--- a/styles/default/templates/products-list.tpl
+++ b/styles/default/templates/products-list.tpl
@@ -90,11 +90,11 @@
                     <div class="col-md-4">
                         <a class="btn btn-link w-100 {$link_classes}" href="{$value.product_href}">{$btn_read_more}</a>
                         {if $value.show_shopping_cart == true}
                             <form action="{$form_action}" method="POST" class="pt-1">
                                 <button class="btn btn-outline-success w-100" name="add_to_cart" value="{$value.product_id}">{$btn_add_to_cart}</button>
-                                <input type="hidden" name="csrf_token" value="{$csrf_token}">
+                                {$hidden_csrf_token}
                             </form>
                         {/if}
                     </div>
                 </div>
             </div>
diff --git a/styles/default/templates/profile_main.tpl b/styles/default/templates/profile_main.tpl
index 1c25e08..d92120a 100644
--- a/styles/default/templates/profile_main.tpl
+++ b/styles/default/templates/profile_main.tpl
@@ -53,10 +53,11 @@
 		</div>
 		<div class="card-footer">
 			<input class="btn btn-primary" type="submit" name="update_address_data" value="{$lang_button_update}">
 		</div>
 	</div>
+	{$hidden_csrf_token}
 </form>
 
 <form class="form-horizontal" id="form_ba_address" action="{$form_url}" method="POST">
 
 	<div class="card mb-3">
@@ -122,11 +123,11 @@
 		<div class="card-footer">
 			<input class="btn btn-primary" type="submit" name="update_ba_data" value="{$lang_button_update}">
 		</div>
 	</div>
 
-
+	{$hidden_csrf_token}
 </form>
 
 <form class="form-horizontal" id="form_psw_data" action="{$form_url}" method="POST">
 	<div class="card mb-3">
 	<div class="card-header">{$lang_legend_access_data} ({$lang_label_psw})</div>
@@ -146,10 +147,11 @@
 	</div>
 	<div class="card-footer">
 		<input class="btn btn-primary" type="submit" name="update_psw_data" value="{$lang_button_update}">
 	</div>
 	</div>
+	{$hidden_csrf_token}
 </form>
 
 <form class="form-horizontal" id="form_email_data" action="{$form_url}" method="POST">
 
 <div class="card">
@@ -174,21 +176,21 @@
 	</div>
 	<div class="card-footer">
 		<input class="btn btn-primary" type="submit" name="update_email_data" value="{$lang_button_update}">
 	</div>
 </div>
+	{$hidden_csrf_token}
 </form>
 
 
 <hr>
 
 
 
 <!-- Avatar -->
 <form id="profileform" action="{$form_url}" method="post" enctype="multipart/form-data">
 
-
 	<div class="card mb-3">
 		<div class="card-header">{$lang_legend_avatar}</div>
 		<div class="card-body">
 	<div class="row">
 		<div class="col-9">
@@ -210,11 +212,11 @@
 		</div>
 	</div>
 
 </div>
 </div>
-
+	{$hidden_csrf_token}
 </form>
 
 
 <hr>
 
@@ -227,9 +229,10 @@
 <legend>{$lang_legend_delete_account}</legend>
 <p>{$lang_msg_delete_account}</p>
 
 <input class="btn btn-danger btn-small" type="submit" onclick="return confirm('{$msg_confirm_delete_account}')" name="delete_my_account" value="{$lang_button_delete}">
 </fieldset>
+	{$hidden_csrf_token}
 </form>
 
 </div>
 {/nocache}
\ No newline at end of file
diff --git a/styles/default/templates/registerform.tpl b/styles/default/templates/registerform.tpl
index 6283735..b2be437 100644
--- a/styles/default/templates/registerform.tpl
+++ b/styles/default/templates/registerform.tpl
@@ -93,9 +93,9 @@
 	</div>
 </div>
 		
 		
 <input class="btn btn-success mt-3 mb-3" type="submit" name="send_registerform" value="{$lang_button_send_register}">
-		
+	{$hidden_csrf_token}
 
 </form>
 {/nocache}
\ No newline at end of file
diff --git a/styles/default/templates/search.tpl b/styles/default/templates/search.tpl
index aab5d11..52d4a82 100644
--- a/styles/default/templates/search.tpl
+++ b/styles/default/templates/search.tpl
@@ -4,8 +4,8 @@
             <div class="col-md-12">
                 <input class="searchbox form-control" type="text" name="s" value="{$search_string}">
             </div>
         </div>
         <hr class="shadow">
+        {$hidden_csrf_token}
     </form>
-</div>
-
+</div>
\ No newline at end of file
diff --git a/styles/default/templates/searchbox.tpl b/styles/default/templates/searchbox.tpl
deleted file mode 100644
index 5bb0986..0000000
--- a/styles/default/templates/searchbox.tpl
+++ /dev/null
@@ -1,10 +0,0 @@
-<div class="search_toggle">
-    <form action="{$search_uri}" method="POST" class="">
-        <div class="row">
-            <div class="col-md-12">
-                <input class="searchbox form-control" type="text" name="s" value="{$search_string}">
-            </div>
-        </div>
-        <hr class="shadow">
-    </form>
-</div>
\ No newline at end of file
diff --git a/styles/default/templates/shopping_cart_form.tpl b/styles/default/templates/shopping_cart_form.tpl
index b6f5ed7..01a33fb 100644
--- a/styles/default/templates/shopping_cart_form.tpl
+++ b/styles/default/templates/shopping_cart_form.tpl
@@ -22,8 +22,9 @@
     <div class="mb-3">
         <label class="form-label" for="requestcomment">{$lang_label_cart_comment}</label>
         <textarea class="form-control" id="requestcomment" name="buyer_comment">{$buyer_comment}</textarea>
     </div>
     <button type="submit" class="btn btn-success" name="send_request" value="send">{$lang_btn_send_order_request}</button>
+    {$hidden_csrf_token}
 </form>
 
 </div>
\ No newline at end of file
diff --git a/styles/default/templates/shopping_cart_table.tpl b/styles/default/templates/shopping_cart_table.tpl
index 3c5c7cb..83d767e 100644
--- a/styles/default/templates/shopping_cart_table.tpl
+++ b/styles/default/templates/shopping_cart_table.tpl
@@ -36,10 +36,11 @@
                         {$lang_label_product_amount}
                         <form action="{$shopping_cart_uri}" method="POST">
                             <input type="number" name="cart_product_amount" value="{$item.amount}" class="form-control"
                                    onchange="this.form.submit()">
                             <input type="hidden" name="cart_item_key" value="{$item.cart_id}">
+                            {$hidden_csrf_token}
                         </form>
                     </div>
                 </div>
 
             </td>
@@ -59,10 +60,11 @@
 
             <td class="text-center">
                 <form action="{$shopping_cart_uri}" method="POST">
                     <button type="submit" class="btn btn-link link-danger" name="remove_from_cart"
                             value="{$item.cart_id}"><i class="bi bi-trash"></i></button>
+                    {$hidden_csrf_token}
                 </form>
             </td>
         </tr>
     {/foreach}
 
@@ -86,10 +88,11 @@
                         <input type="radio" class="form-check-input" name="set_payment" value="{$pm.key}"
                                id="id_{$pm.key}" autocomplete="off" {$checked_{$pm.key}}>
                         <label class="form-check-label" for="id_{$pm.key}">{$pm.title} ({$currency} {$pm.cost})</label>
                     </div>
                 {/foreach}
+                {$hidden_csrf_token}
             </form>
         </td>
         <td class="text-end">{$currency} {$cart_payment_costs}</td>
         <td></td>
     </tr>
@@ -163,8 +166,9 @@
                                 value="send">{$lang_btn_send_order}</button>
 
                     </div>
                 </div>
             </div>
+            {$hidden_csrf_token}
         </form>
     {/if}
 {/if}
\ No newline at end of file
diff --git a/styles/default/templates/sidebar-filter.tpl b/styles/default/templates/sidebar-filter.tpl
index 2793905..ca42ea7 100644
--- a/styles/default/templates/sidebar-filter.tpl
+++ b/styles/default/templates/sidebar-filter.tpl
@@ -53,9 +53,10 @@ example for links:
                         </div>
                     {/if}
 
                 </div>
             {/foreach}
-    </div>
     <input type="hidden" name="set_custom_filters" value="send">
+    {$hidden_csrf_token}
     </form>
+    </div>
 {/if}
\ No newline at end of file
diff --git a/styles/default/templates/statusbox.tpl b/styles/default/templates/statusbox.tpl
index 148ebf5..bc3ec60 100644
--- a/styles/default/templates/statusbox.tpl
+++ b/styles/default/templates/statusbox.tpl
@@ -17,11 +17,11 @@
 		<a href="{$link_acp}" class="btn btn-secondary w-100 mb-1">{$lang_button_acp}</a>
 
 		<form action="{$link_acp}?tn=pages&sub=edit" method="POST">
 			<div class="d-grid gap-2">
 				<button name="editpage" value="{$page_id}" class="btn btn-secondary w-100">{$lang_button_edit_page}</button>
-				<input type="hidden" name="csrf_token" value="{$smarty.session.token}">
+				{$hidden_csrf_token}
 			</div>
 		</form>
 			</div>
 		{/if}
 

Vulnerable code: 	$token = md5(uniqid(rand(), TRUE));
	$_SESSION['token']      = $token;
	$_SESSION['token_time'] = time();
if(empty($_SESSION['visitor_csrf_token'])) {
	$_SESSION['visitor_csrf_token'] = md5(uniqid(rand(), TRUE));
	
                        <input type="hidden" name="csrf_token" value="{$csrf_token}">
                                <input type="hidden" name="csrf_token" value="{$csrf_token}">



		
</div>

<div class="search_toggle">
    <form action="{$search_uri}" method="POST" class="">
        <div class="row">
            <div class="col-md-12">
                <input class="searchbox form-control" type="text" name="s" value="{$search_string}">
            </div>
        </div>
        <hr class="shadow">
    </form>
</div>
    </div>
				<input type="hidden" name="csrf_token" value="{$smarty.session.token}">
Fixed code:     if(empty($_SESSION['token'])) {
        $token = md5(uniqid(rand(), TRUE));
        $_SESSION['token'] = $token;
        $_SESSION['token_time'] = time();
    }
if(empty($_SESSION['token'])) {
	$_SESSION['token'] = md5(uniqid(rand(), TRUE));
    $_SESSION['token_time'] = time();
/* stop all $_POST actions if csrf token is empty or invalid */
if(!empty($_POST)) {
    if(empty($_POST['csrf_token'])) {
        die('Error: CSRF Token is empty');
    }
    if($_POST['csrf_token'] !== $_SESSION['token']) {
        die('Error: CSRF Token is invalid');
    }
}


$hidden_csrf_token = '<input type="hidden" name="csrf_token" value="'.$_SESSION['token'].'">';
$smarty->assign('hidden_csrf_token', "$hidden_csrf_token", true);

		{$hidden_csrf_token}
	{$hidden_csrf_token}
                {$hidden_csrf_token}
			{$hidden_csrf_token}
											{$hidden_csrf_token}
												{$hidden_csrf_token}
												{$hidden_csrf_token}
												{$hidden_csrf_token}
		{$hidden_csrf_token}
		{$hidden_csrf_token}
                        {$hidden_csrf_token}
            {$hidden_csrf_token}
                                {$hidden_csrf_token}
	{$hidden_csrf_token}
	{$hidden_csrf_token}
	{$hidden_csrf_token}
	{$hidden_csrf_token}
	{$hidden_csrf_token}
	{$hidden_csrf_token}
	{$hidden_csrf_token}
        {$hidden_csrf_token}
</div>
    {$hidden_csrf_token}
                            {$hidden_csrf_token}
                    {$hidden_csrf_token}
                {$hidden_csrf_token}
            {$hidden_csrf_token}
    {$hidden_csrf_token}
    </div>
				{$hidden_csrf_token}