How to authenticate with encrypted email as username? - django

In my Django web-app, I would like the user to authenticate itself with an encrypted email address that would simply be the username. Due to the existing GDPR regulations in my country, I have to encrypt e-mail addresses and by doing it with the help of Python Cryptography and Fernet functions, each string is different after encryption, even if two strings are encrypted with one and the same key. Is it possible to authenticate the user without errors in such a situation? If this is possible, where can I read a little more about it?
EDIT: Maybe I incorrectly specified: Django uses username and password for authentication, if the encrypted email is username, when logging in, the user will enter the email when logging in, i.e. harry#example.com. The database keeps an encrypted version of this email, so when using authenticate(request, username, password), it will look for a user with the username harry#example.com, not the encrypted version. If at this point I would like to decrypt the user's e-mail from the database and compare it with the e-mail that the user entered when logging in, app would probably has to decrypt all e-mails in the database, and then check if and which one is harry#example.com and here, in my opinion, it becomes quite problematic, because I have the impression that it is a not good solution in terms of time and server load. Is there any other way that I will be able to compare the e-mail entered when logging in and the encrypted e-mail in the database?

Here is a good lesson on how to use python cryptography https://www.geeksforgeeks.org/how-to-encrypt-and-decrypt-strings-in-python/
As for GDPR, the user can enter their email but you should encrypt it on the store, then decrypt it when you want to use it. Make sure that your secret is hidden. If someone gets access to your database and your secret, the encryption is as good as if it's not there.
You should not be comparing the encrypted strings, you should decrypt the email and compare it to the email that is currently entered. Comparing hashes should only be done with hashing, not encryption. If you don't want to have access to the user's email, you should consider hashing instead of encrypting.

There's a good read here How do I encrypt and decrypt a string in python?. To know the how-to around what you need. Plus, you described the solution quite well, so take a look at the following packages from the Django community which achieve what you are looking for:
https://github.com/orcasgit/django-fernet-fields/
https://github.com/orcasgit/django-fernet-fields/blob/master/fernet_fields/fields.py#L117 It includes an Encrypted email field
https://github.com/patowc/django-encrypted-field

Related

Hashing + salting emails used in authentication - good or bad practice?

Is hashing + salting of email addresses and usernames not a normal thing to do? Like in a data breach you wouldn't be able to know who is registrered to the service since every field is hashed + salted? Is there any cons to this since there isnt much about it on the internet?
It is not standard practice to salt and hash usernames / email addresses.
It is true that an attacker will be unable to identify the stored usernames/ email addresses if salted and hashed prior to storage. In fact, nobody will be able to access the usernames/ emails (including authorized users like the system administrator).
Sounds secure, so why is this a problem?
When a user attempts to login, they will send a username (or email) and password. Since each salt is unique to that specific username, the only way to associate the username/email with the stored hash username/email is to test every salt + hash combination until either a match is found or every entry is tried / rejected.
While this might be possible for a database consisting of few users, it is infeasible in practice because hashing is computationally demanding. Imagine waiting hours or days for a login service to compute every salt+hash combination only to find the username was simply misspelled.
Additionally, if you are salting usernames then how will you prevent duplicates?
Usernames must be unique. If you salt the username then you have no way of preventing multiple users from using the same username.
What other methods can a developer employ to protect information?
The most obvious solution is database encryption. While this is a bit outside the scope of your question, Wikipedia has a good article covering this topic.
It is not common to hash and salt usernames and email addresses for several reasons:
Usability
If your email address was hashed, the website could not display your email address as the value is hashed.
Security
From a security standpoint, you wouldn't gain much by hashing your email address. Imagine if somebody had access to your email address, they could use the same hashing algorithm and find your credentials easily by iterating through the database. That is why some website have implemented a separate login authenticating name and display name. The login name remains hashed (unsalted) while the display name is stored unhashed. Unless the attacker has your login name/address, there is no way of compromising your data. In this particular case, hashing of email addresses guarantees full security of your data.

How to send a hashed password to DRF and get authenticated

I'm working on authenticating users in Django, and I know that Django keeps all the passwords hashed in the data base,
so in order to secure the user credentials, I have to hash the password in my front end (Angular2) before sending it to my back end (Django rest framework).
The problem is that I don't know if Django excepts hashed passwords or is he capable of comparing it to the existing one, and if so , can any one pin point me to the right way.
any help is appreciated, thanks
You do not need to hash the password in Angular. Django will not understand a password hashed by Angular, since Django hashes passwords in a different way and has no information indicating that what you are sending is a hash. Even if you were able to has them the same way, Django would hash it again, which would not work. That is,
H(password) != H(H(password))
For a single hash function, H.
Send the password as plain text to the server. Protect the password by transferring all data over TLS/SSL. Django will accept the plain text password, compare the hashes, and authenticate the user as normal.

How to design email and username login

I have two question about usernames and emails
1. I judge username is a Email if '#' in username, and auth it follow:
email_user = User.objects.get(email__iexact=username)
authenticate(username=email_user.username)
Is that a good way that you recommended? or you may have a better advice?
I know a AbstractBaseUser can do it, but I think use User is more reasonable.
2. Should I store the user's email within the User.email field?
Imagine if I sign up a new user with:
username: '123'
email: '456#google.com'
and when I signup success, then I find that my email is wrong,
and now another user that email is '456#google.com' can't signup again.
I just want to a email is verified that can associate with the user.
what's your advice?
If you want to use email as your unique sign in key, it would save you a lot of trouble in future development of your website if you make a custom User model using AbstractBaseUser. If you want i can post a sample working code
In reference to your second question - You can use Cryptographic signing in Django (https://docs.djangoproject.com/ja/1.9/topics/signing/) to produce a key. Further send this key as a link (eg www.example.com/verify/:some_crypto_key:) and send it as a link to user's email address. This key will contain user id and time stamp. If you receive a request on that link, it means that email is legit. You may find a package that does a similar task maybe.
EDIT:
Implementation (short way) - As the user signups on your website, Immediately ask him/her to verify account using the link you have sent to the given email. If you do not receive a response from that email within a given time (say 20 mins), delete that user entry. This means that you can not let the user access your website until he/she verifies the account.
Flaw - Consider a situation where the user has submitted a wrong email. It is obvious that the user will never be able to verify it but for those 20 mins if co-incidentally the actual user with that same email tries to signup on your website, he won't be able to access. This is very unlikely. Also this user will receive an email from your website saying that user has signed-up on a website (so here you can provide another link, 'if this was not you, please click here' kind of thing)
Unless you have a burning desire to write your own custom user model, which will let you replace the username field with the email, I would recommend using something like Django AllAuth. It includes email verification (as outlined in your question), and can be set to use email as username fairly easily. It's a well established library with lots of support, and will be more immediately usable than rolling your own.
(That said - rolling your own is an illuminating experience, and RA123's point is the answer you should accept if you're going down that road.)

Can I use a hashed password as the secret for generating a hmac?

I think it would be very comfortable to use the user's password hash as the secret for generating a hmac. Why is OAuth and others using tokens and nonces?
I think of something like this:
Client enters a password in the ui.
The application registers with the webservice using the hash of that password, which is stored on the server.
Form now on that hash hasn't to be transmitted again.
The client can always regenerate the secret by asking the user to enter the password and hashing it. Every message is signed with this hash, the server can look it up by username or guid and check if the sent mac is valid.
A intruder on the server can get that hash, but doesn't know the users real password, anyway he could send valid request with that hash. But this is not likely to happen, the saved hashes could also be hashed again using a nonce. Anyway because the pwd-file will be on a client's server it should be obfuscated using e.g. base64 to avoid the file looking like {"password":"a4bd146hashhashhash"}.
Most of all the real password of the user won't ever be transmitted. The request's will be secured with a timestamp/token against replay (I recognize the purpose of the token here).
Sending a hash would be perfectly applicable for me because the client will never be a simple website with a tag e.g.. The webservice will be used in a ajax-based application and a java desktop application, both of them capable of hashing strings...
What's wrong with that? It's so simple, more RESTFul than anything related to authentication, and i think yet effective. What am I missing?
Greets, kruemel

What is the best way to store password in database when API call requires sending of password in plain text?

I'm writing a web app in Django that is interfacing with the Read it Later List API (http://readitlaterlist.com/api/docs/). However, all of the API calls require sending the username and password with each request. In order to do this, it seems that I need to store the user's password in plain text in my database. Am I missing something or is that the only approach possible in this case? Is there some way to store an encrypted password in my database, but yet be able to send a decrypted version of it when making the API call? If not, then are there any best practices that I should be following to safe-guard the user's password?
I'm pretty sure that this can't be the only service requiring sending password in plain-text. I'm interested in knowing how developers deal with these sort of services in general. I'm new to web development, so any help or pointers is appreciated.
do your users have to log into your website to use it? if you also are making use of a password authentication scheme, you could piggy back on top of that. Use the login password for your site as a cipherkey in a symmetric key cipher to encrypt the api password. then you need only store a hash of the users password (to your own site) and an encrypted password for the remote api.
Never save password in plain text. You can encrypt and decrypt the password but the problem is that the key you use to do the encryption and decryption will generally be accessible to anyone who has gained access to your server so it's not secure.
An alternative is to ask them to enter their password and save it in an encrypted cookie, or session variable or something else that will expire when they have logged out of your app. This has the drawback of them having to enter their password every time they user your app.