我如何处理 Vapor 3 中的密码散列加盐?
How do I handle salting a password hash in Vapor 3?
我正在构建一个基本的身份验证设置,类似于它在 Vapor 的 auth-template
模板(来自 here)中的使用方式。我已经按照模板中的相同方式设置了所有内容。
不过,我想加盐。我可以在创建时为用户生成盐:
static func create(_ req: Request, newUserRequest user: CreateUserRequest) throws -> Future<User.Public> {
return User.query(on: req).filter(\.username == user.username).first().flatMap { existingUser in
guard existingUser == nil else {
throw Abort(.badRequest, reason: "A user with the given username already exists.")
}
guard user.password == user.passwordVerification else {
throw Abort(.badRequest, reason: "Given passwords did not match.")
}
let count = 16
var pw_salt_data = Data(count: count)
let _ = pw_salt_data.withUnsafeMutableBytes { mutableBytes in
SecRandomCopyBytes(kSecRandomDefault, count, mutableBytes)
}
let pw_salt = try BCrypt.hash(pw_salt_data.base64EncodedString())
let pw_hash = try BCrypt.hash(pw_salt + user.password)
return User(id: nil, username: user.username, pw_hash: pw_hash, pw_salt: pw_salt, email: user.email).save(on: req).toPublic()
}
}
但是在登录期间执行身份验证时无法检索该盐:
static func login(_ req: Request) throws -> Future<UserToken> {
let user = try req.requireAuthenticated(User.self)
let token = try UserToken.create(userID: user.requireID())
return token.save(on: req)
}
我希望为每个用户随机生成 salt,并将其作为散列密码的单独列存储在数据库中,以便稍后在身份验证期间使用。
是否有标准化的方法来处理 Vapor 3 中的密码散列加盐?
它在 Vapor 中的工作方式是每个 BCrypt 哈希都有一个唯一的盐,它与密码一起保存在数据库中。 Vapor 中的 BCrypt 默认函数期待这一点。
如果您想走另一条路,请查看散列密码的函数 - 这需要加盐。然后,您可以将其保存在自己的字段中,并在验证密码时检索。老实说,除非您有非常具体的理由不这样做,否则我会说只使用默认值
您使用 BCrypt 对密码进行哈希处理。 BCrypt 已经是 Vapor 依赖项的一部分。
BCrypt.hash("vapor", cost: 4)
这将使用随机生成的 salt 对字符串 "vapor" 进行哈希处理,复杂度为 4。选择成本是主观且任意的,但建议现实世界的安全应用程序的成本因子应大于 10 -12。如果您不喜欢 BCrypt 随机生成的盐,并且想生成自己的盐,可以将盐提供给具有以下签名的哈希函数:
public func hash(_ plaintext: LosslessDataConvertible, cost: Int = 12, salt: LosslessDataConvertible? = nil) throws -> String
文档说如果手动提供盐必须是 16 字节。
这是一个示例哈希:
a$/nqhWqplnughhq6mlKmi8.raprxoG/dczY8kdbOKm.zC5sPu.2IBi
如您所见,它包括辅助信息,例如复杂性、算法类型和盐,以及进行验证所需的一切。如果您提供了自己的盐,它也将成为最终散列的一部分,您无需单独提供。您可以按照下面的方式进行验证。
try BCrypt.verify("vapor", created: hashedPasswordSavedInDatabase)
我正在构建一个基本的身份验证设置,类似于它在 Vapor 的 auth-template
模板(来自 here)中的使用方式。我已经按照模板中的相同方式设置了所有内容。
不过,我想加盐。我可以在创建时为用户生成盐:
static func create(_ req: Request, newUserRequest user: CreateUserRequest) throws -> Future<User.Public> {
return User.query(on: req).filter(\.username == user.username).first().flatMap { existingUser in
guard existingUser == nil else {
throw Abort(.badRequest, reason: "A user with the given username already exists.")
}
guard user.password == user.passwordVerification else {
throw Abort(.badRequest, reason: "Given passwords did not match.")
}
let count = 16
var pw_salt_data = Data(count: count)
let _ = pw_salt_data.withUnsafeMutableBytes { mutableBytes in
SecRandomCopyBytes(kSecRandomDefault, count, mutableBytes)
}
let pw_salt = try BCrypt.hash(pw_salt_data.base64EncodedString())
let pw_hash = try BCrypt.hash(pw_salt + user.password)
return User(id: nil, username: user.username, pw_hash: pw_hash, pw_salt: pw_salt, email: user.email).save(on: req).toPublic()
}
}
但是在登录期间执行身份验证时无法检索该盐:
static func login(_ req: Request) throws -> Future<UserToken> {
let user = try req.requireAuthenticated(User.self)
let token = try UserToken.create(userID: user.requireID())
return token.save(on: req)
}
我希望为每个用户随机生成 salt,并将其作为散列密码的单独列存储在数据库中,以便稍后在身份验证期间使用。
是否有标准化的方法来处理 Vapor 3 中的密码散列加盐?
它在 Vapor 中的工作方式是每个 BCrypt 哈希都有一个唯一的盐,它与密码一起保存在数据库中。 Vapor 中的 BCrypt 默认函数期待这一点。
如果您想走另一条路,请查看散列密码的函数 - 这需要加盐。然后,您可以将其保存在自己的字段中,并在验证密码时检索。老实说,除非您有非常具体的理由不这样做,否则我会说只使用默认值
您使用 BCrypt 对密码进行哈希处理。 BCrypt 已经是 Vapor 依赖项的一部分。
BCrypt.hash("vapor", cost: 4)
这将使用随机生成的 salt 对字符串 "vapor" 进行哈希处理,复杂度为 4。选择成本是主观且任意的,但建议现实世界的安全应用程序的成本因子应大于 10 -12。如果您不喜欢 BCrypt 随机生成的盐,并且想生成自己的盐,可以将盐提供给具有以下签名的哈希函数:
public func hash(_ plaintext: LosslessDataConvertible, cost: Int = 12, salt: LosslessDataConvertible? = nil) throws -> String
文档说如果手动提供盐必须是 16 字节。 这是一个示例哈希:
a$/nqhWqplnughhq6mlKmi8.raprxoG/dczY8kdbOKm.zC5sPu.2IBi
如您所见,它包括辅助信息,例如复杂性、算法类型和盐,以及进行验证所需的一切。如果您提供了自己的盐,它也将成为最终散列的一部分,您无需单独提供。您可以按照下面的方式进行验证。
try BCrypt.verify("vapor", created: hashedPasswordSavedInDatabase)