
Show HN: Link-lock – Distributed app to password-protect URLs - jstrieb
https://github.com/jstrieb/link-lock
======
jstrieb
I originally made this as a complement to the URL Pages tool I made last
summer, which stores an entire web page in its own URL.[0] Now, it is possible
for URL Pages to be securely password-protected in a decentralized way.

Unlike other private bookmarking tools, this runs entirely in the browser, so
it can't track users. Also, it only needs a static web host in order to run,
minimizing the amount of infrastructure I have to maintain, which increases
the likelihood it will still be running a decade from now.

Please note: I am a college student, not a security professional. There are
certainly best-practices I am not aware of, and this is my first foray into
using encryption. I have done my best to balance user experience and security,
but am always looking for feedback!

[0]:
[https://news.ycombinator.com/item?id=20317840](https://news.ycombinator.com/item?id=20317840)

~~~
627467
Amazing tool! And of course by the same person who did urlpages! Just this
weekend I was exploring embedding a whole deterministic password gen into
urlpages so I could have a syncable password manager with little
infrastructure but obviously I'd be trusting whoever syncs my bookmarks... Now
I don't need to worry anymore!

~~~
jstrieb
Glad to hear you like URL Pages! Because neither this project, nor that one
use any tracking or analytics, my only indications that people are using it
are GitHub stars, GitHub insights (with information about repository views),
and the occasional message I receive on my website. I have many planned
improvements for it, and knowing that others continue to find it useful is a
great motivator.

I'm always looking for additional feedback on all of my projects, so don't
hesitate to reach out with any additional thoughts, whether positive or
negative! Contact info can be found on my website about page:
[https://jstrieb.github.io/about](https://jstrieb.github.io/about)

------
egberts1
Now if we can have some kind of a timed-self-disappearing URL to go with this?

I had a rough idea outlined but this OP here effectively deals with any
IDS/IPS trying to visit the link and access it. (And also deals with fixed
retrieval count of such URL for my design)

[https://egbert.net/blog/articles/one-time-
url.html](https://egbert.net/blog/articles/one-time-url.html)

------
XMPPwocky
At the risk of being annoyingly negative - as a webapp, this design does
require you to trust the web server hosting it (it could just send you JS to
steal your password!)

Instead, maybe generate a data: URI with the decryption code on it. (If this
would make it too long, could still load the main JS from a server via XHR,
but check the hash of it before executing.)

~~~
applecrazy
> this design does require you to trust the web server hosting it (it could
> just send you JS to steal your password!)

The author mentioned elsewhere in this thread that it's all static and client-
side, so if that was a concern, you could just clone the repo and just open
the HTML file in your web browser (no web server needed!).

~~~
XMPPwocky
Yeah, but when you're sending files around, you've already sort of lost the UX
battle.

The suggestion of a data: URI might be a way to get guarantees similar to that
of a static file, but in a URL.

------
riquito
Interesting concept, thanks for sharing.

It looks like the password is transformed to bytes by decoding base64, which
is not a realistic assumption. You could use TextEncoder().encode() instead.

The nonradom iv is very dangerous, you could randomize that one and the salt
every time, and read them from the additional data before decrypting. Never
propose fixed values as an option

~~~
jstrieb
Thanks so much for the feedback! Based on this and tptacek's comment,[0] I
will likely remove the ability to disable randomizing the IV in future
versions. Luckily, this will not break any encrypted links generated thus far.

Unless I am misunderstanding your comment, I do actually use TextEncoder and
TextDecoder objects for doing some ascii <-> Uint8Array conversion in the
base64 library I wrote,[1] but I have found that they are not actually
inverses, leading me to minimize their use. To be fair, I may just be doing
something incorrectly. However, in a number of my test cases, I have found the
following to be true:

    
    
      (new TextDecoder).decode((new TextEncoder).encode(someText)) != someText
    

Also, note that if the base64 decoding fails, there will be an error shown
explaining that the encoded URL appears corrupted. Does this address what you
mean about the password being transformed to bytes via base64?

[0]:
[https://news.ycombinator.com/item?id=23253488](https://news.ycombinator.com/item?id=23253488)

[1]: [https://github.com/jstrieb/link-
lock/blob/master/b64.js#L41](https://github.com/jstrieb/link-
lock/blob/master/b64.js#L41)

~~~
riquito
Oh, `b64.asciiToBinary` tripped me off, I thought it was about base64, but it
encodes any string using utf-8, which is fine for what you use it but a bit
confusing to the reader.

About TextDecoder/TextEncoder not being the reverse of one another, I suspect
you are not feeding it text but random bytes, which can cause that behaviour.

~~~
jstrieb
This is good feedback, I will make a note to adjust this later. Thanks!

------
fr2null
While I like the idea, it does seem very gimmicky to me. The only real reason
I see to use this, is ease of use, but then, why only encrypt URLs. Why not
just make an easy tool to encrypt small text fragments?

That said, I does look like a good exercise and a nice way to get familiar
with crypto in the browser. Kudos for making it.

~~~
jedimastert
From OPs comment

> I originally made this as a complement to the URL Pages tool I made last
> summer, which stores an entire web page in its own URL.[0] Now, it is
> possible for URL Pages to be securely password-protected in a decentralized
> way.

> [0]:
> [https://news.ycombinator.com/item?id=20317840](https://news.ycombinator.com/item?id=20317840)

------
tptacek
I don't understand the "non-randomization of IVs" feature, since GCM is
totally insecure if an IV is reused. Is this code deriving deterministic IVs
from the plaintext or something? Or can keys simply not be reused? It seems
like they can, if KDF salts are by default non-random.

~~~
jstrieb
Thanks for your comment! The basis for the feature is that it makes the
generated URLs a few characters shorter – empirically, savings are on the
order of 20-25 characters. Minor for most, but I don't purport to know what
users will do with the tool, or if this length will make a difference.

Under the hood, once encryption is performed, the version number, encrypted
data, salt and IV (if randomly-generated), and hint (if given) are JSON-
stringified and base64-encoded for storage in the URL fragment. There is a
hard-coded default salt and IV if the user chooses not to randomize, which
means they don't need to be encoded in the object that is eventually stored in
the URL, thus making the generated URL shorter.

Based on your comment, it seems hard-coding an IV and giving the user the
ability to disable randomization may unnecessarily make unwitting users
vulnerable. Would you recommend I remove the ability for users to disable
randomization moving forward? Do you think I should just add a big warning if
they try to uncheck the box instead? I'm open to suggestions.

In general, I'm very interested in all recommendations for best-practices, as
the tool is under active development, and is a learning opportunity for me.

~~~
prophesi
A hard-coded IV with AES-GCM is bad only if you're encrypting content with the
same key.

Since you're deriving keys via a password, that would mean that two Link-Lock
URL's protected with the same password _and_ using the same hard-coded IV
would be vulnerable.

It's likely an infeasible attack, but definitely worth giving a warning to
those who really want to cut back on those 20-25 characters.

Though since the URL fragment has _encrypted_ content, I think the users
should understand up-front that the URL's will be long; it's impossible not
to.

------
jedimastert
Is there a reason the password boxes are regular input fields and not password
fields?

~~~
jstrieb
Thanks for your comment! When creating the encrypted URL, the password entry
uses proper password fields, but the decryption prompt does not.

I originally made the decryption password prompt use a JavaScript
window.prompt because I didn't want to risk users' hints posing an XSS
vulnerability. Unfortunately, that means entering the password in the prompt
is not done with a proper password field, and is instead done in plaintext.

Eventually, I hope to build out a better UI for decrypting links. Handling
this is definitely on my list of planned improvements!

------
h43z
Cool idea! I love those small experiments.

------
mrkramer
This is so useful, thanks for making it!

