Intro
I've actually been thinking on this a while myself, and I agree that using a password manager isn't ideal for password security.
- Because it introduces a single point of failure, which in my book is too great a risk. I don't want to entrust a third party with my security, or place the weight of security for all my accounts onto the strength of one password.
- Because you either need to store your passwords in the cloud which is potentially accessible to everyone, or keep local copies and run the risk of losing the data and access to all of your sites with it.
The best solution I can come up with is one that has been mentioned in a few different answers already: using an algorithm to generate passwords. Although it's been previously mentioned, I feel that none of the other answers have quite provided a good implementation of this method yet, nor have they touched on several noteworthy points.
Generating Passwords Using Hashing/Encryption
A secure password generating algorithm should do three things:
- Generate a secure password
- Make it easy for the user to retrieve their passwords
- Be inefficiently reversible without the initial input
So, my solution to this would be to run your password through the following generalized algorithm:
ENCODE(HASH("password" + "salt"))
My ideal suggestion would be to use:
Base85(SHA3-512("password" + "salt"))
Essentially, your password will first be run through a hashing algorithm to generate a random, irreversible output (in this example SHA3-512), and then encoded to give the password a large range of character values which increases it's resistance to hashcracking exponentially (here I've chosen Base85).
I suggest setting the password as the base domain of the site you are trying to log into, and setting the salt as your "password".
A concrete example:
Base85(SHA3-512("security.stackexchange.com" + "MyPassword"))
Preforming SHA3-512 on the string "security.stackexchange.comMyPassword"
yeilds
8b6fe2cfd65d5faf4f3babe823afa844a9eb9c36dc6c1459b0f430a0f77318052318ce6b607eab0114f990aa52bb2a7acd06735f8590c2886bbc68ca42d05328
And after running this through Base85 you get
<~3+=dXAMRb-A2Z;U2.g9/1hJOQ@:Ee-1,E?N@5p)#@6%n.3Faj'A7IN-0f1aJ@PDCN1GE5kAiDY)0fUjE1,CXE@q@Q-2D@($@:CoF0f3K&3A=lM2)8WM11<+M@q78O2`!='3&N]M@kp#'2I^-*2E52T1brSq2)@!I~>
I think you could argue the above password is a strong one, and all you have to do is remember "MyPassword".
Altering My Example
Obviously, this concept could be applied with any hashing algorithm and encoding scheme, and whatever pattern you want as the input. Be wary though, the security of your generated password relies on what you choose for the algorithm (as well as your base password!). Some things to note:
- When it comes to hashing, hashing twice doesn't really add to the security of the password. If you use two different hash algorithms, in theory the resulting hash is only as secure as the weakest one in the chain. Take
SHA1(MD5("pass"))
for example. MD5()
produces a 128 bit hash, where as SHA1()
produces a hash of 160 bits. By doing this, you are essentially mapping 2128 inputs onto 2160 outputs. By theory of the pigeonhole principle, at a maximum this algorithm can only generate 2128 possible outputs, which undermines the security of SHA1()
.
The same result occurs with MD5(SHA1("pass"))
, but what about SHA1(SHA1("pass"))
? By using SHA1()
twice, you are mapping 2160 inputs onto 2160 outputs, which theoretically should be fine, but in practice it is not because of hash collisions. Any two strings A
and B
that collide on SHA1()
will collide on SHA1(SHA1())
. Furthermore, two unique hashes SHA1("C")
and SHA1("D")
may collide on SHA1()
! This means that not only will SHA1()
output <2160, but it will also have roughly twice the chance at forming hash collisions.
TL;DR hashing a string twice only weakens the security of that hash, because instead of mapping ∞ onto n bits you are mapping <∞ onto n bits.
- You could exchange a hashing function for an encryption function, but I would argue that this wouldn't increase the security of the algorithm at all. If we assume both the encryption function
E()
and the hashing function H()
are equally secure, then they're essentially doing the same thing: On one hand E()
is using the string "MyPassword"
as a key to encrypt the domain "security.stackexchange.com"
while on the other hand H()
is using "MyPassword"
to salt and hash the domain.
I want to put a disclaimer here that I am well aware of the difference between encryption and hashing. They are two separate types of string permutation, but in the context of generating a password they are essentially the same. If you don't know the difference between hashing and encryption, you can learn more here, and this is another interesting, related article.
- Changing the encoding is a possibility, but I think Base85 is that sweet spot. If you go with something lower like Base64 (or any encoding scheme that generates output from a smaller pool of characters) you're only making your passwords weaker. You could increase the encoding past Base85, but eventually you start to hit outputs with non-printable characters and issues would arise from there.
One issue with encoding is that there are sites that actually limit the length and what you're allowed to input for passwords. This is utterly ridiculous because the only thing you should be doing with a user password is hashing it down to a fixed length and storing it. Password fields shouldn't offer any sort of vector for attack, not even SQL Injection because the data should be hashed before it even touches a query. Alas, there are sites out there that restrict password field inputs, so in rare cases Base85 might not work universally.
- Lastly, it's completely acceptable to use multiple passwords for this solution, and even different Hashing/Encryption algorithms for different sites. If you choose 3 strong passwords and 3 strong Hashing algorithms, you now have 9 generators to draw from! Of course, if you extrapolate too much then you'll find yourself with the exact same problem this method aims to solve, but I believe there is a happy medium to this.
Perhaps create 3 base passwords to insert into this algorithm, one for sites you don't care about, another for sites you partially care about, and a third for sites with sensitive information on them. Or you could do the same but with different Hashing/Encryption algorithms. The world is your oyster here, as long as you can remember them all, the more the merrier.
The Security of this Method
Theoretically, using this algorithm to generate a password is no more secure than using your password in the first place. This is because if the attacker gets ahold of your base password ("MyPassword"
), and assuming the algorithm is public (which it would need to be for this method to become widespread), there is nothing stopping them from generating your passwords for each site you visit.
In practice however, I would argue this method IS more secure. You can look at it from several different attack vectors:
Database Breach
If an attacker were to breach a database and collect the stored password hashes, his only route of attack would be to crack them. Brute force would take too long and a Mask Attack wouldn't reasonably succeed either with the Base85 encoding.
If the passwords weren't hashed (pray for that DB Admin) then the attacker could easily decode the Base85 encoding. But then all he'd be left with is a hash. Although he wouldn't need to crack this hash (or even strip the encoding) to login as you on that site, he wouldn't gain access to any of your other accounts without first discovering your base password.
Man In The Middle Attack
Ultimately, the hashed and encoded password you generate is being sent over the wire to the website. Hopefully that site uses HTTPS, but not all of them do. If an eavesdropper is sitting on your connection and they manage to read this password they can login as you, but like mentioned above it will only be for that site. Without your base password the eavesdropper will have no way to access your other accounts. This is a better outcome than if you didn't generate passwords, because in that case the eavesdropper could login to any account in which you've used the same username/password combo.
Brute Force/Mask Attack
If a malicious actor is attempting to brute force your base password, then your generated passwords will be no more secure than using a regular, non-generated password (assuming the actor knows the algorithm you used to generate those passwords). In this case generating passwords doesn't add any extra security, but it doesn't diminish the security at all either. This attack vector requires you as an individual to be targeted as well, which in practice is much less likely to happen than the above methods. That said, it's still possible, so make sure to choose a secure base password.
Uncovering Your Base Password
As it stands, the ONLY way an attacker could generate the passwords for all of your sites is if they knew your base password. There are only two ways they can get this (without you directly telling them).
- Crack the generated hash of your base password
- Brute force your algorithm until they come up with your base password
First of all, by attempting #1 you are actually using #2. The most efficient method of cracking a hash IS brute force, so these are essentially the same thing with minor differences.
As long as you have chosen a secure base password, both of these methods should be close to impossible. This is because the attacker would need to come up with your EXACT base password to gain access to all of your accounts. Only with your EXACT base password can an attacker correctly generate your other passwords, hash collisions don't even factor in here.
To explain further, say you have your generated password P1 where P1 = G(S1) and S1 = p + s1 (G = generator algorithm, p = base password string, s1 = salt). If an attacker procured P1 and found some S1' such that P1 = G(S1'), then S1' would HAVE to equal S1 or the attacker wouldn't have access to your other accounts. Just because S1' evaluates to P1 does not imply that S1' will evaluate to P2 where P2 = G(p + s2).
Hopefully I didn't lose anyone with that explanation.
Outro
To specifically answer the OP's questions:
Is it possible to create such a good password scheme?
I think the method I've outlined in my post is a sufficiently good password scheme. It gives you a unique, strong password for every site you have an account on without straining your brain to remember all said passwords individually. All you have to remember is the algorithm and base password(s) you used.
Would that method provide security that's not substantially inferior to a well-designed password manager?
At it's weakest, the method I've given is equally secure to using regular passwords, and a PM to store them.
- When using a PM, you are essentially encrypting each (password, website) pair for every site with a single key. Using this key gives you access to all of your accounts.
- When generating passwords, you are essentially encrypting (or hashing) each password so that the only way to get that password is by knowing the key. Using this key gives you access to all of your accounts.
However, at it's strongest, you could reasonably say that this method is more secure than a Password Manager. Namely because You aren't entrusting someone else to keep that base password safe for you. The only thing you have to trust is your brain, and as long as you don't go crazy that's not such a scary thing to do. :)
PS: If there are any oversights to my solution or extra tidbits that anyone thinks I should add, please comment and let me know. I would be happy to improve this answer by editing in any valid points you guys come up with.
arxjzxgbf
instead ofstckxchng
(one to the left on the keyboard). to prevent target attacks like discussed.