Cleanup login/reset functionality, address security issue with 2FA pathways

This commit is contained in:
Dane Everitt 2018-04-07 16:17:51 -05:00
parent eade81f89b
commit c3e462ab2f
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
11 changed files with 158 additions and 39 deletions

View file

@ -106,6 +106,10 @@ abstract class AbstractLoginController extends Controller
$this->getField($request->input('user')) => $request->input('user'),
]);
if ($request->route()->named('auth.checkpoint')) {
throw new DisplayException(trans('auth.checkpoint_failed'));
}
throw new DisplayException(trans('auth.failed'));
}

View file

@ -3,7 +3,7 @@
namespace Pterodactyl\Http\Controllers\Auth;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Password;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Events\Auth\FailedPasswordReset;
@ -18,9 +18,9 @@ class ForgotPasswordController extends Controller
*
* @param \Illuminate\Http\Request
* @param string $response
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\JsonResponse
*/
protected function sendResetLinkFailedResponse(Request $request, $response): RedirectResponse
protected function sendResetLinkFailedResponse(Request $request, $response): JsonResponse
{
// As noted in #358 we will return success even if it failed
// to avoid pointing out that an account does or does not
@ -34,9 +34,9 @@ class ForgotPasswordController extends Controller
* Get the response for a successful password reset link.
*
* @param string $response
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
* @return \Illuminate\Http\JsonResponse
*/
protected function sendResetLinkResponse($response)
protected function sendResetLinkResponse($response): JsonResponse
{
return response()->json([
'status' => trans($response),

View file

@ -10,9 +10,8 @@ class LoginCheckpointController extends AbstractLoginController
{
/**
* Handle a login where the user is required to provide a TOTP authentication
* token. In order to add additional layers of security, users are not
* informed of an incorrect password until this stage, forcing them to
* provide a token on each login attempt.
* token. Once a user has reached this stage it is assumed that they have already
* provided a valid username and password.
*
* @param \Pterodactyl\Http\Requests\Auth\LoginCheckpointRequest $request
* @return \Illuminate\Http\JsonResponse
@ -28,7 +27,7 @@ class LoginCheckpointController extends AbstractLoginController
return $this->sendFailedLoginResponse($request);
}
if (! array_get($cache, 'valid_credentials') || array_get($cache, 'request_ip') !== $request->ip()) {
if (array_get($cache, 'request_ip') !== $request->ip()) {
return $this->sendFailedLoginResponse($request, $user);
}
@ -40,7 +39,7 @@ class LoginCheckpointController extends AbstractLoginController
return $this->sendFailedLoginResponse($request, $user);
}
$this->authManager->guard()->login($user, true);
$this->auth->guard()->login($user, true);
return $this->sendLoginResponse($request);
}

View file

@ -33,22 +33,26 @@ class LoginController extends AbstractLoginController
return $this->sendFailedLoginResponse($request);
}
$validCredentials = password_verify($request->input('password'), $user->password);
// Ensure that the account is using a valid username and password before trying to
// continue. Previously this was handled in the 2FA checkpoint, however that has
// a flaw in which you can discover if an account exists simply by seeing if you
// can proceede to the next step in the login process.
if (! password_verify($request->input('password'), $user->password)) {
return $this->sendFailedLoginResponse($request, $user);
}
// If the user is using 2FA we do not actually log them in at this step, we return
// a one-time token to link the 2FA credentials to this account via the UI.
if ($user->use_totp) {
$token = str_random(128);
$this->cache->put($token, [
'user_id' => $user->id,
'valid_credentials' => $validCredentials,
'request_ip' => $request->ip(),
], 5);
return response()->json(['complete' => false, 'token' => $token]);
}
if (! $validCredentials) {
return $this->sendFailedLoginResponse($request, $user);
}
$this->auth->guard()->login($user, true);
return response()->json(['complete' => true]);