-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Solution to Issue #3126 (symfony/symfony-docs) #3134
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
you could make the diff shorter by simply switching |
Yes I know, but following Robert C. Martin (Uncle Bob) principles in "Clean Code" I have chosen the way clearest and intuitive ;-) |
@@ -240,7 +240,7 @@ the ``PasswordDigest`` header value matches with the user's password. | |||
} | |||
|
|||
// Validate nonce is unique within 5 minutes | |||
if (file_exists($this->cacheDir.'/'.$nonce) && file_get_contents($this->cacheDir.'/'.$nonce) + 300 > time()) { | |||
if (file_exists($this->cacheDir.'/'.$nonce) && (time() - file_get_contents($this->cacheDir . '/' . $nonce) ) > 300) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the space between the two closing parentheses should be removed
I think this is wrong, but one way or another, there's clearly been some confusion on this little line. See #1602 for why it looks how it looks now. I've never used WSSE, but the purpose of the nonce (as I understand it) is to prevent replay attacks. See http://publib.boulder.ibm.com/infocenter/wsdoc400/v6r0/index.jsp?topic=/com.ibm.websphere.iseries.doc/info/ae/ae/cwbs_noncev6.html.
If I understand this all correctly, I believe the new code is wrong, which is basically: // Expire timestamp after 5 minutes
if (time() - strtotime($created) > 300) {
return false;
}
if (file_exists($this->cacheDir.'/'.$nonce) && (time() - file_get_contents($this->cacheDir . '/' . $nonce) ) > 300) {
throw new NonceExpiredException('Previously used nonce detected');
} A few cases, using a simple UNIX timestamp of around 1000 for simplicity.
So as I see it, the purpose of this line is to make sure that the nonce can only be used once... until it is regenerated again later. Honestly, it's still a bit fuzzy for me, and checking the times as we are now seems very obtuse (and hence why there's confusion). What do you guys think? Much better than discussing this (because it's really not a Symfony-topic, and trying to logic out security is never a good idea) would be to find a solid implementation and copy it. Does anyone know of one... in any language? Thanks! |
Ryan, you've summed it up nicely and are dead on. The whole purpose of the So the existing code says "IF Nonce-X exists, AND this request is within five minutes of when Nonce-X was used, throw an exception. Otherwise, let it through and set Nonce-X to the current timestamp." This PR does the opposite. It says "If Nonce-X exists, AND this request is AFTER five minutes of when Nonce-X was used, throw an exception". So it's wrong. If you're using something like memcache, this would look a lot simpler, as you'd just be setting Nonce-X to expire after 5 minutes, and there'd be no need for the logic above. But we're not that smart in this example :P |
... Maybe we'd be better off changing to OAuth? :) |
Hi guys! I've added some small comments at sha: 6833c30. I want this part to be understandable, however it's also something specific to WSSE, and this post is meant to teach people about Symfony's custom authentication. If you'd like to make some improvements on mine change, please do! Thanks! |
I like it — On Sat, Nov 16, 2013 at 3:52 PM, Ryan Weaver notifications@github.com
|
public function authenticate(TokenInterface $token) {
$user = $this->userProvider->loadUserByUsername($token->getUsername());
if ($user && $this->validateDigest($token->digest, $token->nonce, $token->created, $user->getPassword())) {
$authenticatedToken = new WsseUserToken($user->getRoles());
$authenticatedToken->setUser($user);
return $authenticatedToken;
}
throw new AuthenticationException('The WSSE authentication failed.');
} how to prevent attack while we load user from database first? |
@weburnit what do you mean ? |
whatever the validateDigest is, we always load $user from database which costs amount of environment! |
well, you cannot validate the digest without loading the hashed password from the DB to compare it. Look at the arguments of |
Hi guys! @weaverryan My example $currentTime = time();
$lifetime = 300; // Seconds
$cacheDir = '/tmp';
// Check that the timestamp has not expired
if (($currentTime < strtotime($created) - $lifetime) || ($currentTime > strtotime($created) + $lifetime)) {
throw new AuthenticationException('Created timestamp is not valid.');
}
$nonceDecoded = base64_decode($nonce);
$nonceFilePath = $cacheDir.'/'.$nonceDecoded;
// Validate that the nonce is *not* used in the last 5 minutes
// if it has, this could be a replay attack
if (file_exists($nonceFilePath) && (((int) file_get_contents($nonceFilePath)) + $lifetime) > $currentTime) {
throw new NonceExpiredException('Digest nonce has expired.');
}
if (!is_dir($this->cacheDir)) {
mkdir($this->cacheDir, 0777, true);
}
file_put_contents($nonceFilePath, $currentTime, LOCK_EX);
// Valid the secret
$expected = base64_encode(sha1($nonceDecoded.$created.$secret, true));
return hash_equals($expected, $digest); |
Explained here: #3126