Nette Http 3.2: change access to credentials

13 days ago by David Grudl  

A new version of Nette Http 3.2 has been released, which includes a backwards incompatible change that was made for security reasons. It concerns the way to obtain a username and password when logging in via HTTP Basic Authentication, the oldest technique performed directly by the browser.

Whereas previously the data were returned by methods of the Nette\Http\Url class, now the new Nette\Http\Request::getBasicCredentials() method is used to do this:

// $request is a Nette\Http\Request object

// previously
$url = $request->getUrl();
$user = $url->getUser();
$password = $url->getPassword();

// since nette/http 3.2
[$user, $password] = $request->getBasicCredentials();
// $url->getUser() and $url->getPassword() now return ''

The reason for this change is purely security. Because the username and password are no longer part of the Url object, the risk that they will be disclosed when the address is written out has disappeared (i.e. echo $url).

Access to name and password via $request->getUrl() has been used in Nette since the beginning. Firstly, the Url class already had the corresponding methods, but also in browsers it is possible to pass the username and password directly in the address as http://username:password@example.com, so such a solution was offered. Today's browsers limit this functionality in various ways because it has been misused for nefarious purposes. For example, attackers have lured users to https://YourBank.com@HackersSite.com/, which at first glance looks like the real YourBank.com address, but the first part of the URL is actually a username.

But back to Nette. The risk of exposing login credentials was directly preceded by the Url class, which simply did not output them:

// Nette 2.x (and also 0.x)
$url = new Nette\Http\Url('http://user:pass@example.org');
echo $url; // http://example.org

Over time, however, there were requests to change the behavior because it was confusing to programmers. Starting with Nette Http 3.0, credentials are thus printed out:

// Nette 3.x
$url = new Nette\Http\Url('http://user:pass@example.org');
echo $url; // http://user:pass@example.org

At the same time, instead of the original $request->getUrl(), presenters started working with a copy without credentials, $request->getUrl()->withoutUserInfo(). However, and this is the reason for the current change, the programmers may not have been aware that printing out the original $request->getUrl() had the risk of disclosing credentials. Therefore, as of Nette Http 3.2, credentials are not passed to the Url object at all. Thus, printing $request->getUrl() is completely safe.

Instead, they are passed via the Authorization header, which contents can be decoded by the new $request->getBasicCredentials() method:

// Nette Http 3.2
$request->getHeader('Authorization'); // 'Basic dXNlcjpwYXNzd29yZA=='
$request->getBasicCredentials(); // ['user', 'password']

If the browser does not send login credentials or if another authentication method is used (e.g. Digest Access Authentication), method getBasicCredentials() returns null.