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 在 加密
每月下载量:696
在 djpass 中使用
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_settings
和 make_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
用于DjangoHashers1.7.3
)。DjangoVersion::V1_4
Django 1.4DjangoVersion::V1_5
Django 1.5DjangoVersion::V1_6
Django 1.6DjangoVersion::V1_7
Django 1.7DjangoVersion::V1_8
Django 1.8DjangoVersion::V1_9
Django 1.9DjangoVersion::V1_10
Django 1.10DjangoVersion::V1_11
Django 1.11DjangoVersion::V2_0
Django 2.0DjangoVersion::V2_1
Django 2.1DjangoVersion::V2_2
Django 2.2DjangoVersion::V3_0
Django 3.0DjangoVersion::V3_1
Django 3.1DjangoVersion::V3_2
Django 3.2DjangoVersion::V4_0
Django 4.0DjangoVersion::V4_1
Django 4.1DjangoVersion::V4_2
Django 4.2DjangoVersion::V5_0
Django 5.0DjangoVersion::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