<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[56804] trunk: REST API: Correct parsing of password from Authorization header when processing Application Password credentials.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/56804">56804</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/56804","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>kadamwhite</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2023-10-09 14:47:57 +0000 (Mon, 09 Oct 2023)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>REST API: Correct parsing of password from Authorization header when processing Application Password credentials.

Exit early when parsing Application Password credentials if Authorization header value does not contain at least one colon. The `Authorization` Basic header must use a colon to separate the username and password components per RFC 7617, so a username-only string is malformed and should not be processed.

Split `Authorization` header only on the first colon, properly handling passwords containing colons.

Resolves PHP 8.0 warning when `list()` was called on an exploded credentials array containing only one element.

Props kalpeshh, shooper, sc0ttkclark, jrf, mukesh27, oglekler, nicolefurlan.
Fixes <a href="https://core.trac.wordpress.org/ticket/57512">#57512</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesloadphp">trunk/src/wp-includes/load.php</a></li>
<li><a href="#trunktestsphpunittestsauthphp">trunk/tests/phpunit/tests/auth.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpincludesloadphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/load.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/load.php    2023-10-09 11:21:30 UTC (rev 56803)
+++ trunk/src/wp-includes/load.php      2023-10-09 14:47:57 UTC (rev 56804)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -126,8 +126,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">        $token    = substr( $header, 6 );
</span><span class="cx" style="display: block; padding: 0 10px">        $userpass = base64_decode( $token );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        list( $user, $pass ) = explode( ':', $userpass );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // There must be at least one colon in the string.
+       if ( ! str_contains( $userpass, ':' ) ) {
+               return;
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        list( $user, $pass ) = explode( ':', $userpass, 2 );
+
</ins><span class="cx" style="display: block; padding: 0 10px">         // Now shove them in the proper keys where we're expecting later on.
</span><span class="cx" style="display: block; padding: 0 10px">        $_SERVER['PHP_AUTH_USER'] = $user;
</span><span class="cx" style="display: block; padding: 0 10px">        $_SERVER['PHP_AUTH_PW']   = $pass;
</span></span></pre></div>
<a id="trunktestsphpunittestsauthphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/auth.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/auth.php        2023-10-09 11:21:30 UTC (rev 56803)
+++ trunk/tests/phpunit/tests/auth.php  2023-10-09 14:47:57 UTC (rev 56804)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -844,4 +844,46 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'not allowed' => array( 'subscriber', false ),
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       /*
+        * @ticket 57512
+        * @covers ::wp_populate_basic_auth_from_authorization_header
+        */
+       public function tests_basic_http_authentication_with_username_and_password() {
+               // Header passed as "username:password".
+               $_SERVER['HTTP_AUTHORIZATION'] = 'Basic dXNlcm5hbWU6cGFzc3dvcmQ=';
+
+               wp_populate_basic_auth_from_authorization_header();
+
+               $this->assertSame( $_SERVER['PHP_AUTH_USER'], 'username' );
+               $this->assertSame( $_SERVER['PHP_AUTH_PW'], 'password' );
+       }
+
+       /*
+        * @ticket 57512
+        * @covers ::wp_populate_basic_auth_from_authorization_header
+        */
+       public function tests_basic_http_authentication_with_username_only() {
+               // Malformed header passed as "username" with no password.
+               $_SERVER['HTTP_AUTHORIZATION'] = 'Basic dXNlcm5hbWU=';
+
+               wp_populate_basic_auth_from_authorization_header();
+
+               $this->assertArrayNotHasKey( 'PHP_AUTH_USER', $_SERVER );
+               $this->assertArrayNotHasKey( 'PHP_AUTH_PW', $_SERVER );
+       }
+
+       /*
+        * @ticket 57512
+        * @covers ::wp_populate_basic_auth_from_authorization_header
+        */
+       public function tests_basic_http_authentication_with_colon_in_password() {
+               // Header passed as "username:pass:word" where password contains colon.
+               $_SERVER['HTTP_AUTHORIZATION'] = 'Basic dXNlcm5hbWU6cGFzczp3b3Jk';
+
+               wp_populate_basic_auth_from_authorization_header();
+
+               $this->assertSame( $_SERVER['PHP_AUTH_USER'], 'username' );
+               $this->assertSame( $_SERVER['PHP_AUTH_PW'], 'pass:word' );
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre>
</div>
</div>

</body>
</html>