jstrieb / link-lock

Password-protect URLs using AES in the browser; create hidden bookmarks without a browser extension
https://jstrieb.github.io/link-lock
MIT License
867 stars 161 forks source link

Is there protection against brute force attacks? #1

Open cktang88 opened 4 years ago

cktang88 commented 4 years ago

For example, a potential attacker could potentially keep on guessing a password many times via brute-force until the link unlocks.

jstrieb commented 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.

jstrieb commented 4 years ago

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:

https://jstrieb.github.io/link-lock/bruteforce/

yogsototh commented 4 years ago

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.

jstrieb commented 4 years ago

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.

jstrieb commented 3 years ago

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!

PrinceOfParallax commented 2 years ago

Not seeing that work right the - I have roughly 52 threads and its only running on one.

cool-dev-guy commented 1 year ago

hey @jstrieb i have a question about where the password is being stored in the client side?(is it inside the url itself)?

jstrieb commented 1 year ago

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:

The 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!

cool-dev-guy commented 1 year ago

Hopefully this answers your question!

Thanks jstrieb for the answer,it really helped me.

cool-dev-guy commented 1 year ago

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?