说明:本文主要研究利用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
web
进去Duo官网注册个帐号先,Duo Pricing对我的使用不超过10个用户时是免费的,其他套餐的价格也很便宜。而后在手机端下载个Duo应用。最后使用Duo帐号登陆进后台,后台登陆使用Push认证,这样Duo Mobile App就会收到Push Notification,选择Approve就会自动登陆Duo 后台:redis
登陆后台,建立一个Application获取keys,不一样的Application有不一样的keys,这样能够不一样的Admin后台使用不一样Application的keys,方便管理:bootstrap
选择Web SDK,由于本文是学习使用Duo的SDK集成进Admin后台,来保护后台Admin程序:api
这样就获得了名叫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程序中。之后遇到好的技术再分享下,到时见。