Open cktang88 opened 4 years ago
Thanks for taking a look at the project! Theoretically, that is absolutely possible! Unfortunately, I am not aware of anything I can realistically do to protect against it. For example, given that all encryption and decryption is done client-side, I could not effectively perform rate-limiting.
Out of curiosity, I wrote a quick brute-force tool to try it out. While I know that nobody would actually ever brute-force anything too seriously in their browser, I coded up a web-based brute-forcer that will attempt to decrypt locked URLs by trying all combinations of characters in a user-submitted charset. I made a browser-based implementation simply because I could directly import the APIs I'd already written for the main project.
You can try my brute-forcer here. The code for it is here if you would like to modify it. I have included a link to my brute-force application below. In practice, using this very naive implementation of brute force, it turns out that even relatively short passwords with limited character sets take a very long time to crack! For me it tests about 22 passwords per second.
In the meantime, I will leave this issue open in case others have ideas for how to protect against this.
In the spirit of openly discussing this issue, I have added the code to brute force locked links directly to the main repository. Users can attempt a brute force in their browser here:
A solution is to use scrypt or bcrypt instead of directly AES.
So you can provide complexity parameters that will make very hard for an attacker to brute force the password.
A solution is to use scrypt or bcrypt instead of directly AES.
Since a user with the password must be able to recover the original URL, I need the encryption to be symmetric, and cannot use a hashing algorithm for this. Thus, I wouldn't actually be able to replace AES with bcrypt
or scrypt
. I could potentially use those hash functions in place of SHA-256 for secure key derivation, but unfortunately neither algorithm seems to be available in the SubtleCrypto
API.
As a fun project while learning Go, I wrote a cross-platform, command-line application to brute force Link Lock URLs. It parallelizes on as many CPU cores as possible. Find it here: https://github.com/jstrieb/bruteforce-link-lock
Interestingly, while profiling this code, I discovered that (perhaps unsurprisingly) the bottleneck for each attempt is the 100,000 iterations of SHA256 used for PBKDF2 key derivation. What did surprise me is that the Go code's rate of attempts per second per thread does not seem to be significantly better than the browser (both between 15 and 20 password attempts per second), so the only benefit of this new code over the browser-based brute force tool is parallelizing across CPU cores. Turns out the SubtleCrypto
API is pretty fast!
Not seeing that work right the - I have roughly 52 threads and its only running on one.
hey @jstrieb i have a question about where the password is being stored in the client side?(is it inside the url itself)?
hey @jstrieb i have a question about where the password is being stored in the client side?(is it inside the url itself)?
Hi @cool-dev-guy! The password is not stored at all. Only encrypted data is stored. And all of the encrypted data is stored in the URL.
To give a concrete example, here is the structure extracted from one of the example URLs in the README.
jacob@jacob:~$ echo "https://jstrieb.github.io/link-lock/#eyJ2IjoiMC4wLjEiLCJlIjoiZEx3Yi9CNitlK0ZjM1B3ZURrbUY2NjdQWFlIV1dsS3dpclhvZmkvRXBFTXU0ZERlVkJuSmUrN1loS2JxQ3RrPSIsImgiOiIxICsgMSA9ID8iLCJpIjoiRDJYd1MyK1EzaHpuUDV1NyJ9" \
| sed 's/.*#\(.*\)/\1/g' \
| base64 -d - \
| jq
{
"v": "0.0.1",
"e": "dLwb/B6+e+Fc3PweDkmF667PXYHWWlKwirXofi/EpEMu4dDeVBnJe+7YhKbqCtk=",
"h": "1 + 1 = ?",
"i": "D2XwS2+Q3hznP5u7"
}
Within this extracted JSON structure:
v
is the API versione
is the base64-encoded, encrypted bytesh
is the (optional) password hinti
is the Initialization Vector (IV) for AES-GCMThe password itself is not stored in here, but all of the encrypted data is entirely in the URL. If we go one step further and try to inspect the encrypted data, we'll see that it doesn't have any structure itself, and is just gibberish bytes.
jacob@jacob:~$ echo "https://jstrieb.github.io/link-lock/#eyJ2IjoiMC4wLjEiLCJlIjoiZEx3Yi9CNitlK0ZjM1B3ZURrbUY2NjdQWFlIV1dsS3dpclhvZmkvRXBFTXU0ZERlVkJuSmUrN1loS2JxQ3RrPSIsImgiOiIxICsgMSA9ID8iLCJpIjoiRDJYd1MyK1EzaHpuUDV1NyJ9" \
| sed 's/.*#\(.*\)/\1/g' \
| base64 -d - \
| jq --raw-output .e \
| base64 -d - \
| xxd -
00000000: 74bc 1bfc 1ebe 7be1 5cdc fc1e 0e49 85eb t.....{.\....I..
00000010: aecf 5d81 d65a 52b0 8ab5 e87e 2fc4 a443 ..]..ZR....~/..C
00000020: 2ee1 d0de 5419 c97b eed8 84a6 ea0a d9 ....T..{.......
Hopefully this answers your question!
Hopefully this answers your question!
Thanks jstrieb for the answer,it really helped me.
hey @jstrieb ,i have a doubt about encryption,is it possible to encrypt a string(length < 280chars) to an encrypted string of length<280 characters?
For example, a potential attacker could potentially keep on guessing a password many times via brute-force until the link unlocks.