CVE-2021-3734 (published 2021-08-26T13:15Z)

yourls is vulnerable to Improper Restriction of Rendered UI Layers or Frames

Severity: MEDIUM
CVSS v2 base score: 6.8 (AV:N/AC:M/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 8.8, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-352
cwe_name: Cross-Site Request Forgery (CSRF)
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.
extended_description: When a web server is designed to receive a request from a client without any mechanism for verifying that it was intentionally sent, then it might be possible for an attacker to trick a client into making an unintentional request to the web server which will be treated as an authentic request. This can be done via a URL, image load, XMLHttpRequest, etc. and can result in exposure of data or unintended code execution.
         
cwe_id: CWE-1021
cwe_name: Improper Restriction of Rendered UI Layers or Frames
description: The web application does not restrict or incorrectly restricts frame objects or UI layers that belong to another application or domain, which can lead to user confusion about which interface the user is interacting with.
extended_description: A web application is expected to place restrictions on whether it is allowed to be rendered within frames, iframes, objects, embed or applet elements. Without the restrictions, users can be tricked into interacting with the application when they were not intending to.
         

Fix information:
repo_url: https://github.com/yourls/yourls
commit_details: repo_url: https://github.com/yourls/yourls
author: ྅༻ Ǭɀħ ༄༆ཉ
author_date: 2021-08-24 18:10:08+02:00
msg: Minor security improvements (#3034)

* Prevent iframe jacking
* Enforce nonce on admin login screen
repository: repo_url: https://github.com/yourls/yourls
repo_name: YOURLS/YOURLS
description: 🔗 The de facto standard self hosted URL shortener in PHP
repo_language: PHP
owner: yourls
file_changes: filename: admin-ajax.php
change_type: ModificationType.MODIFY
diff: @@ -7,6 +7,7 @@
 // This file will output a JSON string
 yourls_content_type_header( 'application/json' );
 yourls_no_cache_headers();
+yourls_no_frame_header();
 
 if( !isset( $_REQUEST['action'] ) )
 	die();

programming_language: PHP
filename: functions-auth.php
change_type: ModificationType.MODIFY
diff: @@ -122,6 +122,12 @@ function yourls_is_valid_user() {
  */
 function yourls_check_username_password() {
 	global $yourls_user_passwords;
+
+	// If login form (not API), check for nonce
+    if(!yourls_is_API()) {
+        yourls_verify_nonce('admin_login');
+    }
+
 	if( isset( $yourls_user_passwords[ $_REQUEST['username'] ] ) && yourls_check_password_hash( $_REQUEST['username'], $_REQUEST['password'] ) ) {
 		yourls_set_user( $_REQUEST['username'] );
 		return true;

programming_language: PHP
filename: functions-html.php
change_type: ModificationType.MODIFY
diff: @@ -59,6 +59,7 @@ function yourls_html_head( $context = 'index', $title = '' ) {
 	// Force no cache for all admin pages
 	if( yourls_is_admin() && !headers_sent() ) {
         yourls_no_cache_headers();
+        yourls_no_frame_header();
 		yourls_content_type_header( yourls_apply_filter( 'html_head_content-type', 'text/html' ) );
 		yourls_do_action( 'admin_headers', $context, $title );
 	}
@@ -725,6 +726,7 @@ function yourls_login_screen( $error_msg = '' ) {
 				yourls_do_action( 'login_form_bottom' );
 			?>
 			<p style="text-align: right;">
+			    <?php yourls_nonce_field('admin_login'); ?>
 				<input type="submit" id="submit" name="submit" value="<?php yourls_e( 'Login' ); ?>" class="button" />
 			</p>
 			<?php

programming_language: PHP
filename: functions.php
change_type: ModificationType.MODIFY
diff: @@ -276,7 +276,30 @@ function yourls_no_cache_headers() {
 }
 
 /**
- * Send a filerable content type header
+ * Send header to prevent display within a frame from another site (avoid clickjacking)
+ *
+ * This header makes it impossible for an external site to display YOURLS admin within a frame,
+ * which allows for clickjacking.
+ * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
+ * This said, the whole function is shuntable : legit uses of iframes should be still possible.
+ *
+ * @since 1.8.1
+ * @return void|mixed
+ */
+function yourls_no_frame_header() {
+    // Allow plugins to short-circuit the whole function
+    $pre = yourls_apply_filter( 'shunt_no_frame_header', false );
+    if ( false !== $pre ) {
+        return $pre;
+    }
+
+    if( !headers_sent() ) {
+        header( 'X-Frame-Options: SAMEORIGIN' );
+    }
+}
+
+/**
+ * Send a filterable content type header
  *
  * @since 1.7
  * @param string $type content type ('text/html', 'application/json', ...)

programming_language: PHP