Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
security.py162 linesDownload Raw Back to werkzeug
1from __future__ import annotations2 3import hashlib4import hmac5import os6import posixpath7import secrets8 9SALT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"10DEFAULT_PBKDF2_ITERATIONS = 60000011 12_os_alt_seps: list[str] = list(13    sep for sep in [os.sep, os.path.altsep] if sep is not None and sep != "/"14)15 16 17def gen_salt(length: int) -> str:18    """Generate a random string of SALT_CHARS with specified ``length``."""19    if length <= 0:20        raise ValueError("Salt length must be at least 1.")21 22    return "".join(secrets.choice(SALT_CHARS) for _ in range(length))23 24 25def _hash_internal(method: str, salt: str, password: str) -> tuple[str, str]:26    method, *args = method.split(":")27    salt_bytes = salt.encode()28    password_bytes = password.encode()29 30    if method == "scrypt":31        if not args:32            n = 2**1533            r = 834            p = 135        else:36            try:37                n, r, p = map(int, args)38            except ValueError:39                raise ValueError("'scrypt' takes 3 arguments.") from None40 41        maxmem = 132 * n * r * p  # ideally 128, but some extra seems needed42        return (43            hashlib.scrypt(44                password_bytes, salt=salt_bytes, n=n, r=r, p=p, maxmem=maxmem45            ).hex(),46            f"scrypt:{n}:{r}:{p}",47        )48    elif method == "pbkdf2":49        len_args = len(args)50 51        if len_args == 0:52            hash_name = "sha256"53            iterations = DEFAULT_PBKDF2_ITERATIONS54        elif len_args == 1:55            hash_name = args[0]56            iterations = DEFAULT_PBKDF2_ITERATIONS57        elif len_args == 2:58            hash_name = args[0]59            iterations = int(args[1])60        else:61            raise ValueError("'pbkdf2' takes 2 arguments.")62 63        return (64            hashlib.pbkdf2_hmac(65                hash_name, password_bytes, salt_bytes, iterations66            ).hex(),67            f"pbkdf2:{hash_name}:{iterations}",68        )69    else:70        raise ValueError(f"Invalid hash method '{method}'.")71 72 73def generate_password_hash(74    password: str, method: str = "scrypt", salt_length: int = 1675) -> str:76    """Securely hash a password for storage. A password can be compared to a stored hash77    using :func:`check_password_hash`.78 79    The following methods are supported:80 81    -   ``scrypt``, the default. The parameters are ``n``, ``r``, and ``p``, the default82        is ``scrypt:32768:8:1``. See :func:`hashlib.scrypt`.83    -   ``pbkdf2``, less secure. The parameters are ``hash_method`` and ``iterations``,84        the default is ``pbkdf2:sha256:600000``. See :func:`hashlib.pbkdf2_hmac`.85 86    Default parameters may be updated to reflect current guidelines, and methods may be87    deprecated and removed if they are no longer considered secure. To migrate old88    hashes, you may generate a new hash when checking an old hash, or you may contact89    users with a link to reset their password.90 91    :param password: The plaintext password.92    :param method: The key derivation function and parameters.93    :param salt_length: The number of characters to generate for the salt.94 95    .. versionchanged:: 2.396        Scrypt support was added.97 98    .. versionchanged:: 2.399        The default iterations for pbkdf2 was increased to 600,000.100 101    .. versionchanged:: 2.3102        All plain hashes are deprecated and will not be supported in Werkzeug 3.0.103    """104    salt = gen_salt(salt_length)105    h, actual_method = _hash_internal(method, salt, password)106    return f"{actual_method}${salt}${h}"107 108 109def check_password_hash(pwhash: str, password: str) -> bool:110    """Securely check that the given stored password hash, previously generated using111    :func:`generate_password_hash`, matches the given password.112 113    Methods may be deprecated and removed if they are no longer considered secure. To114    migrate old hashes, you may generate a new hash when checking an old hash, or you115    may contact users with a link to reset their password.116 117    :param pwhash: The hashed password.118    :param password: The plaintext password.119 120    .. versionchanged:: 2.3121        All plain hashes are deprecated and will not be supported in Werkzeug 3.0.122    """123    try:124        method, salt, hashval = pwhash.split("$", 2)125    except ValueError:126        return False127 128    return hmac.compare_digest(_hash_internal(method, salt, password)[0], hashval)129 130 131def safe_join(directory: str, *pathnames: str) -> str | None:132    """Safely join zero or more untrusted path components to a base133    directory to avoid escaping the base directory.134 135    :param directory: The trusted base directory.136    :param pathnames: The untrusted path components relative to the137        base directory.138    :return: A safe path, otherwise ``None``.139    """140    if not directory:141        # Ensure we end up with ./path if directory="" is given,142        # otherwise the first untrusted part could become trusted.143        directory = "."144 145    parts = [directory]146 147    for filename in pathnames:148        if filename != "":149            filename = posixpath.normpath(filename)150 151        if (152            any(sep in filename for sep in _os_alt_seps)153            or os.path.isabs(filename)154            or filename == ".."155            or filename.startswith("../")156        ):157            return None158 159        parts.append(filename)160 161    return posixpath.join(*parts)162 
codekingpro/portable-devtools · Team Ai