文档章节

Laravel5.3之Two-Factor Authentication神器——Duo

botkenni
 botkenni
发布于 2016/11/16 09:53
字数 2121
阅读 27
收藏 0
Duo

说明:本文主要研究利用Duo来实现双重认证,Two-Factor Authentication就是除了username-password这种登录认证之外,还使用第二层安全认证,引用官网What is Two-Factor Authentication?的一句话:

A second layer of security to your login, in addition to your password.

这里,就是使用Duo来实现二次安全认证,保护程序防止被恶意者登录。需要实现二次登录认证的场景还是很多的,如登录云服务器AWS或Aliyun时只是账号密码登录是远远不够,安全性较差,如果登录AWS的private key被别人知道了,那恶意者也会登录到你的AWS,那就麻烦了,代码岂不暴露了;还有公司内部的一些后台网站,如果只是username-password这种基本认证也远不够安全,如果被别人知道了账号密码登陆进去那就泄露了公司秘密了,限制IP登录也挺麻烦的,那岂不是除了公司外其他地方不能访问内部网站了,如果想在家访问一个业务的后台就麻烦了。

使用Duo来做多一层保护会更安全,Duo的Web Application Protection工作原理如图:

上图描述的主要内容就是除了输入基本的账号密码认证外,还得经过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账号。

这里主要学习下如何利用Duo来Protect Web Application,这里假设Web程序是Laravel写的,看如何集成进Laravel中实现二次认证。假设由于业务需求,有一个后台Admin,并是username-password这种HTTP Basic Authentication的(很多时候username-password认证在公司内都是SSO{Single Sign On},多个系统只需要一套username-password,这个可以使用Atlassian Crowd来做,以后再聊这个Crowd)。

开发环境:Laravel5.3 + PHP7

Duo Account

进去Duo官网注册个账号先,Duo Pricing对个人使用不超过10个用户时是免费的,其余套餐的价格也很便宜。然后在手机端下载个Duo应用。最后使用Duo账号登录进后台,后台登录使用Push认证,这样Duo Mobile App就会收到Push Notification,选择Approve就会自动登录Duo 后台:

登录后台,创建一个Application获取keys,不同的Application有不同的keys,这样可以不同的Admin后台使用不同Application的keys,方便管理:

选择Web SDK,因为本文是学习使用Duo的SDK集成进Admin后台,来保护后台Admin程序:

这样就得到了名叫Web SDK的Application了,并得到了对应的Integration key,Secret key,API hostname这三个keys,后续集成SDK时需要这三个keys:

Two-Factor Authentication

把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.basicLaravel自带的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程序中。以后遇到好的技术再分享下,到时见。

© 著作权归作者所有

共有 人打赏支持
botkenni
粉丝 20
博文 409
码字总数 434882
作品 0
西城
程序员
私信 提问
Laravel5.3之Errors Tracking神器——Sentry

说明:Laravel之bootstrap源码解析中聊时提到过Sentry这个神器,并打算以后聊聊这款神器,本文主要就介绍这款Errors Tracking神器,Sentry官网有一句话个人觉得帅呆了: Stop hoping your us...

botkenni
2016/11/16
46
0
企业级开源单点登录解决方案 - CAS

CAS 是 Central Authentication Service 的缩写 —— 中央认证服务,一种独立开放指令协议,是 Yale 大学发起的一个企业级开源项目,旨在为 Web 应用系统提供一种可靠的 SSO 解决方案。 CAS ...

匿名
2018/11/22
0
0
电脑制造商更新程序竟不验证文件 存在严重安全隐患

     您的计算机之所以在软件更新过程中不会被劫持,很大程度上我们要感谢像微软这类软件公司,为了保障安全性,他们在操作系统和应用程序的更新上付出了巨大的努力。   与之相对的,P...

嘶吼RoarTalk
2018/09/29
0
0
轻松搭建CAS 5.x系列(8)-在CAS Server增加双因素认证(DUO版)

概述说明 为了让系统更加安全,很多登录会加入双因素认证。何为双因素,如果把登陆作为开一扇门的话,那就是在原来的锁上再加一把锁,第二锁用新的钥匙,这样安全系数就更加高了。 CAS是通过...

自助土豆
2018/08/11
0
0
cocos2d-x支持c++、js、lua开发

作者:左文 链接:https://www.zhihu.com/question/21130385/answer/21789568 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 纯属个人观点 1 Unity3d支...

壹峰
2017/10/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Go Timer实现原理剖析

简介 快速使用 操作介绍

恋恋美食
13分钟前
0
0
记录一个奇怪的问题

环境:jdk1.8虚拟机参数:-verbose:gc -XX:+PrintGCDetails -Xmx20m -Xms20m -Xmn10m -XX:SurvivorRatio=8  -XX:+HeapDumpOnOutOfMemoryError 可以看出,eden占8M却放不下6M数据,发生了......

暗中观察
41分钟前
1
0
创建多个git账号

实习开发中我们可能一个机子上配置多个git账号,如github.com,oschina.com 或者工作账号,私人账号,这时候就2个账号用一个key,肯定会冲突,有一个会提示没权限(账号和密码对应不上) ssh ...

echojson
44分钟前
1
0
rabbitmq安装教程

RabbitMQ有Windows与Linux版本的,这里先写Windows版本的安装。 以前安装软件总是在百度上找某某安装教程,结果能按照教程安装好的软件真的不多。想起先前以为大牛说的一句话,去官网按照官网...

em_aaron
今天
7
0
Android 贝塞尔曲线实践——波浪式运动

一、波浪效果如下 贝塞尔曲线自定义波浪效果的案例很多,同样方法也很简单,大多数和本案例一样使用二次贝塞尔曲线实现,同样还有一种是PathMeasure的方式,这里我们后续补充,先来看贝塞尔曲...

IamOkay
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部