8000 [Security] Fixed digest authentication · symfony/symfony@c067586 · GitHub
[go: up one dir, main page]

Skip to content

Commit c067586

Browse files
Vincent Simoninsstok
authored andcommitted
[Security] Fixed digest authentication
Digest authentication fail if digest parameters contains `=` character or `, ` string.
1 parent cb00411 commit c067586

File tree

2 files changed

+116
-5
lines changed

2 files changed

+116
-5
lines changed

src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,12 @@ class DigestData
141141
public function __construct($header)
142142
{
143143
$this->header = $header;
144-
$parts = preg_split('/, /', $header);
144+
preg_match_all('/(\w+)=("([^"]+)"|([^\s,$]+))/', $header, $matches, PREG_SET_ORDER);
145145
$this->elements = array();
146-
foreach ($parts as $part) {
147-
list($key, $value) = explode('=', $part);
148-
$this->elements[$key] = '"' === $value[0] ? substr($value, 1, -1) : $value;
146+
foreach ($matches as $match) {
147+
if (isset($match[1]) && isset($match[3])) {
148+
$this->elements[$match[1]] = isset($match[4]) ? $match[4] : $match[3];
149+
}
149150
}
150151
}
151152

@@ -188,7 +189,7 @@ public function validateAndDecode($entryPointKey, $expectedRealm)
188189
$this->nonceExpiryTime = $nonceTokens[0];
189190

190191
if (md5($this->nonceExpiryTime.':'.$entryPointKey) !== $nonceTokens[1]) {
191-
new BadCredentialsException(sprintf('Nonce token compromised "%s".', $nonceAsPlainText));
192+
throw new BadCredentialsException(sprintf('Nonce token compromised "%s".', $nonceAsPlainText));
192193
}
193194
}
194195

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Security\Tests\Http\Firewall;
13+
14+
use Symfony\Component\Security\Http\Firewall\DigestData;
15+
16+
class DigestDataTest extends \PHPUnit_Framework_TestCase
17+
{
18+
public function setUp()
19+
{
20+
class_exists('Symfony\Component\Security\Http\Firewall\DigestAuthenticationListener', true);
21+
}
22+
23+
public function testGetResponse()
24+
{
25+
$digestAuth = new DigestData(
26+
'username="user", realm="Welcome, robot!", ' .
27+
'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", ' .
28+
'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", ' .
29+
'response="b52938fc9e6d7c01be7702ece9031b42"'
30+
);
31+
32+
$this->assertEquals('b52938fc9e6d7c01be7702ece9031b42', $digestAuth->getResponse());
33+
}
34+
35+
public function testGetUsername()
36+
{
37+
$digestAuth = new DigestData(
38+
'username="user", realm="Welcome, robot!", ' .
39+
'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", ' .
40+
'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", ' .
41+
'response="b52938fc9e6d7c01be7702ece9031b42"'
42+
);
43+
44+
$this->assertEquals('user', $digestAuth->getUsername());
45+
}
46+
47+
public function testValidateAndDecode()
48+
{
49+
$time = microtime(true);
50+
$key = 'ThisIsAKey';
51+
$nonce = base64_encode($time . ':' . md5($time . ':' . $key));
52+
53+
$digestAuth = new DigestData(
54+
'username="user", realm="Welcome, robot!", nonce="' . $nonce . '", ' .
55+
'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", ' .
56+
'response="b52938fc9e6d7c01be7702ece9031b42"'
57+
);
58+
59+
try {
60+
$digestAuth->validateAndDecode($key, 'Welcome, robot!');
61+
} catch (\Exception $e) {
62+
$this->fail(sprintf('testValidateAndDecode fail with message: %s', $e->getMessage()));
63+
}
64+
}
65+
66+
public function testCalculateServerDigest()
67+
{
68+
$username = 'user';
69+
$realm = 'Welcome, robot!';
70+
$password = 'pass,word=password';
71+
$time = microtime(true);
72+
$key = 'ThisIsAKey';
73+
$nonce = base64_encode($time . ':' . md5($time . ':' . $key));
74+
$nc = '00000001';
75+
$cnonce = 'MDIwODkz';
76+
$qop = 'auth';
77+
$method = 'GET';
78+
$uri = '/path/info?p1=5&p2=5';
79+
80+
$response = md5(
81+
md5($username . ':' . $realm . ':' . $password) .
82+
':' . $nonce . ':' . $nc . ':' . $cnonce . ':' . $qop . ':' . md5($method . ':' . $uri)
83+
);
84+
85+
$digest = sprintf('username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc="%s", qop="%s", response="%s"',
86+
$username, $realm, $nonce, $uri, $cnonce, $nc, $qop, $response
87+
);
88+
89+
$digestAuth = new DigestData($digest);
90+
91+
$this->assertEquals($digestAuth->getResponse(), $digestAuth->calculateServerDigest($password, $method));
92+
}
93+
94+
public function testIsNonceExpired()
95+
{
96+
$time = microtime(true) + 10;
97+
$key = 'ThisIsAKey';
98+
$nonce = base64_encode($time . ':' . md5($time . ':' . $key));
99+
100+
$digestAuth = new DigestData(
101+
'username="user", realm="Welcome, robot!", nonce="' . $nonce . '", ' .
102+
'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", ' .
103+
'response="b52938fc9e6d7c01be7702ece9031b42"'
104+
);
105+
106+
$digestAuth->validateAndDecode($key, 'Welcome, robot!');
107+
108+
$this->assertFalse($digestAuth->isNonceExpired());
109+
}
110+
}

0 commit comments

Comments
 (0)
0