通过在电子邮件而不是短信中发送代码来确保帐户安全:Laravel 5.2
Account security by Sending code in email instead of SMS: Laravel 5.2
当我们第一次登录我们的 gmail 帐户或删除缓存和 cookie 后,我们会收到 window 以输入发送到我们手机的代码。
我正在尝试通过电子邮件而不是短信来实现这一点。下面是我实现这个的方法。
I am following this link : https://laravel.com/docs/5.2/session
并在数据库中创建一个 Session table
。我还可以在 Session Table 记录中看到我的浏览器详细信息。我不确定这是否是正确的方法。
Gmail 提供了跟踪多个浏览器的功能。这意味着如果我上次从 Firefox 登录,这次从 Chrome 登录,那么我将再次被要求输入密码。今后,如果 cache/cookie 未被删除,我将不会被要求填写 Chrome 和 Firefox 的代码。
有人可以给我任何 link 来解释如何在 cache/cookie 保存时为多个浏览器提供准备吗?这样我就可以发送电子邮件获取安全码
另外创建一个 table(除了会话 1)
类似
用户 ID |用户代理 | IP
当他们登录时,根据他们在 $_SERVER
数组中的当前值进行检查。如果它在那里一切都很好,如果没有中断登录,并向他们发送 link 以确认新数据。您可能想要对原始登录执行某种 ajax 以检查他们何时登录,然后一旦发生这种情况就重定向到他们要去的地方。
有道理。
正如我在可维护性的评论中所说,我会自己处理它而不使用任何第三方 API,数据很容易验证。那部分比较琐碎,继续登录过程就不多了。
您可以通过发出一个额外的 cookie(比方说 browser_cookie)来记住已经通过身份验证的浏览器来实现这一点。
实施:
创建以下 table (browser_management) :
token (pk)| user_id (fk) | series_identifier
其中:
token :发给用户的令牌的散列形式(使用 bcrypt 或类似算法)(发给用户的令牌本身是不可猜测的随机生成的密钥从适当大 space)
series_identifier :从适当大的 space
随机生成的不可猜测的密钥
每当用户登录时检查browser_cookie
。
案例 1: 用户是第一次登录。
考虑到用户是第一次登录 browser_cookie
不会出现。因此,您将发送一封包含验证码的电子邮件。
身份验证后,为 token
和 series_identifier
分别生成两个随机数。将哈希 token
和 series_identifier
存储在 browser_management
table 中,用于由 user_id
.
标识的用户
此外,使用 token
和 series_identifier
向用户发出 browser_cookie
。
情况二:用户下次重新登录。
现在,当同一用户下次登录时,取 token
并在 browser_management
table 中找到具有散列 token
.[=46 的条目=]
如果找到,请检查 user_id
和 series_identifier
是否匹配。
案例 2.1: 匹配的条目:
允许用户进入系统无需重新验证邮箱密码。
生成另一个令牌并将 cookie
中的 token
和 table
替换为新令牌。 (这将降低会话劫持的风险)。
案例 2.2: 条目不匹配:
按照电子邮件身份验证的步骤并通知用户可能的盗窃。(就像 gmail 通知新的浏览器登录一样)。
参考文献:
- Improved Persistent Login Cookie Best Practice
- What is the best way to implement “remember me” for a website?
更新:
示例代码:
迁移:
<?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');
}
}
中间件:创建一个新的中间件
<?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);
}
Browser_management 模型: 创建一个模型来表示 browser_managements table
<?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');
}
}
电子邮件验证方法:将以下方法添加到您的 AuthController 以处理电子邮件验证
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');
}
路由: 添加这些路由以处理电子邮件验证请求。我们还将向其中添加 email_verification 中间件。
Route::get('/auth/email_verification',`AuthController@getVerification')->middleware('email_verification');
Route::post('/auth/email_verification',`AuthController@postVerification')->middleware('email_verification');<br/>
更新 2:
关于gmail的流量..
我遵循了以下步骤:
1) 登录 gmail,然后进行两步验证。
2)注销
3)清除缓存link
4)重新登录
当我再次登录时,清除缓存后,它没有要求我进行两步验证。
不过,如果您清除 cookie,它会要求进行两步验证。
原因:
所有识别用户的用户数据(此处 token
)都存储在 cookie 中。如果清除cookies,服务器将没有机制来识别用户。
更新 3:
Gmail 请求两步验证:
首先,Gmail 或任何其他网站都不会收到有关清除缓存的通知
如给定 here:
The cache is nothing more than a place on your hard disk where the
browser keeps things that it downloaded once in case they’re needed
again.
现在,cookies是服务器发布的用于存储用户相关信息的小文本文件。如给定 here
The main purpose of a cookie is to identify users and possibly prepare
customized Web pages or to save site login information for you.
因此,基本上当您清除浏览器中的 cookie 时,网络服务器将不会获取任何用户数据。因此,用户将被视为访客,并会受到相应的对待。
OP,如果我理解清楚的话,你只是想了解如何实现 laravel 会话 table 这样你就可以在同一浏览器中从同一用户进行多次登录:
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');
});
虽然这个问题已经,但我要补充一点,您可以在实际登录方法中轻松实现此功能,而无需修改您的核心文件。
为此,您可以按照以下逻辑
登录前,手动检查请求用户代理头是否与会话中的会话用户代理相同,即:
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
}
}
}
您将需要做比这更多的工作,但我希望您能理解这个概念。
当我们第一次登录我们的 gmail 帐户或删除缓存和 cookie 后,我们会收到 window 以输入发送到我们手机的代码。
我正在尝试通过电子邮件而不是短信来实现这一点。下面是我实现这个的方法。
I am following this link : https://laravel.com/docs/5.2/session
并在数据库中创建一个 Session table
。我还可以在 Session Table 记录中看到我的浏览器详细信息。我不确定这是否是正确的方法。
Gmail 提供了跟踪多个浏览器的功能。这意味着如果我上次从 Firefox 登录,这次从 Chrome 登录,那么我将再次被要求输入密码。今后,如果 cache/cookie 未被删除,我将不会被要求填写 Chrome 和 Firefox 的代码。
有人可以给我任何 link 来解释如何在 cache/cookie 保存时为多个浏览器提供准备吗?这样我就可以发送电子邮件获取安全码
另外创建一个 table(除了会话 1)
类似
用户 ID |用户代理 | IP
当他们登录时,根据他们在 $_SERVER
数组中的当前值进行检查。如果它在那里一切都很好,如果没有中断登录,并向他们发送 link 以确认新数据。您可能想要对原始登录执行某种 ajax 以检查他们何时登录,然后一旦发生这种情况就重定向到他们要去的地方。
有道理。
正如我在可维护性的评论中所说,我会自己处理它而不使用任何第三方 API,数据很容易验证。那部分比较琐碎,继续登录过程就不多了。
您可以通过发出一个额外的 cookie(比方说 browser_cookie)来记住已经通过身份验证的浏览器来实现这一点。
实施:
创建以下 table (browser_management) :
token (pk)| user_id (fk) | series_identifier
其中:
token :发给用户的令牌的散列形式(使用 bcrypt 或类似算法)(发给用户的令牌本身是不可猜测的随机生成的密钥从适当大 space)
series_identifier :从适当大的 space
随机生成的不可猜测的密钥
每当用户登录时检查browser_cookie
。
案例 1: 用户是第一次登录。
考虑到用户是第一次登录 browser_cookie
不会出现。因此,您将发送一封包含验证码的电子邮件。
身份验证后,为 token
和 series_identifier
分别生成两个随机数。将哈希 token
和 series_identifier
存储在 browser_management
table 中,用于由 user_id
.
此外,使用 token
和 series_identifier
向用户发出 browser_cookie
。
情况二:用户下次重新登录。
现在,当同一用户下次登录时,取 token
并在 browser_management
table 中找到具有散列 token
.[=46 的条目=]
如果找到,请检查 user_id
和 series_identifier
是否匹配。
案例 2.1: 匹配的条目:
允许用户进入系统无需重新验证邮箱密码。
生成另一个令牌并将 cookie
中的 token
和 table
替换为新令牌。 (这将降低会话劫持的风险)。
案例 2.2: 条目不匹配:
按照电子邮件身份验证的步骤并通知用户可能的盗窃。(就像 gmail 通知新的浏览器登录一样)。
参考文献:
- Improved Persistent Login Cookie Best Practice
- What is the best way to implement “remember me” for a website?
更新:
示例代码:
迁移:
<?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');
}
}
中间件:创建一个新的中间件
<?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);
}
Browser_management 模型: 创建一个模型来表示 browser_managements table
<?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');
}
}
电子邮件验证方法:将以下方法添加到您的 AuthController 以处理电子邮件验证
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');
}
路由: 添加这些路由以处理电子邮件验证请求。我们还将向其中添加 email_verification 中间件。
Route::get('/auth/email_verification',`AuthController@getVerification')->middleware('email_verification');
Route::post('/auth/email_verification',`AuthController@postVerification')->middleware('email_verification');<br/>
更新 2:
关于gmail的流量..
我遵循了以下步骤:
1) 登录 gmail,然后进行两步验证。
2)注销
3)清除缓存link
4)重新登录
当我再次登录时,清除缓存后,它没有要求我进行两步验证。
不过,如果您清除 cookie,它会要求进行两步验证。
原因:
所有识别用户的用户数据(此处 token
)都存储在 cookie 中。如果清除cookies,服务器将没有机制来识别用户。
更新 3:
Gmail 请求两步验证:
首先,Gmail 或任何其他网站都不会收到有关清除缓存的通知
如给定 here:
The cache is nothing more than a place on your hard disk where the browser keeps things that it downloaded once in case they’re needed again.
现在,cookies是服务器发布的用于存储用户相关信息的小文本文件。如给定 here
The main purpose of a cookie is to identify users and possibly prepare customized Web pages or to save site login information for you.
因此,基本上当您清除浏览器中的 cookie 时,网络服务器将不会获取任何用户数据。因此,用户将被视为访客,并会受到相应的对待。
OP,如果我理解清楚的话,你只是想了解如何实现 laravel 会话 table 这样你就可以在同一浏览器中从同一用户进行多次登录:
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');
});
虽然这个问题已经
为此,您可以按照以下逻辑
登录前,手动检查请求用户代理头是否与会话中的会话用户代理相同,即:
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
}
}
}
您将需要做比这更多的工作,但我希望您能理解这个概念。