From c3e462ab2f508952bbf7ca6f18ef02bae84e5063 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 7 Apr 2018 16:17:51 -0500 Subject: [PATCH] Cleanup login/reset functionality, address security issue with 2FA pathways --- .../Auth/AbstractLoginController.php | 4 ++ .../Auth/ForgotPasswordController.php | 10 ++-- .../Auth/LoginCheckpointController.php | 9 ++- app/Http/Controllers/Auth/LoginController.php | 16 ++++-- .../components/auth/ForgotPassword.vue | 55 ++++++++++++++++--- .../scripts/components/auth/LoginForm.vue | 26 +++++---- .../pterodactyl/styles/components/buttons.css | 7 ++- .../styles/components/spinners.css | 52 ++++++++++++++++++ resources/assets/pterodactyl/styles/main.css | 1 + resources/lang/en/auth.php | 3 +- routes/auth.php | 14 +++-- 11 files changed, 158 insertions(+), 39 deletions(-) create mode 100644 resources/assets/pterodactyl/styles/components/spinners.css diff --git a/app/Http/Controllers/Auth/AbstractLoginController.php b/app/Http/Controllers/Auth/AbstractLoginController.php index eac305315..f36a8c231 100644 --- a/app/Http/Controllers/Auth/AbstractLoginController.php +++ b/app/Http/Controllers/Auth/AbstractLoginController.php @@ -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')); } diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php index f35be6d37..edec72307 100644 --- a/app/Http/Controllers/Auth/ForgotPasswordController.php +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -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), diff --git a/app/Http/Controllers/Auth/LoginCheckpointController.php b/app/Http/Controllers/Auth/LoginCheckpointController.php index cacd0da1f..be6357516 100644 --- a/app/Http/Controllers/Auth/LoginCheckpointController.php +++ b/app/Http/Controllers/Auth/LoginCheckpointController.php @@ -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); } diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 22d6f4686..91c49e481 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -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]); diff --git a/resources/assets/pterodactyl/scripts/components/auth/ForgotPassword.vue b/resources/assets/pterodactyl/scripts/components/auth/ForgotPassword.vue index a252dab94..71b058dd8 100644 --- a/resources/assets/pterodactyl/scripts/components/auth/ForgotPassword.vue +++ b/resources/assets/pterodactyl/scripts/components/auth/ForgotPassword.vue @@ -1,9 +1,19 @@