2016-10-04 153 views
2

我生成一个由单个使用令牌组成的url,并通过电子邮件将其发送给用户。用户应该点击这个链接并重定向到一个页面,该页面将验证令牌并执行一些操作。存储的盐渍令牌和令牌比较

在数据库方面,我存储了这个令牌(出于安全原因散列和腌制)。问题是我在验证令牌时遇到了一些困难,因为我存储它的方式不能为同一标记生成相同的盐。因此,我无法将这种盐与我储存的盐进行比较。

# Retrieving or creating object usertokens 
usr, created = UserToken.objects.get_or_create(email=email) 
# Adding a new token 
token = uuid.uuid4().hex 
usr.token = make_password(token) # Stores in the local database the salted and hashed token 
usr.save() 

我使用的这个make_password方法定义在django.contrib.auth.hashers中。

通过使用这种方法,我不能从相同的标记生成两次相同的盐。

>>> token = 'test' 
>>> enc_token1 = make_password(token) 
>>> enc_token2 = make_password(token) 
>>> enc_token1 == enc_token2 
False 

但是,这并不能帮助我从我的数据库检索对应于令牌的条目,我无法验证它。

+0

现在有件事我从来没有听说过; _带有盐渍的标记_。你想用这些令牌来番茄酱吗?...... –

+2

除了所有的笑话外,散列和腌制的目的是不能从散列字符串中获取原始密码。由于密码经常以某种方式重用,并且希望密码在潜在违规后很长时间内保持秘密,所以需要密码。令牌可以简单地重置,然后攻击者绝对不会使用获取的令牌。正如您已经注意到的,没有有效的方法来查询盐渍和散列的标记。这就是为什么令牌通常以纯文本格式存储的原因。为了什么特定的安全原因,你想存储他们散列? – knbk

回答

1

使用一个简单的字符串相等性检查两个哈希和盐渍标记将不起作用。该Django docs for password managementdjango.contrib.auth.hashers命名空间来处理这一切为你提供了一个非常简单的方法:

>>> token = 'test' 
>>> enc_token1 = make_password(token) 
>>> check_password('test', enc_token1) 
True 

check_password方法做了几件事情篷子后面,像检查,如果哈希算法发生了变化。它返回实现BasePasswordHasher基类的算法的verify方法的结果。下面是来自source of the PBKDF2PasswordHasher实现的例子:

def verify(self, password, encoded): 
    algorithm, iterations, salt, hash = encoded.split('$', 3) 
    assert algorithm == self.algorithm 
    encoded_2 = self.encode(password, salt, int(iterations)) 
    return constant_time_compare(encoded, encoded_2) 

注意如何盐被分割在“$”的encoded_string发现,由于Django docs note

一个用户对象的密码属性在这种格式的字符串:

<algorithm>$<iterations>$<salt>$<hash> 
+0

感谢您的回答。使用check_password的问题是因为我必须知道编码的字符串和令牌。在我的情况下,我的输入是令牌,我在我的数据库中存储了许多编码的字符串。请原谅我,如果我错了,但是,如果我使用check_password方法,似乎我应该执行令牌和所有存储的编码字符串之间的配对比较。也许这将是昂贵的。 – revy

+0

请记住,散列算法的本质是单向的。如果您在不知道原始字符串的情况下验证哈希值,那么您几乎可以在第一位击败哈希的目的,这是为了防止具有哈希值的攻击者知道纯文本。 – brianpck