說明:本文主要研究利用Duo來實現雙重認證,Two-Factor Authentication就是除了username-password這種登陸認證以外,還使用第二層安全認證,引用官網What is Two-Factor Authentication?的一句話:javascript
A second layer of security to your login, in addition to your password.php
這裏,就是使用Duo來實現二次安全認證,保護程序防止被惡意者登陸。須要實現二次登陸認證的場景仍是不少的,如登陸雲服務器AWS或Aliyun時只是帳號密碼登陸是遠遠不夠,安全性較差,若是登陸AWS的private key被別人知道了,那惡意者也會登陸到你的AWS,那就麻煩了,代碼豈不暴露了;還有公司內部的一些後臺網站,若是隻是username-password這種基本認證也遠不夠安全,若是被別人知道了帳號密碼登錄進去那就泄露了公司祕密了,限制IP登陸也挺麻煩的,那豈不是除了公司外其餘地方不能訪問內部網站了,若是想在家訪問一個業務的後臺就麻煩了。css
使用Duo來作多一層保護會更安全,Duo的Web Application Protection工做原理如圖:html
上圖描述的主要內容就是除了輸入基本的帳號密碼認證外,還得通過Duo的二次認證。如在我司在登陸AWS雲時,除了private key認證外,還得必須通過Duo安全認證才能安全登陸AWS,Duo認證選擇的方式是Mobile Push Notification,這樣當有惡意者知道了我的的private key想要登陸AWS,我的的手機就會收到Duo Push Notification,只有我的在手機上選擇Approve才能登陸,並且也知道了private key被盜取了趕忙換一個key作補救措施。在登陸後臺時也是必須Duo認證才行。實際上,Duo還能集成進Github上,這樣登陸Github時也必須通過Duo認證,就算被知道了帳號密碼也不會被登陸我的的Github帳號。java
這裏主要學習下如何利用Duo來Protect Web Application,這裏假設Web程序是Laravel寫的,看如何集成進Laravel中實現二次認證。假設因爲業務需求,有一個後臺Admin,並是username-password這種HTTP Basic Authentication的(不少時候username-password認證在公司內都是SSO{Single Sign On},多個系統只須要一套username-password,這個可使用Atlassian Crowd來作,之後再聊這個Crowd)。jquery
開發環境:Laravel5.3 + PHP7
laravel
進去Duo官網註冊個帳號先,Duo Pricing對我的使用不超過10個用戶時是免費的,其他套餐的價格也很便宜。而後在手機端下載個Duo應用。最後使用Duo帳號登陸進後臺,後臺登陸使用Push認證,這樣Duo Mobile App就會收到Push Notification,選擇Approve就會自動登陸Duo 後臺:web
登陸後臺,建立一個Application獲取keys,不一樣的Application有不一樣的keys,這樣能夠不一樣的Admin後臺使用不一樣Application的keys,方便管理:redis
選擇Web SDK,由於本文是學習使用Duo的SDK集成進Admin後臺,來保護後臺Admin程序:bootstrap
這樣就獲得了名叫Web SDK的Application了,並獲得了對應的Integration key
,Secret key
,API hostname
這三個keys,後續集成SDK時須要這三個keys:
把Duo SDK集成進Laravel中實際上就是多加一個Middleware,這裏假設名爲auth.duo
,先作箇中間件:
php artisan make:middleware DuoTwoFactorAuthentication
而後寫上中間件代碼,首先通過username-password第一層認證(這裏假設是HTTP Basic Authentication),而後再是Duo Authentication第二層認證,最後認證經過再$next($request):
<?php namespace App\Http\Middleware; use App\Http\Controllers\TwoFactorAuthenticationController; use Auth; use Closure; use Illuminate\Http\Response; class DuoTwoFactorAuthentication { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @param null|string $guard * * @return mixed */ public function handle($request, Closure $next, $guard = null) { // HTTP Basic Authentication if (Auth::guard($guard)->guest()) { // Basic authentication is not set. return response('Unauthorized.', Response::HTTP_UNAUTHORIZED); } elseif ($request->session()->get(TwoFactorAuthenticationController::SESSION_KEY) == Auth::guard($guard)->user()->getAuthIdentifier()) { return $next($request); } else { // Duo Authentication // Basic authentication is set, but the duo middleware is not set. return redirect()->guest('/2fa'); } } }
並在\App\Http\Kernel中加上auth.duo
:
protected $routeMiddleware = [ 'auth' => \Illuminate\Auth\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'auth.crowd' => Middleware\CrowdAuthentication::class, 'auth.duo' => Middleware\DuoTwoFactorAuthentication::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => Middleware\RedirectIfAuthenticated::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, ];
而後寫上路由就行web.php,這裏是使用auth.basic
Laravel自帶的HTTP Basic Authentication(使用Crowd SSO登陸之後再聊):
//Route::group(['middleware' => 'auth.crowd'], function () { Route::group(['middleware' => 'auth.basic'], function () { Route::get('/2fa', 'TwoFactorAuthenticationController@get'); Route::post('/2fa', 'TwoFactorAuthenticationController@post'); Route::group(['middleware' => 'auth.duo'], function () { Route::get('/duo', function () { return 'Duo Authentication'; }); }); Route::get('/duo/users', function () { return view('users'); }); Route::get('/duo/accounts', function () { return view('accounts'); }); // and so on });
這樣Admin程序後臺路由是http://sentry.app:8888/duo(假設本地配置的host是sentry.app:8888),但須要通過HTTP Basic Authentication這個第一層認證,HTTP Basic Authentication就是根據輸入的username-password來查詢users表中有沒有對應的user,這裏先在users表中造一個,使用Laravel自帶的Auth Scaffold,而後使用Register功能往users表中插入一個user,這樣也方便:
php artisan make:auth
而後輸入http://sentry.app:8888/register往users表插入一個username: user@example.com
,password: lx1036
:
根據官方文檔Duo Web中說明,須要安裝一個package:
composer require duosecurity/duo_php
而後加上控制器TwoFactorAuthenticationController,這裏須要向session中寫入$user_id,這裏使用redis做爲session驅動,記得修改.env中SESSION_DRIVER=redis
:
php artisan make:controller TwoFactorAuthenticationController <?php namespace App\Http\Controllers; use Auth; use Duo\Web; use Illuminate\Http\Request; use Illuminate\Http\Response; class TwoFactorAuthenticationController extends Controller { const SESSION_KEY = 'auth.duo'; public function get() { return view('duo.2fa', [ 'host' => config('services.duo.host'), 'sig_request' => Web::signRequest( config('services.duo.integration_key'), config('services.duo.secret_key'), config('services.duo.application_key'), Auth::user()->getAuthIdentifier() ), 'post_action' => url('2fa'), ]); } public function post(Request $request) { $user_id = Web::verifyResponse( config('services.duo.integration_key'), config('services.duo.secret_key'), config('services.duo.application_key'), $request->input('sig_response') ); if ($user_id == Auth::user()->getAuthIdentifier()) { $request->session()->put(self::SESSION_KEY, $user_id); return redirect()->intended('/duo'); } else { abort(Response::HTTP_UNAUTHORIZED); } } } // config/services.php 'duo' => [ 'host' => env('DUO_HOST'), 'integration_key' => env('DUO_IKEY'), 'secret_key' => env('DUO_SKEY'), 'application_key' => env('DUO_AKEY'), ],
記得在.env文件中寫入DUO_HOST,DUO_IKEY,DUO_SKEY
這三個從Web SDK 這個Application中獲得的keys,DUO_AKEY根據官方文檔是我的生成的,這裏選擇Laravel的APP_KEY。
最後按照官方文檔的格式,把view頁面寫上:
// resources/views/duo/2fa.blade.php @extends('layouts.duo') @section('content') <div id="duo"> <h2>Two Factor Authentication</h2> <iframe id="duo_iframe" frameborder="0"> </iframe> <form method="post" id="duo_form"> {{csrf_field()}} </form> </div> @stop @section('js') <script type="application/javascript" src="{{asset('js/duo/Duo-Web-v2.min.js')}}"></script> <script type="application/javascript"> Duo.init({ 'host' : "{{$host}}", 'sig_request': "{{$sig_request}}", 'post_action': "{{$post_action}}" }); </script> <link rel="stylesheet" type="text/css" href="{{asset('css/duo/duo.css')}}"> @endsection // resources/views/layouts/duo.blade.php <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Duo: Two Factor Authentication</title> <!-- Bootstrap --> <link href="//cdn.bootcss.com/bootstrap/4.0.0-alpha.3/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container"> <section> @yield('content') </section> </div> @yield('js') <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <script src="http://cdn.bootcss.com/jquery/1.11.1/jquery.min.js"></script> <!-- Include all compiled plugins (below), or include individual files as needed --> <script src="//cdn.bootcss.com/bootstrap/4.0.0-alpha.3/js/bootstrap.min.js"></script> </body> </html> // public/css/duo/duo.css #duo_iframe { width: 100%; min-width: 304px; max-width: 620px; height: 330px; } #duo { align-content: center; margin: auto; }
其中,Duo-Web-v2.min.js是duosecurity/duo_php這個package裏就有的,拷貝過來就行。
而後輸入路由http://sentry.app:8888/duo會彈出Basic Authentication Form,輸入剛剛註冊的user@example.com
,lx1036
實現第一層認證後,再根據中間件DuoTwoFactorAuthentication的return redirect()->guest('/2fa');
邏輯就會跳轉到/2fa
頁面實現第二層認證:
選擇Send me a Push
後,手機端Duo APP就會就會收到Push Notification了,固然前提是手機端Duo已經登陸了。選擇Approve
後桌面端程序就自動跳轉到路由http://sentry.app:8888/duo,此次走的中間件DuoTwoFactorAuthentication
中邏輯是$request->session()->get(TwoFactorAuthenticationController::SESSION_KEY) == Auth::guard($guard)->user()->getAuthIdentifier()
,這樣程序就通過二次認證了,程序就進入登錄後的頁面,這裏只是簡單顯示Duo Authentication
:
It is working.
有了Duo這個神器,就很安全的實現二次認證了,這裏是展現瞭如何使用Web SDK來保護Web Application,須要編碼,還能夠在Duo後臺配置實現服務器登陸的二次認證,這些就是配置下就行,不須要編碼,固然Duo還有不少其餘集成來實現二次保護。使用這種Modern Security Protection總比粗暴的限制IP訪問來保護安全要高效的多,一切都是爲了自動化,爲了提升生產率
。Duo已經在我司RightCapital長時間使用了,用起來還很順手,值得推薦下。
總結:本文主要學習使用Duo這個神器來作Two Factor Authentication,並學習瞭如何使用Web SDK集成進Laravel程序中。之後遇到好的技術再分享下,到時見。
我司最近須要一名夥伴一塊兒共同航海去,有興趣速來Profile。
歡迎關注Laravel-China。