通过电子邮件而不是SMS发送代码来实现帐户安全:Laravel 5.2

时间:2016-08-05 06:54:55

标签: php laravel laravel-5 laravel-5.1 laravel-5.2

当我们第一次登录我们的Gmail帐户或删除缓存和cookie后,我们会在窗口中输入发送到我们移动设备的代码。

我试图通过电子邮件而不是短信来实现这一点。以下是我实现此方法的方法。

I am following this link https://laravel.com/docs/5.2/session

并在数据库中创建Session table。我还可以在会话表记录中看到我的浏览器详细信息。我不确定这是否是正确的方法。

Gmail可以跟踪多个浏览器。这意味着如果我上次从Firefox登录并且这次从Chrome登录,那么我将再次被要求提供代码。展望未来,如果未删除缓存/ Cookie,我将不会要求我填写Chrome和Firefox的代码。

有人可以给我任何链接,解释如何在保存缓存/ cookie时为多个浏览器提供服务吗?这样我就可以发送安全码的电子邮件

3 个答案:

答案 0 :(得分:3)

你可以通过发一个额外的cookie(比如说browser_cookie)来记住已经过身份验证的浏览器。

实现:

创建下表(browser_management):

token (pk)| user_id (fk) | series_identifier

其中:

  • 令牌:向用户发出的哈希形式的令牌(使用bcrypt或类似的算法)(发给用户的令牌,本身是从适当大的空间中随机生成的密钥)< / p>

  • series_identifier :从适当大的空间中随机生成的密钥

每当用户登录时都会检查browser_cookie

案例1:用户首次登录。

考虑到用户首次登录browser_cookie将不会出现。因此,您将发送包含身份验证代码的电子邮件。

经过身份验证后,为tokenseries_identifier生成两个随机数。将散列tokenseries_identifier存储在browser_management表中,供user_id标识的用户使用。

另外,使用browser_cookietoken向用户发出series_identifier

案例2:用户下次重新登录。

现在,当同一用户下次登录时,请点击token,然后在browser_management表中找到带有散列token的条目。

如果找到,请检查user_idseries_identifier是否匹配。

案例2.1:匹配的条目:

允许用户进入系统而无需重新验证电子邮件代码。

生成另一个令牌,并将token中的cookie以及table替换为新<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class browser_management extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('browser_management', function (Blueprint $table) { $table->string('token'); $table->string('user_id'); $table->string('series_identifier'); $table->timestamps(); $table->primary('token'); $table->foreign('user_id')->references('id')->on('users'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('users'); } } 。 (这将降低会话劫持的风险)。

案例2.2:条目不匹配:

按照电子邮件身份验证的步骤进行操作,并通知用户可能的盗窃行为。(例如gmail通知新的浏览器登录)。

参考文献:

更新

示例代码:

<强>迁移:

<?php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;
use Cookies;

class Email_verification
{
    public function handle($request, Closure $next, $guard = null)
    {
        //retrieve $token from the user's cookie
        $token = $request->cookie('browser_cookie');

        //check id token is present
        if($token == null){
            //if token is not present allow the request to the email_verification
            return $next($request);
        }
        else{
            //Retrieve the series_identifier issued to the user
            $series_identifier = Auth::user()
                                    ->series_identifier(Hash::make($token))
                                    ->first()
                                    ->series_identifier;

            //Check if series_identifier matches            
            if($series_identifier != $request->cookie('series_identifier')){
                //if series_identifier does not match allow the request to the email_verification
                return $next($request);
            }
        }

       return redirect('/dashboard'); //replace this with your route for home page
    }
}

中间件:创建新的中间件

kernel.php

protected $routeMiddleware = [ 'email_verification' => \App\Http\Middleware\Email_verification::class, //your middlewares ];

中创建中间件的条目
// method to retrieve series_identifier related to token
public function series_identifier($token){
    return $this->hasMany(Browser_management::class)->where('token',$token);
}

//method to retriev the tokens related to user
public function tokens (){
    return $this->hasMany(Browser_management::class);
}

用户模型:将以下方法添加到您的用户模型

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;


class Browser_management extends Model
{
    protected $primaryKey = 'token';
    protected $fillable = array('token','series_identifier');

    public function User(){
        return $this->hasOne('App\Models\User');
    }    
}

Browser_management模型:创建一个代表browser_managements表的模型

public function getVerification(Request $request){
    //Create a random string to represent the token to be sent to user via email. 
    //You can use any string as we are going to hash it in our DB
    $token = str_random(16);

    //Generate random string to represent series_identifier
    $series_identifier = str_random(64);

    //Issue cookie to user with the generated series_identifier
    Cookie::queue('series_identifier', $series_identifier,43200,null,null,true,true);

    //Store the hashed token and series_identifier ini DB
    Auth::user()->tokens()->create(['token'=>Hash::make($token)]);

    //Your code to send an email for authentication

    //return the view with form asking for token
    return view('auth.email_verification');
}

public function postVerification(Request $request){
    //Retrieve the series_identifier issued to the user in above method
    $series_identifier = $request->cookie('series_identifier');

    //Retrieve the token associated with the series_identifier
    $token = Auth::user()
                ->tokens()
                ->where('series_identifier',$series_identifier)
                ->first()
                ->value('token');

    //Check if the user's token's hash matches our token entry
    if(Hash::check($request->token,$token)){
        // If token matched, issue the cookie with token id in it. Which we can use in future to authenticate the user
        Cookie::queue('token', $token,43200,null,null,true,true);
        return redirect('dashboard');
    }

    //If token did not match, redirect user bak to the form with error
    return redirect()->back()
                ->with('msg','Tokens did not match');
}

电子邮件验证方法:将以下方法添加到AuthController以处理电子邮件验证

Route::get('/auth/email_verification',`AuthController@getVerification')->middleware('email_verification');
Route::post('/auth/email_verification',`AuthController@postVerification')->middleware('email_verification');<br/>

路由:添加这些路由以处理电子邮件验证请求。我们还将添加email_verification中间件。

token

更新2:

关于gmail的流程..
我按照以下步骤:
1)登录gmail,然后进行两步验证。
2)退出
3)清除缓存link
4)再次登录

当我再次插入清除缓存后,它没有要求我进行两步验证。

但是,如果您清除Cookie ,则会要求进行两步验证。 的原因:
标识用户的所有用户数据(此处为keyUpPress)都存储在cookie中。如果您清除cookie,服务器将没有机制来识别用户。

更新3:

Gmail要求进行两步验证:
首先,Gmail或任何其他网站不会收到有关清除缓存的通知 给定here:

  

缓存只不过是硬盘上的一个地方了   浏览器保存下载后的内容以防万一   试。

现在,cookie是服务器发布的用于存储用户相关信息的小文本文件。给定here

  

Cookie的主要目的是识别用户并准备好   自定义网页或为您保存网站登录信息。

因此,基本上当您在浏览器中清除cookie时,Web服务器将不会获得任何用户数据。因此,用户将被视为访客,并将得到相应的处理。

答案 1 :(得分:1)

创建一个附加表(除了会话一)

这样的东西

UserId | UserAgent | IP

当他们去登录时,检查$_SERVER数组中的当前值。如果它在那里一切都很好,如果没有中断登录,并发送一个链接来确认新数据。您可能希望在原始登录时执行某种ajax以检查他们何时登录,然后一旦发生这种情况,请将其重定向到他们要去的地方。

有道理。

正如我在可维护性评论中所说,我会自己处理它而不使用任何第三方API,数据很容易验证。这部分相对简单,继续登录过程并不是那么多。

答案 2 :(得分:1)

OP,如果我清楚地了解你,你只想了解如何实现laravel会话表,这样你就可以在同一个浏览器中从同一个用户进行多次登录:

Schema::create('sessions', function ($table) {
    $table->string('id')->unique();
    $table->integer('user_id')->nullable();
    $table->string('ip_address', 45)->nullable();
    $table->text('user_agent')->nullable();
    $table->text('payload');
    $table->integer('last_activity');
});

虽然这个问题已经answered before here,但我会补充一点,您可以在实际登录方法中轻松实现此功能,而无需修改核心文件。

为此,您可以遵循以下逻辑

登录前,请手动检查请求用户代理标头是否与会话中的会话用户代理相同,即:

public function authenticate()
{
    $data = Input::all();
    $user = User::where('email', '=', $data['email'])->first();
    if($user != null)
    {
         //check if user agent different from session
         if($request->header('User-Agent') != session('user_agent'))
         {
             //do some extra login/password validation check
         }
         if(Auth::attempt($data))
         {
             //here you now may manually update the session ID on db
         }
    }
}

你必须做更多的工作,但我希望你能得到这个概念。