#password-hashing #django #password-hash #password #hash #python #crypto

djangohashers

用于Django项目中密码原语的一个Rust端口

51个版本 (稳定版)

1.7.3 2024年4月4日
1.7.2 2023年12月4日
1.7.1 2023年10月3日
1.6.8 2023年7月6日
0.1.0 2015年12月31日

#89加密

Download history 143/week @ 2024-04-22 111/week @ 2024-04-29 135/week @ 2024-05-06 66/week @ 2024-05-13 56/week @ 2024-05-20 94/week @ 2024-05-27 186/week @ 2024-06-03 86/week @ 2024-06-10 167/week @ 2024-06-17 131/week @ 2024-06-24 81/week @ 2024-07-01 245/week @ 2024-07-08 182/week @ 2024-07-15 178/week @ 2024-07-22 151/week @ 2024-07-29 185/week @ 2024-08-05

每月下载量:696
djpass 中使用

BSD-3-Clause

48KB
810

Rust DjangoHashers

Django项目中密码原语的Rust端口。

Django的django.contrib.auth.models.User类有几个处理密码的方法,如set_password()check_password()DjangoHashers实现了这些方法背后的原始函数。所有Django的内置散列器都得到了支持。

这个库是为Django集成而设计的,但并不仅限于它;你可以将密码散列算法用于任何Rust项目(或FFI集成),因为其安全模型已经过实战考验。

TL;DR

examples/tldr.rs的内容

extern crate djangohashers;
use djangohashers::*;

fn main() {
    let encoded = make_password("K2jitmJ3CBfo");
    println!("Hash: {:?}", encoded);
    let is_valid = check_password("K2jitmJ3CBfo", &encoded).unwrap();
    println!("Is valid: {:?}", is_valid);
}

输出

$ cargo run --quiet --example tldr
Hash: "pbkdf2_sha256$390000$7HRd1YJBZvYj$Rc3BW6f7ss3CShWkULiXI9Rxj7CDdstBeoyCgFFQaK0="
Is valid: true

安装

将依赖项添加到你的Cargo.toml

[dependencies]
djangohashers = "^1.7"

参考和导入

extern crate djangohashers;

// Everything (it's not much):
use djangohashers::*;

// Or, just what you need:
use djangohashers::{check_password, make_password, Algorithm};

编译功能

默认情况下,所有散列器都已启用,但你可以选择只使用你需要的散列器,以避免不必要的依赖。

  • default:所有散列器。
  • with_pbkdf2:仅PBKDF2和PBKDF2SHA1。
  • with_argon2:仅Argon2。
  • with_scrypt:仅Scrypt。
  • with_bcrypt:仅BCrypt和BCryptSHA256。
  • with_legacy:仅SHA1、MD5、UnsaltedSHA1、UnsaltedMD5和Crypt。
  • fpbkdf2:启用Fast PBKDF2(需要OpenSSL,见下文)。
  • fuzzy_tests:仅用于开发,启用模糊测试。

Fast PBKDF2版本

根据您的平台、操作系统和库版本,DjangoHashers 可能会比 Python/Django 的参考实现慢。如果性能对您的情况至关重要,有一个替代实现:名为 fastpbkdf2 的包,它使用一个需要 OpenSSL 的库的 C 绑定。如果 ring 的 PBKDF2 实现达到了这个优化水平,那么 fastpbkdf2 版本将被弃用。

安装

将依赖项添加到您的 Cargo.toml 中,声明该功能

[dependencies.djangohashers]
version = "^1.7"
features = ["fpbkdf2"]

您需要安装 OpenSSL 并设置环境变量以便编译器可见;这取决于操作系统和包管理器,例如,在 macOS 上,您可能需要这样做

$ brew install openssl
$ export LIBRARY_PATH="$(brew --prefix openssl)/lib"
$ export CFLAGS="-I$(brew --prefix openssl)/include"
$ cargo ...

对于其他操作系统和包管理器,请遵循指南以安装 Python 的 Cryptography 依赖项,它也链接到 OpenSSL。

性能

在四核英特尔酷睿 i7 上

方法 编码或检查 性能
Django 4.1.5 在 Python 3.11.1 上 189ms 100% (基准)
带有 ring::pbkdf2 的 djangohashers(默认) 145ms 76.7% 🐇
带有 fastpbkdf2 的 djangohashers 119ms 62.9 🐇

在苹果 M1 上

方法 编码或检查 性能
Django 4.1.5 在 Python 3.11.1 上 65ms 100% (基准)
带有 ring::pbkdf2 的 djangohashers(默认) 38ms 58.5% 🐇
带有 fastpbkdf2 的 djangohashers 26ms 40.0% 🐇

使用 Docker 重复上述测试

$ docker build -t rs-dj-hashers-profile .
...

$ docker run -t rs-dj-hashers-profile
Hashing time: 65ms (Python 3.11.1, Django 4.1.5).
Hashing time: 38ms (Vanilla PBKDF2).
Hashing time: 26ms (Fast PBKDF2).

兼容性

DjangoHashers 通过了从 Django 1.4 到 5.0(以及 5.1 的测试版)的所有相关单元测试,甚至还有一个 逐行翻译测试/auth_tests/test_hashers.py

未涵盖的内容

  • 升级/降级回调。
  • 任何 Django 代码之外的第三方哈希器。
  • 一些在惯用的 Rust 中没有意义的测试。

使用方法

API 文档,感谢 docs.rs 项目!

验证散列密码

函数签名

pub fn check_password(password: &str, encoded: &str) -> Result<bool, HasherError> {}
pub fn check_password_tolerant(password: &str, encoded: &str) -> bool {}

完整版本

let password = "KRONOS"; // Sent by the user.
let encoded = "pbkdf2_sha256$24000$..."; // Fetched from DB.

match check_password(password, encoded) {
    Ok(valid) => {
        if valid {
            // Log the user in.
        } else {
            // Ask the user to try again.
        }
    }
    Err(error) => {
        // Deal with the error.
    }
}

可能的错误

  • HasherError::UnknownAlgorithm:任何无法识别为算法的东西。
  • HasherError::BadHash:散列字符串已损坏。
  • HasherError::InvalidIterations:迭代次数不是正整数。
  • HasherError::EmptyHash:散列字符串为空。
  • HasherError::InvalidArgon2Salt:Argon2 盐应该是 Base64 编码的。

如果您想自动将所有错误假定为 "无效密码",有一个快捷方式可以实现这一点

if check_password_tolerant(password, encoded) {
	// Log the user in.
} else {
	// Ask the user to try again.
}

生成散列密码

函数签名

pub fn make_password(password: &str) -> String {}
pub fn make_password_with_algorithm(password: &str, algorithm: Algorithm) -> String {}
pub fn make_password_with_settings(password: &str, salt: &str, algorithm: Algorithm) -> String {}

可用的算法

  • Algorithm::PBKDF2(默认)
  • 算法::PBKDF2SHA1
  • 算法::Argon2
  • 算法::Scrypt
  • 算法::BCryptSHA256
  • 算法::BCrypt
  • 算法::SHA1
  • 算法::MD5
  • 算法::UnsaltedSHA1
  • 算法::UnsaltedMD5
  • 算法::Crypt

算法遵循 Django 命名模型,只是没有 PasswordHasher 后缀。

使用默认设置(PBKDF2 算法,随机盐)

let encoded = make_password("KRONOS");
// Returns something like:
// pbkdf2_sha256$24000$go9s3b1y1BTe$Pksk4EptJ84KDnI7ciocmhzFAb5lFoFwd6qlPOwwW4Q=

使用定义的算法(随机盐)

let encoded = make_password_with_algorithm("KRONOS", Algorithm::BCryptSHA256);
// Returns something like:
// bcrypt_sha256$$2b$12$e5C3zfswn.CowOBbbb7ngeYbxKzJePCDHwo8AMr/SZeZCoGrk7oue

使用定义的算法和盐(不推荐,仅用于调试)

let encoded = make_password_with_settings("KRONOS", "seasalt", Algorithm::PBKDF2SHA1);
// Returns exactly this (remember, the salt is fixed!):
// pbkdf2_sha1$24000$seasalt$F+kiWNHXbMBcwgxsvSKFCWHnZZ0=

警告:如果盐不是仅包含字母和数字(^[A-Za-z0-9]*$),则 make_password_with_settingsmake_password_core 都会 panic。

根据 Django 版本生成散列密码

不同版本的Django对PBKDF2和BCrypt算法的哈希器迭代次数可能不同;这种抽象使得可以生成与该版本中使用相同迭代次数的密码。

use djangohashers::{Django, DjangoVersion};

let django = Django {version: DjangoVersion::V1_8};  // Django 1.8.
let encoded = django.make_password("KRONOS");
// Returns something like:
// pbkdf2_sha256$20000$u0C1E8jrnAYx$7KIo/fAuBJpswQyL7pTxO06ccrSjGdIe7iSqzdVub1w=
//               |||||
// ...notice the 20000 iterations, used in Django 1.8.

可用版本

  • DjangoVersion::CURRENT 当前Django版本(5.0用于DjangoHashers 1.7.3)。
  • DjangoVersion::V1_4 Django 1.4
  • DjangoVersion::V1_5 Django 1.5
  • DjangoVersion::V1_6 Django 1.6
  • DjangoVersion::V1_7 Django 1.7
  • DjangoVersion::V1_8 Django 1.8
  • DjangoVersion::V1_9 Django 1.9
  • DjangoVersion::V1_10 Django 1.10
  • DjangoVersion::V1_11 Django 1.11
  • DjangoVersion::V2_0 Django 2.0
  • DjangoVersion::V2_1 Django 2.1
  • DjangoVersion::V2_2 Django 2.2
  • DjangoVersion::V3_0 Django 3.0
  • DjangoVersion::V3_1 Django 3.1
  • DjangoVersion::V3_2 Django 3.2
  • DjangoVersion::V4_0 Django 4.0
  • DjangoVersion::V4_1 Django 4.1
  • DjangoVersion::V4_2 Django 4.2
  • DjangoVersion::V5_0 Django 5.0
  • DjangoVersion::V5_0 Django 5.1

验证哈希格式(预加密)

函数签名

pub fn is_password_usable(encoded: &str) -> bool {}

在运行昂贵的加密操作之前,您可以检查密码哈希是否格式正确。

let encoded = "pbkdf2_sha256$24000$..."; // Fetched from DB.

if is_password_usable(encoded) {
    // Go ahead.
} else {
    // Check your database or report an issue.
}

贡献

  • 对我有耐心,我是Rust的新手,这是我的第一个项目。
  • 不要让你的mad-rust-skillz失控,可读性是首要任务。
  • 请在你代码中使用rustfmt
  • 始终包括一些测试案例。

许可证

Rust DjangoHashers在3-Clause BSD许可证下发布。

tl;dr: "只要给我信用就可以免费使用"

依赖关系

~2–12MB
~147K SLoC