Hashing Users7:45 with Kenneth Love
In "Build a Social Network with Flask", you hashed user passwords with bcrypt. Bcrypt is still very strong but, thanks to advances in GPUs, its days are numbered. A new way of securing user passwords is needed and I just happen to have one.
- Storing Passwords in a Highly Parallelized World
You can leave the
**kwargs off of
create_user if you want. I find this to be handy way to add in flags like
can_delete, you just need to add them as fields on the model or in some sort of permissions system. It's a good idea to be forward-thinking in your designs!
[MUSIC] 0:00 If you did build a social network with Flask, 0:04 you probably remember how we hashed our users passwords with bcrypt. 0:06 bcrypt is still an amazing hashing Library. 0:11 But its main protection, 0:13 the fact that it can be configured to use progressively more processor time, 0:14 has kind of been surpassed by the creation of faster and better GPU's. 0:18 This advance in hardware means that our software has been given a best by date. 0:22 Thankfully some for development doesn't just sit on its laurels and new 0:26 hashing libraries and techniques have been created that stress processors and memory. 0:29 No matter how much processor power you have, 0:35 eventually you will run out of memory. 0:36 Let's go see about building users with argon2 hashed passwords. 0:39 Before I can use the argon2 library to hash passwords. 0:43 I have to install it. 0:47 So for python, I'm just gonna pip install aargon2-cffi. 0:49 There are argon2 packages for lots of other languages. 0:53 And whenever you're installing this, you might find that you are missing ffi, so 0:57 you'll just need to lookup how to install that for 1:02 whatever your operating system is. 1:05 I've put a link to the argon2 docs in the teacher's notes, so 1:06 be sure to check there if there's something you wanna know more about. 1:09 I've also included a blog post from a friend of mine named Hinnick, 1:12 about why to use something like argon2, instead of bcrypt or whatever. 1:15 Okay, so I've got argon2 installed. 1:21 I think it's time to make a user model. 1:23 Let's make a new model up here. 1:26 And I'm gonna make it above these others, so that I can do foreign keys to them. 1:30 You know what, actually before, let's do a couple of other things here real quick. 1:35 So we need to do from argon2 import PasswordHasher. 1:42 And then I have these config things here, 1:50 I think I wanna move those into a new file. 1:52 So let's make a new file. 1:54 And we're gonna call this config.py. 1:56 And we'll take these things and cut them. 2:01 And then we will paste them into here. 2:07 And then let's get rid of that line, and let's just say import config. 2:14 And then down here, 2:21 I'll add this as config., config. 2:24 and config. 2:29 Cool. 2:31 So, I can save that. 2:32 And I wanna add one more item in here. 2:35 I want to add a secret_key. 2:37 And then you're just going to add a bunch of random characters to your secret key. 2:40 Just make it all fancy. 2:50 And then from here of course I want to do import config. 2:53 Now you might consider moving the database out to there as well just for kind of 2:56 putting everything together, but I'm not gonna worry about that for right now. 3:03 Okay, so let's add a HASHER in here which will be the PasswordHasher. 3:07 Okay, so let's make this user model. 3:13 All right, so users should have a username. 3:15 And that'll be a char field and I want that to be unique because I don't wanna 3:18 have more than one user with the same username, of course. 3:23 And then I'm gonna do a char field for the email address. 3:26 And I'm also gonna make that unique because again 3:29 I don't want more than one user to have the same stuff. 3:32 And then let's do password is equal to a CharField. 3:36 And I'm not gonna set any requirements on that. 3:39 So class meta database = database. 3:42 All right and then I want to provide a method for 3:47 creating a user and this should be 3:50 a class method because I need to work on the class and not on an instance. 3:55 So we're gonna accept the class. 4:01 And we're gonna take the username. 4:03 And we're gonna take the email. 4:05 And we're gonna take the password. 4:06 And we're gonna take any other cards that happen to come in. 4:07 Right now we don't care about them but who knows, 4:10 maybe in the future we want to allow their arguments. 4:13 All right. So first of all, 4:16 let's lowercase this email. 4:17 And then let's do cls.select 4:20 .where, we need two things here. 4:24 So, csl.email is equal to e-mail that was passed in. 4:29 Or cls.username is like, 4:34 case insensitive, is like username. 4:37 And then do a .get on that. 4:43 And then, if we get a cls.DoesNotExist error. 4:45 Then, that's good. 4:52 That means that the user doesn't exist. 4:54 So, we'll do An email=email. 4:58 And then I’m gonna do user.password = user.set password. 5:05 And provide the password. 5:11 User.save and return user. 5:13 Else, let's raise an exception. 5:17 And let's say user with that email or username already exists. 5:19 All right, cool. 5:29 So now, I need to produce this set password method. 5:30 So this is gonna be a static method. 5:37 Because again I don't want it to operate on an instance. 5:38 But I don't even want it to operate on the class. 5:41 I want it to just be this method that happens to belong to the user class. 5:43 But it doesn't care anything at all about the class, 5:46 which is why I say static method. 5:48 So we're gonna set the password, and we're gonna put in the password. 5:50 And we're gonna return HASHER.hash(password). 5:55 Cool, and then let's make a verify_password. 6:02 And this is actually going to take an instance. 6:09 This is just a normal everyday method nothing. 6:11 Nothing special about this one. 6:14 And we're gonna return HASHER.verify, 6:15 that self.password matches password. 6:20 And so argon2 is kind of cool because it actually throws an exception if 6:24 the passwords don't match. 6:28 So if this step was to fail it would throw an exception. 6:30 Because argon2 doesn't have any concept of not matching or an invalid password. 6:33 It just know these two things don't match. 6:39 Ideally you get the idea of how that works. 6:42 There's nothing really different about this. 6:46 One thing I will point out is that we don't actually need this. 6:49 In workspaces because we're using SQLite. 6:52 SQLite is really horrible with case sensitive searching. 6:54 So SQLite will work fine without that. 6:58 You do wanna have this for all the other databases, MySQL, Postres, 7:01 things like that. 7:05 I think we're good there and I'm going to go and make a user resource off camera. 7:06 I want you to try and make one on your own. 7:13 All it needs to have is a user list with a post that will create a new user. 7:15 That's all you have to make. 7:19 Go ahead and register it with the app and everything. 7:20 We'll check that out in the next video. 7:22 One thing that's really smart to do and Django does it Is if you want an example 7:25 is to create a way to upgrade password hashing. 7:28 You don't want to store the actual passwords but 7:31 when you use a new hasher to create an upgrade path, 7:34 that will change your users old hash to the new one the next time they will login. 7:36 You'll have a more secure application and 7:40 they'll appreciate not getting any we've been hacked e-mails. 7:42
You need to sign up for Treehouse in order to download course files.Sign up