我正在建立一个基本的身份验证设置,类似于在Vapor的lw
模板(来自here)中的使用方式。我已经按照模板中的所有方法进行了设置。
但是,我想加盐。创建后,我可以为用户生成盐:
auth-template
但是在登录期间执行身份验证时无法检索该盐:
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()
}
}
我希望为每个用户随机生成盐,并将其与哈希密码分开存储在数据库中,以供以后在身份验证期间使用。
在Vapor 3中是否有标准化的方法来处理密码哈希?
答案 0 :(得分:5)
它在Vapor中的工作方式是每个BCrypt哈希都有一个唯一的盐,并使用密码保存在数据库中。 Vapor中的BCrypt默认功能可以实现这一点。
如果您想走一条不同的路线,请查看用于对密码进行哈希处理的函数-这需要花些时间。然后,您可以将其保存在其自己的字段中,并在验证密码时进行检索。老实说,除非您有非常具体的理由,否则我会说只使用默认值
答案 1 :(得分:1)
您使用BCrypt对密码进行哈希处理。 BCrypt已成为Vapor依赖项的一部分。
BCrypt.hash("vapor", cost: 4)
这将使用随机生成的盐对字符串“ vapor”(哈希)进行散列处理,复杂度为4。选择成本是主观的和任意的,但是建议现实世界中安全的应用程序的成本系数应大于10-12。如果您不喜欢由BCrypt随机生成的盐,并且想要生成自己的盐,则可以将盐提供给具有以下签名的哈希函数:
public func hash(_ plaintext: LosslessDataConvertible, cost: Int = 12, salt: LosslessDataConvertible? = nil) throws -> String
文档说,如果手动提供,salt必须为16字节。 这是一个示例哈希:
$2a$04$/nqhWqplnughhq6mlKmi8.raprxoG/dczY8kdbOKm.zC5sPu.2IBi
如您所见,它包含辅助信息,例如复杂性,算法类型和盐,进行验证所需的一切。如果您提供了自己的盐,那么它也将是最终哈希的一部分,并且您无需单独提供它。您可以按照以下说明进行验证。
try BCrypt.verify("vapor", created: hashedPasswordSavedInDatabase)