2014-01-07 79 views
1

我正在开发一个php/laravel-4项目,我们需要自动对来自我们发送给他们的电子邮件中链接的用户进行身份验证,因此我们需要对链接进行时间限制,以便链接在电子邮件不会验证过期时间过去后,我已经到了这种方法,但我对它的安全性有一些怀疑:通过电子邮件进行PHP用户身份验证

首先我使用用户的电子邮件,时间戳和一个密钥,如下所示做一个MD5散列:

$timestamp = time(); 
$hash = md5($email . $timestamp . $secret_key); 

然后我就可以产生这样的网址:

$url = "http://www.example.com/url?email={$email}&hash={$hash}&timestamp={$timestamp} 

那么我可以检查时间戳(用于时间验证)并重新生成散列并使用提供的电子邮件验证用户,您认为它有任何安全漏洞吗?如果是,请给我建议安全的方法。

+0

什么是防止有人嗅探网络流量,并在预期用户之前使用此链接?假设很多用户将通过未加密的方法检查电子邮件是安全的。 –

+3

为什么包含时间戳?散列本身在理论上是唯一的,所以只发送**散列,并将关联的用户ID /时间戳存储在数据库表中。当用户点击链接时,您在表格中查找哈希值,检查它是否仍然有效,并在该点获取用户ID。 –

+0

@JohnChrysostom所以你有什么建议通过电子邮件验证用户? – Amir

回答

3

我不会这么做。我会做什么:

创建一个表为您的链接:

public function up() 
{ 
    Schema::create('login', function($table) { 
     $table->string('id')->primary(); 

     $table->string('user_id'); 

     $table->timestamps(); 
    }); 
} 

你生成一个链接添加一行这个表中的每个时间:

$user = User::find(1); 

$login = Login::create(['id' => Login::generateID(), 'user_id' => $user->id]); 

$url = "http://www.example.com/url?login_id={$login->id}" 

然后当用户点击链接,你可以自动登录他,也立即无效该链接:

$login = Login::findOrFail(Input::get('login_id')); 

$user = User::find($login->user_id); 

Auth::login($user); 

$login->delete(); 

并创建一个艺术ISAN命令在该表periodiacally删除旧记录:

Login::where('created_at', '<=', Carbon\Carbon::now()->subDays(2))->delete(); 

这可以为generateID()的代码,这是一个基本的UUID代码生成:

public static function v4() 
{ 
    return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', 

    // 32 bits for "time_low" 
    mt_rand(0, 0xffff), mt_rand(0, 0xffff), 

    // 16 bits for "time_mid" 
    mt_rand(0, 0xffff), 

    // 16 bits for "time_hi_and_version", 
    // four most significant bits holds version number 4 
    mt_rand(0, 0x0fff) | 0x4000, 

    // 16 bits, 8 bits for "clk_seq_hi_res", 
    // 8 bits for "clk_seq_low", 
    // two most significant bits holds zero and one for variant DCE1.1 
    mt_rand(0, 0x3fff) | 0x8000, 

    // 48 bits for "node" 
    mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) 
    ); 
} 

连接到系统上的任何这样的字符串。

+0

为什么不只是使用随机MD5散列? – Amir

+0

您应该在查找ID时检查时间戳。如果由于某种原因在清理过程中存在漏洞,您可能会得到陈旧的结果。您也可以在检查时运行清理程序,或者等待检查时间的1/10。 – Halcyon

+0

我并没有试图给出一个完整的代码实现,只是一个更好的方法的指南,如何真正实现它是由OP决定的。只是改变了v4的实现,你是对的。 –

0

只要密钥不是用户密码,那么这对我来说似乎很好。

0

你不会找到一个完全安全的方法来做到这一点。如果某人的电子邮件遭到黑客攻击?或一百万其他情况。

我认为你最好的选择是发送包含用户名(或电子邮件)的电子邮件散列,并可能再次发送时间戳。一旦他们点击链接,他们必须输入密码,然后继续。

您也可以在电子邮件链接中包含重定向链接,以便脚本在输入密码后将其重定向到适当的位置。

1

md5不是密码安全的,因为它是容易破解(以密码术语)。

主要关注的是,md5,你不希望出现这种情况,你希望它尽可能,它需要你太久生成和验证慢,但不能这么慢。

看看PHP password guide,这里至关重要的是cost参数。更高的成本意味着它更安全。基本上,cost所做的是:它增加了散列函数应用的次数,更多的应用程序意味着它需要更长的时间来制作,因此蛮力。


你可以做的另一件事是混淆你的输入。如果您可以将您的URL更改为:

$string = $timestamp . "|" . $email . "|" . $hash; 
// then encrypt this string 
$encrypted = mcrypt_encrypt($cypher, $secret_password_2, $string); // or any other two-way encryption 
$url = $base . "?hash=" . $encrypted; 

当您收到散列解密后,将其拆分并检查散列。这使得它不太明显发生了什么;所有3个值都打包在一个加密的blob中。

+0

这将以真正的长$加密字符串结束。 – Amir

+0

网址的事实上的限制是2000个字符,我们甚至没有接近!也许100? – Halcyon

相关问题