Skip to main content
 首页 » 编程设计

php之像web2py一样在php中进行密码加密

2025年05月04日10bluestorm

我不会在 php 中实现在 web2py 中完成的密码散列,现在我从 web2py 的创建者 massimo de piero 那里得到了一些关于如何做到这一点的说明,但我仍然无法在 php 中实现它,说明如下:

逻辑非常复杂,因为它需要处理很多选项和不间断的向后兼容性。

通常哈希密码看起来像

算法$盐$散列 算法$$散列(无盐) 散列(遗留)

哈希是使用算法、盐和可选的用户提供的 key 计算的。 key 是唯一的。每个密码的盐都不同。

每次调用 CRYPT()('password') 时,您都会得到一个 LazyCrypt 对象。该对象可以序列化为字符串。您得到的字符串总是不同的,因为它包含随机盐。你不能比较其中两个字符串,因为你总是得到假,即使是相同的密码。然而,您可以将 LazyObject 与字符串进行比较,惰性对象将使用与字符串相同的算法和相同的盐来计算哈希值,并将其与字符串中的哈希值进行比较。示例:

>>> a = CRYPT()('password') 
>>> b = CRYPT()('password') 
>>> sa = str(a) 
>>> sb = str(b) 
>>> sa == sb 
False 
>>> a == sb 
True 
>>> c = CRYPT()('wrong') 
>>> c == sb 
False 

下面是 web2py 框架中用于实现该加密的类:

class LazyCrypt(object): 
    """ 
    Stores a lazy password hash 
    """ 
 
    def __init__(self, crypt, password): 
        """ 
        crypt is an instance of the CRYPT validator, 
        password is the password as inserted by the user 
        """ 
        self.crypt = crypt 
        self.password = password 
        self.crypted = None 
 
    def __str__(self): 
        """ 
        Encrypted self.password and caches it in self.crypted. 
        If self.crypt.salt the output is in the format <algorithm>$<salt>$<hash> 
        Try get the digest_alg from the key (if it exists) 
        else assume the default digest_alg. If not key at all, set key='' 
        If a salt is specified use it, if salt is True, set salt to uuid 
        (this should all be backward compatible) 
        Options: 
        key = 'uuid' 
        key = 'md5:uuid' 
        key = 'sha512:uuid' 
        ... 
        key = 'pbkdf2(1000,64,sha512):uuid' 1000 iterations and 64 chars length 
        """ 
        if self.crypted: 
            return self.crypted 
        if self.crypt.key: 
            if ':' in self.crypt.key: 
                digest_alg, key = self.crypt.key.split(':', 1) 
            else: 
                digest_alg, key = self.crypt.digest_alg, self.crypt.key 
        else: 
            digest_alg, key = self.crypt.digest_alg, '' 
        if self.crypt.salt: 
            if self.crypt.salt == True: 
                salt = str(web2py_uuid()).replace('-', '')[-16:] 
            else: 
                salt = self.crypt.salt 
        else: 
            salt = '' 
 
        hashed = simple_hash(self.password, key, salt, digest_alg) 
        self.crypted = '%s$%s$%s' % (digest_alg, salt, hashed) 
        return self.crypted 
 
    def __eq__(self, stored_password): 
        """ 
        compares the current lazy crypted password with a stored password 
        """ 
        # LazyCrypt objects comparison 
        if isinstance(stored_password, self.__class__): 
            return ((self is stored_password) or 
                   ((self.crypt.key == stored_password.crypt.key) and 
                   (self.password == stored_password.password))) 
 
        if self.crypt.key: 
            if ':' in self.crypt.key: 
                key = self.crypt.key.split(':')[1] 
            else: 
                key = self.crypt.key 
        else: 
            key = '' 
        if stored_password is None: 
            return False 
        elif stored_password.count('$') == 2: 
            (digest_alg, salt, hash) = stored_password.split('$') 
            h = simple_hash(self.password, key, salt, digest_alg) 
            temp_pass = '%s$%s$%s' % (digest_alg, salt, h) 
        else:  # no salting 
            # guess digest_alg 
            digest_alg = DIGEST_ALG_BY_SIZE.get(len(stored_password), None) 
 
            if not digest_alg: 
                return False 
            else: 
                temp_pass = simple_hash(self.password, key, '', digest_alg) 
        return temp_pass == stored_password 
 
 
class CRYPT(object): 
 
    """ 
    example:: 
        INPUT(_type='text', _name='name', requires=CRYPT()) 
    encodes the value on validation with a digest. 
    If no arguments are provided CRYPT uses the MD5 algorithm. 
    If the key argument is provided the HMAC+MD5 algorithm is used. 
    If the digest_alg is specified this is used to replace the 
    MD5 with, for example, SHA512. The digest_alg can be 
    the name of a hashlib algorithm as a string or the algorithm itself. 
    min_length is the minimal password length (default 4) - IS_STRONG for serious security 
    error_message is the message if password is too short 
 
    Notice that an empty password is accepted but invalid. It will not allow login back. 
 
    Stores junk as hashed password. 
    Specify an algorithm or by default we will use sha512. 
    Typical available algorithms: 
      md5, sha1, sha224, sha256, sha384, sha512 
 
    If salt, it hashes a password with a salt. 
    If salt is True, this method will automatically generate one. 
    Either case it returns an encrypted password string in the following format: 
 
      <algorithm>$<salt>$<hash> 
 
    Important: hashed password is returned as a LazyCrypt object and computed only if needed. 
    The LasyCrypt object also knows how to compare itself with an existing salted password 
 
    Supports standard algorithms 
 
    >>> for alg in ('md5','sha1','sha256','sha384','sha512'): 
 
    ...     print str(CRYPT(digest_alg=alg,salt=True)('test')[0]) 
    md5$...$... 
    sha1$...$... 
    sha256$...$... 
    sha384$...$... 
    sha512$...$... 
 
    The syntax is always alg$salt$hash 
 
    Supports for pbkdf2 
    >>> alg = 'pbkdf2(1000,20,sha512)' 
    >>> print str(CRYPT(digest_alg=alg,salt=True)('test')[0]) 
    pbkdf2(1000,20,sha512)$...$... 
 
    An optional hmac_key can be specified and it is used as salt prefix 
    >>> a = str(CRYPT(digest_alg='md5',key='mykey',salt=True)('test')[0]) 
    >>> print a 
    md5$...$... 
 
    Even if the algorithm changes the hash can still be validated 
    >>> CRYPT(digest_alg='sha1',key='mykey',salt=True)('test')[0] == a 
    True 
 
    If no salt is specified CRYPT can guess the algorithms from length: 
    >>> a = str(CRYPT(digest_alg='sha1',salt=False)('test')[0]) 
    >>> a 
    'sha1$$a94a8fe5ccb19ba61c4c0873d391e987982fbbd3' 
    >>> CRYPT(digest_alg='sha1',salt=False)('test')[0] == a 
    True 
    >>> CRYPT(digest_alg='sha1',salt=False)('test')[0] == a[6:] 
    True 
    >>> CRYPT(digest_alg='md5',salt=False)('test')[0] == a 
    True 
    >>> CRYPT(digest_alg='md5',salt=False)('test')[0] == a[6:] 
    True 
    """ 
 
    def __init__(self, 
                 key=None, 
                 digest_alg='pbkdf2(1000,20,sha512)', 
                 min_length=0, 
                 error_message='too short', salt=True): 
 
        """ 
        important, digest_alg='md5' is not the default hashing algorithm for 
        web2py. This is only an example of usage of this function. 
 
        The actual hash algorithm is determined from the key which is 
        generated by web2py in tools.py. This defaults to hmac+sha512. 
        """ 
 
        self.key = key 
        self.digest_alg = digest_alg 
        self.min_length = min_length 
        self.error_message = error_message 
        self.salt = salt 
 
    def __call__(self, value): 
        if len(value) < self.min_length: 
            return ('', translate(self.error_message)) 
        return (LazyCrypt(self, value), None)  

希望大家多多指教, 最好的问候,

请您参考如下方法:

您想使用 password_hash()和 password_verify()而不是直接使用 crypt()。