Estoy creando una configuración de autenticación básica similar a cómo se usa en la plantilla de plantilla de auth-template de Vapor (desdeaquí ). Tengo todo configurado de la misma manera que en la plantilla.
Sin embargo, me gustaría agregar sal. Puedo generar una sal para un usuario al momento de la creación:
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() } }Pero no hay forma de recuperar esa sal al realizar la autenticación durante el inicio de sesión:
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) }Quiero que la sal se genere aleatoriamente para cada usuario y se almacene en la base de datos como una columna separada de la contraseña codificada para usarse más tarde durante la autenticación.
¿Existe una forma estandarizada de manejar el salteado de un hash de contraseña en Vapor 3?
La forma en que funciona en Vapor es que cada hash BCrypt tiene una sal única que se guarda con la contraseña en la base de datos. Las funciones predeterminadas de BCrypt en Vapor esperan esto.
Si desea seguir una ruta diferente, eche un vistazo a la función para codificar una contraseña, eso requiere sal. Luego puede guardar eso en su propio campo y recuperarlo cuando verifique la contraseña. Honestamente, diría que solo use los valores predeterminados a menos que tenga una razón muy específica para no hacerlo
Hash la contraseña usando BCrypt. BCrypt ya es parte de las dependencias de Vapor.
BCrypt.hash("vapor", cost: 4)Esto codificará la cadena "vapor", utilizando una sal generada aleatoriamente, con una complejidad de 4. Elegir el costo es subjetivo y arbitrario, pero se recomienda que las aplicaciones seguras del mundo real tengan un factor de costo superior a 10-12. Si no le gusta que BCrypt genere la sal aleatoriamente y desea generar su propia sal, puede proporcionar la sal a la función hash, que tiene la siguiente firma:
public func hash(_ plaintext: LosslessDataConvertible, cost: Int = 12, salt: LosslessDataConvertible? = nil) throws -> StringLa documentación dice que la sal debe ser de 16 bytes si se proporciona manualmente. Este es un hash de muestra:
$2a$04$/nqhWqplnughhq6mlKmi8.raprxoG/dczY8kdbOKm.zC5sPu.2IBiComo puedes ver incluye información auxiliar como la complejidad, el tipo de algoritmo y la sal, todo lo necesario para hacer la verificación. Si proporcionaste tu propia sal, también será parte del hash final y no es necesario que la proporciones por separado. Puede hacer la verificación como está escrito a continuación.
try BCrypt.verify("vapor", created: hashedPasswordSavedInDatabase)