From 288ee1a258d67170d024a3e12c3780f23fb66419 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Thu, 10 Dec 2015 21:58:17 -0500 Subject: [PATCH] Improved TOTp handling in login. Cleaned up the code a bit, also checks TOTP before attemping to verify user. This addresses the potential for an attacker to try at a password and/or confirm that the password is correct unless they have a valid TOTP code for the request. A failed TOTP response will trigger a throttle count on the login as well. --- app/Http/Controllers/Auth/AuthController.php | 138 +++++++++---------- app/Http/Routes/AuthRoutes.php | 12 +- resources/lang/en/auth.php | 1 + resources/lang/en/strings.php | 1 - resources/views/auth/login.blade.php | 62 +++++---- 5 files changed, 113 insertions(+), 101 deletions(-) diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index 9609ec89a..e3afe1228 100644 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -4,8 +4,9 @@ namespace Pterodactyl\Http\Controllers\Auth; use Pterodactyl\Models\User; -use Validator; use Auth; +use Alert; +use Validator; use Pterodactyl\Http\Controllers\Controller; use PragmaRX\Google2FA\Google2FA; @@ -28,73 +29,6 @@ class AuthController extends Controller use AuthenticatesAndRegistersUsers, ThrottlesLogins; - /** - * Handle a login request to the application. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response - */ - public function postLogin(Request $request) - { - $this->validate($request, [ - $this->loginUsername() => 'required', 'password' => 'required', - ]); - - $throttles = $this->isUsingThrottlesLoginsTrait(); - - if ($throttles && $this->hasTooManyLoginAttempts($request)) { - return $this->sendLockoutResponse($request); - } - - $credentials = $this->getCredentials($request); - - if (Auth::attempt($credentials, $request->has('remember'))) { - if(User::select('id')->where('email', $request->input('email'))->where('use_totp', 1)->exists()) { - $validator = Validator::make($request->all(), [ - 'totp_token' => 'required|numeric' - ]); - - if($validator->fails()) { - Auth::logout(); - return redirect('auth/login')->withErrors($validator)->withInput(); - } - - $google2fa = new Google2FA(); - - if($google2fa->verifyKey(User::where('email', $request->input('email'))->first()->totp_secret, $request->input('totp_token'))) { - return $this->handleUserWasAuthenticated($request, $throttles); - } else { - Auth::logout(); - $validator->errors()->add('field', trans('validation.welcome')); - return redirect('auth/login')->withErrors($validator)->withInput(); - } - } else { - return $this->handleUserWasAuthenticated($request, $throttles); - } - } - - if ($throttles) { - $this->incrementLoginAttempts($request); - } - - return redirect($this->loginPath()) - ->withInput($request->only($this->loginUsername(), 'remember')) - ->withErrors([ - $this->loginUsername() => $this->getFailedLoginMessage(), - ]); - } - - /** - * Check if the provided user has TOTP enabled. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response - */ - public function checkTotp(Request $request) - { - return response()->json(User::select('id')->where('email', $request->input('email'))->where('use_totp', 1)->first()); - } - /** * Post-Authentication redirect location. * @@ -121,7 +55,7 @@ class AuthController extends Controller * * @var integer */ - protected $maxLoginAttempts = 5; + protected $maxLoginAttempts = 3; /** * Create a new authentication controller instance. @@ -162,4 +96,70 @@ class AuthController extends Controller ]); } + /** + * Handle a login request to the application. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function postLogin(Request $request) + { + + $this->validate($request, [ + 'email' => 'required|email', + 'password' => 'required', + ]); + + $throttled = $this->isUsingThrottlesLoginsTrait(); + if ($throttled && $this->hasTooManyLoginAttempts($request)) { + return $this->sendLockoutResponse($request); + } + + $G2FA = new Google2FA(); + $user = User::select('use_totp', 'totp_secret')->where('email', $request->input($this->loginUsername()))->first(); + + // Verify TOTP Token was Valid + if($user->use_totp === 1) { + if(!$G2FA->verifyKey($user->totp_secret, $request->input('totp_token'))) { + + if ($throttled) { + $this->incrementLoginAttempts($request); + } + + Alert::danger(trans('auth.totp_failed'))->flash(); + return redirect()->route('auth.login')->withInput($request->only('email', 'remember')); + + } + } + + // Attempt to Login + if (Auth::attempt([ + 'email' => $request->input('email'), + 'password' => $request->input('password') + ], $request->has('remember'))) { + return $this->handleUserWasAuthenticated($request, $throttled); + } + + if ($throttled) { + $this->incrementLoginAttempts($request); + } + + return redirect()->route('auth.login') + ->withInput($request->only('email', 'remember')) + ->withErrors([ + 'email' => $this->getFailedLoginMessage(), + ]); + } + + /** + * Check if the provided user has TOTP enabled. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function checkTotp(Request $request) + { + return response()->json(User::select('id')->where('email', $request->input('email'))->where('use_totp', 1)->first()); + } + } diff --git a/app/Http/Routes/AuthRoutes.php b/app/Http/Routes/AuthRoutes.php index 027ec809f..236144552 100644 --- a/app/Http/Routes/AuthRoutes.php +++ b/app/Http/Routes/AuthRoutes.php @@ -10,20 +10,22 @@ class AuthRoutes { public function map(Router $router) { $router->group(['prefix' => 'auth'], function () use ($router) { + $router->get('login', [ 'as' => 'auth.login', 'uses' => 'Auth\AuthController@getLogin' ]); - $router->post('login/totp', [ 'as' => 'auth.login.totp', 'uses' => 'Auth\AuthController@checkTotp' ]); - $router->post('login', [ 'as' => 'auth.login.submit', 'uses' => 'Auth\AuthController@postLogin' ]); + $router->post('login', [ 'uses' => 'Auth\AuthController@postLogin' ]); + $router->post('login/totp', [ 'uses' => 'Auth\AuthController@checkTotp' ]); + $router->get('password', [ 'as' => 'auth.password', 'uses' => 'Auth\PasswordController@getEmail' ]); $router->post('password', [ 'as' => 'auth.password.submit', 'uses' => 'Auth\PasswordController@postEmail' ], function () { return redirect('auth/password')->with('sent', true); }); - + $router->post('password/verify', [ 'uses' => 'Auth\PasswordController@postReset' ]); $router->get('password/verify/{token}', [ 'as' => 'auth.verify', 'uses' => 'Auth\PasswordController@getReset' ]); - $router->post('password/verify', [ 'as' => 'auth.verify.submit', 'uses' => 'Auth\PasswordController@postReset' ]); $router->get('logout', [ 'as' => 'auth.logout', 'uses' => 'Auth\AuthController@getLogout' ]); + }); } -} \ No newline at end of file +} diff --git a/resources/lang/en/auth.php b/resources/lang/en/auth.php index 87fa9e453..31d3515f7 100644 --- a/resources/lang/en/auth.php +++ b/resources/lang/en/auth.php @@ -21,5 +21,6 @@ return [ 'sendlink' => 'Send Password Reset Link', 'emailsent' => 'Your password reset email is on its way.', 'remeberme' => 'Remeber Me', + 'totp_failed' => 'The TOTP token provided was invalid. Please ensure that the token generated by your device was valid.' ]; diff --git a/resources/lang/en/strings.php b/resources/lang/en/strings.php index 9ccb615d2..6b452c4a6 100644 --- a/resources/lang/en/strings.php +++ b/resources/lang/en/strings.php @@ -12,7 +12,6 @@ return [ 'password' => 'Password', 'email' => 'Email', 'whoops' => 'Whoops', - 'failed' => 'Your request could not be processed. Please try again later.', 'success' => 'Success', 'location' => 'Location', 'node' => 'Node', diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index 971d56415..5df4e1d0f 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -24,6 +24,14 @@ @endif + @foreach (Alert::getMessages() as $type => $messages) + @foreach ($messages as $message) + + @endforeach + @endforeach
@@ -76,33 +84,35 @@
@endsection