Hacker News new | past | comments | ask | show | jobs | submit login
Password protect a static HTML page (github.com/robinmoisson)
653 points by mooreds on Feb 18, 2023 | hide | past | favorite | 266 comments



Gad! I have spent all morning trying to --- password protect a static html page. How in the hell does HN have a story about the exact topic I've been struggling with appear just a dozen or so hours after I started working on it. Wow.

I was _extremely_ happy to see this posted.

However when I click the link I am taken to the library I had initially tried and had to reject. Without getting into the crypto side of things (which very well may resolve to "just make it tough enough for most folks") I crashed and burned using this tool once my page reached certain sizes. (I was cramming everything I needed into one page and I had a big hunk of bytes that I didn't want hanging out on the filesystem)

As I understand it, this is a known issue in V8. V8 limits the number of properties/collection members an object can have. There's a flag you can use with node to get a little more headspace, I believe it's something like "--max-old-space-size=8192" but it's a V8 problem, not a node problem. The staticrypt coders could also switch to a streaming cryptography instead of block, but that's far too much to ask of the casual user who only wants to lock up a page or two.

This is a really cool tool, and I wished I could have used it. Good luck to the team going forward. I've gotta find something else as this doesn't work for me.


If your server supports it, a fast and easy way to password protect a page is to put it in a folder and password protect that folder with htaccess:

https://ithemes.com/blog/what-is-the-htaccess-file/#password...


Keep in mind the same server will lock you out for a day or so if you autofill the wrong password, be warned. :D


No it won't, just restart the browser if cleaning cookies didn't work. Entered password is only valid for a session.


HTTP basic authentication is stateless


This could be true if the server is using something like fail2ban.


Something similar has been posted before some time ago: https://news.ycombinator.com/item?id=34083366


Which in turn has inspired the creation of PrivacyProtect [0]. It allows to do basically the same thing as the other applications, but in my opinion the interface is better, it also allows you to directly select a file which I guess is easier than copying the contents, it also means you can just select an image file for instance or some other type of file other than a web page.

[0]: https://www.privacyprotect.dev/


Why is everybody forcing me to upload to the web the very files I want to keep private to myself? If any code in the universe should run locally, it's this kind of code.

I don't get it.

Great UI, though. But I still don't get it.


That the frontend is hosted on someone else's server is a convenience, but I see your point and agree. Technically nothing is uploaded anywhere, nor does it call any APIs to do the de/encryption. Instead, it uses the cryptography mechanisms built into your browser. Local file access and creation are via FileSystem api[0]. Said differently, you could save the page to your desktop—ensure all the includes are to local files, and still run it. In fact, thats's what I did with Portable Secret[1] which others noted was previously posted here. I can confirm this works and nothing "calls home".

[0] https://developer.mozilla.org/en-US/docs/Web/API/FileSystem [1] https://mprimi.github.io/portable-secret


> Why is everybody forcing me to upload to the web the very files I want to keep private to myself?

This isn't the use-case I had in mind.

My personal one: I want to build a small utility web app for a very specific task, and it would be used by me and by some of my friends (less than 10 users total). The entire thing can easily be front-end code, which is nice, because I can just host it on Netlify and not bother with hosting, managing it, etc., thus making the complexity of it all much lower.

But there is one catch - the web app uses an external API, and, along with it, the API access key. This puts a big stop sign for making it front-end only. And I don't want to up the level of complexity by managing the back-end just for the API access key retrieval (for a webapp used by less than 10 people). Exposing the API key in the front-end code on a publicly accessible website is one of the worst ideas I can think of.

I haven't done the evaluation on how well the solution in the OP would serve my specific purpose, but it looks promising.


That project and the parent do exactly what you wish:

- run encryption locally

- do not upload anything

You may not realize that’s the case because it’s a web form


https://github.com/robinmoisson/staticrypt#encrypting-multip...

> This allows encrypting multiple page on a single domain with the same password

This might still work for you if putting everything on one page isn't a requirement. Using the "remember me" checkbox means the user only has to input this password once.


> I have spent all morning trying to --- password protect a static html page

Curious. Can you say more about your use case, even if only in general terms?


Is there a reason you can't load it up as a pdf with pdf.js?


I'm not a cryptographer, but I'm pretty sure that CBC (https://github.com/robinmoisson/staticrypt/blob/main/lib/cry...) should be replaced with GCM (https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypt...) since this is not a stream.

https://security.stackexchange.com/questions/184305/why-woul...

(also, use the built-in WebCrypto API instead of the crypto-js package)


I don't know what "being a stream" has to do with anything (you should virtually always use "stream" modes of AES); the meaningful distinction between CBC and GCM is that GCM authenticates the ciphertext, so you can't tamper with it.


Also I checked the code quickly, they do Cbc-hmac, and they check the Mac before decrypting so it seems fine.

The 1000 rounds only of pbkdf2 on something that's essentially going to be available without any access control (since the encryption is the access control here) might be more annoying as the password can be human generated.


> so you can't tamper with it.

... if the API provided is correctly designed, and either the language made it practical to design the API in a misuse-resistant way /or/ the programmer was actually careful and used it properly.

We both know it's way more likely the problem in your actual system is "Oops, we use the bogus plaintext here despite the validation error" than "Dastardly enemy cryptographers have made a breakthrough in cryptanalysis to attack our product".


I'm not thinking very hard about it; there is just never a reason to use unauthenticated encryption modes. When I wrote that comment, I hadn't even looked to see if they'd manually authenticated their ciphertext (spoiler alert: you can guess).


SubtleCrypto (Web Cryptography API) will simply throw an exception if the authentication fails for AES-GCM, so there's no way to even get at the bogus plaintext.


CBC is Cipher Block Chaining.

I think you might be referencing that the incorrect use of CBC leads to chosen-plaintext attacks, chosen-ciphertext attacks, or padding oracle attacks. GCM is AEAD, providing ciphertext authentication. [1]

For those reasons that you mention, use of encryption should always have a design review.

[1] https://en.wikipedia.org/wiki/Authenticated_encryption#Authe...


The readme links to this issue wrt that decision https://github.com/robinmoisson/staticrypt/issues/19

It seems that the hmac validation makes the GCM unnecessary in their thread model


CBC is far easier to implement than GCM mode.

I bet that most programmers cannot implement a Galois Field in Javascript. Meanwhile, CBC is just "encrypt then xor".


> CBC is far easier to implement...

Never implement your own cryptography.

Edit:

In fact an incorrect implementation of CBC mode famously caused a vulnerability in Microsoft's ASP.NET in 2010 (https://learn.microsoft.com/en-us/security-updates/securityb...). The margin for error is small and even subtle mistakes or incorrect design can cripple security. Even Microsoft got it wrong once (although they handled remediation very well).


Or... you can have a more nuanced viewpoint and note that CBC is really, really, really easy to implement, and _really_ doesn't fall into that category of discussion.

The reason you don't implement your own block ciphers is because side-channel attacks are damn near impossible for normal programmers to understand. Especially timing attacks.

But block-modes of operation? Some of them are really easy. I've ever heard of a bad implementation of CBC causing a security bug.

-------

I'd say you shouldn't implement your own GCM mode. GCM is quite complex, and the Galois Field's authentication bits could be side-channeled if you don't know what you're doing.

CBC? Where's the flaw? Its so stupid simple I don't think that even a novice would make a critical error.


You keep saying "the Galois Field" as if that was a thing. It's GCM. The components of GCM are CTR mode and the GMAC authentication code, which is based on GHASH. If you're afraid of Galois fields, you don't get to use AES at all! Nobody should be implementing any of these primitives themselves, very much including CBC, which, as you saw downthread, left both you and the author of this project with an insecure cryptosystem.

Vulnerabilities in CBC systems were for a long time during the 2000s the most common crypto vulnerabilities on the Internet. There are more things that go wrong with CBC mode than just forgetting to authenticate it!


> You keep saying "the Galois Field" as if that was a thing

You're kidding, right? You've never looked at how GCM-mode works? The entire set of math is inside of the GF(2^128) field. That's why its called a Galois Counter Mode.

I don't think anyone should be implementing their own GCM mode. Its very subtle and potentially full of traps. CBC on the other hand is pretty dumb and simple, and surprisingly secure and robust

> Vulnerabilities in CBC systems were for a long time during the 2000s the most common crypto vulnerabilities on the Internet. There are more things that go wrong with CBC mode than just forgetting to authenticate it!

If they're so common, you shouldn't have much of an issue naming one such vulnerability.


The math used in AES (Rijndael) utilize operations in GF(2^8) tho, so you're doing operations using Galois fields whether your utilizing GCM or CBC. I don't really see how adding the GCM mode utilizing GF(2^128) on top is significantly more difficult or error prone than implementing the AES block cipher itself. You should still be familiar with operations over Galois fields regardless if you've for some reason (foolishly imo) decided you want to implement AES cryptographic primitives on your own.

Regardless there's no good reason not to use a vetted open source implementation instead, preferably with an even higher level of abstraction so your not having to worry about ciphers or modes of operation at all[1].

[1] https://doc.libsodium.org/secret-key_cryptography/secretbox


The library used in this Javascript widget has AES already implemented, but not GCM mode.

> Regardless there's no good reason not to use a vetted open source implementation instead, preferably with an even higher level of abstraction so your not having to worry about ciphers or modes of operation at all[1].

I think that's generally the preferred solution, yes.


I think some of my comment went over your head.


BEAST and POODLE were both high profile attacks against how SSL used CBC.


But neither were attacks on CBC itself. That is to say: to "fix" BEAST or POODLE, you don't change a lick of CBC code at all.


> Or... you can have a more nuanced viewpoint

The nuanced viewpoint is never implement your own cryptography.

> Its so stupid simple I don't think that even a novice would make a critical error.

Ask Microsoft about that one: https://learn.microsoft.com/en-us/security-updates/securityb...


That's a Padding Oracle vulnerability, not a CBC error.


It's a CBC padding oracle, intrinsic to the operation of CBC --- if you PKCS7-pad some other mode, you do not get the same attack --- the single best-known cryptographic vulnerability on the Internet, and the parent commenter took the time to give you the most famous instance of it. For the record: that's also not the only CBC vulnerability.


The padding is a key part of using a block cipher in CBC mode.

I’m not sure why you’re ferociously defending the practice of implementing your own cryptography. It’s well known that this is a horrible idea for good reason.


Padding is an important part of CBC.


[flagged]


> Furthermore, padding oracles are completely irrelevant to data at rest, like as described in this topics use case. So it really is a bit of a non sequitur too.

That's not true and extremely dangerous to say. In an offline, black-box scenario no server is needed for a padding-oracle. You are thinking of a side-channel oracle. A padding-oracle attack can absolutely be feasible in many cases.


I appreciate your efforts at actually elevating this discussion. Yes, I said something too general when I really was just trying to describe _this_ particular HTML-page encryption implementation.

You're right that its possible that an offline decryption algorithm could in fact be a padding oracle under the right scenario. But I still posit it probably doesn't apply to this static-HTML page generator.


By that metric all security vulnerabilities can be explained away as a fault in a different part of the system.


No, to all of this.


Nobody implements GCM themselves; they get it from a library. CBC, implemented the way you're describing, is almost always insecure.


As the old quote goes - "if you are typing "AES" into your code at any point, and you are not a world-renowned crypto expert - you will get this wrong, guaranteed". There are so few cases of people who are not seasoned crypto engineers getting everything 100% correct that it's basically a myth, and in crypto, getting things "almost right" is the same as getting them wrong...

I too loved playing with crypto - read a ton of books, attended some crypto classes in University, implemented some crypto systems which I thought were secure, etc. - but nowadays I wouldn't dare use anything that isn't considered secure by a large audience unless I had extremely good reason to do so.


And the library they use doesn't have GCM mode.

So now what?

https://cryptojs.gitbook.io/docs/

Because CBC mode is easier to implement, you'll find it in far more libraries. And honestly, if your underlying block-cipher is secure (that's the hard part: where your side-channels all exist), then CBC mode is really the easy part and can be safely implemented yourself. It really is that simple.

-----------------

CBC doesn't have authentication. So add on an HMAC. Done. It takes up a few more bytes but that's not a big deal these days.


So use WebCrypto, instead of the cabinet of curiosities that is crypto-js. (Or, better yet, don't do stuff like this at all, because a browser window is simply an awful setting for serious cryptography).


WebCrypto only works in secure contexts which is a significant limitation (https origins only)


If you're talking about sending these files around via email/messenger, most browsers treat localhost as a secure context, so this doesn't seem like that significant of a limitation.

If you are serving out an encrypted HTML page insecurely, your users are already hosed, because someone on the path could inject a script that sends the password to evil[.]com when they type it in.


Your next best bet might be sjcl.


> CBC doesn't have authentication. So add on an HMAC. Done

Literally one of the most common things to fuck up when doing crypto yourself.


It’s unclear why you’re being so flippant about “adding on HMAC.”

The things you are describing are difficult and dangerous for developers to build.


Mostly because we have a bloody simple use case in this topic.

HTML uses some Javascript to encrypt a file. Then later, the Javascript decrypts the file and returns it to normal.

This is a "beginner level" cryptography situation. Yes, I know there's all sorts of traps all around the field of cryptography. But I also know that this particular use case is simple enough that beginners can try their hand at it, and probably make something useful. And with low-probability of a critical error.

-------------

Don't write your own AES-GCM implementation. Don't write your own TLS1.2 implementation. Etc. etc. There's all kinds of subtle errors involved. IVs, Padding, side-channels, man-in-the-middle... more than I can count and very subtle to think about.

The fact that this subthread has gone down a rabbit-hole of hypotheticals that are _COMPLETELY IRRELEVANT_ to the use-case discussed in this github is proof enough. The community is a bit rabid over its "advice" on this subject.


> The fact that this subthread has gone down a rabbit-hole of hypotheticals that are _COMPLETELY IRRELEVANT_

I see what you’re saying, but the fact is that these hypotheticals are not irrelevant. They’re important intricacies and potential trip mines associated with implementing your own encryption.


There are a myriad of encryption libraries that have been implemented, reimplemented, and redesigned by various hackers.

In any case, I think reimplementing CBC is absolutely beginner level as far as implementation is concerned. Yes, you can be tripped up by various _uses_ of CBC, but the failure modes are extremely well studied at this point.

Of course, a proper AEAD implementation would avoid that. But AEAD is commonly established today with GCM, which is not beginner level. So now we're trapped. What am I supposed to tell a young programmer who is interested in playing around with encryption on their own?

Perhaps you wish to stop anyone from studying that subject. But on the contrary, I think the only way a programmer can truly learn this stuff is to implement the easiest stuff and try it out themselves from the ground up.

--------

The _proper_ advice, is... don't write your own production encryption. But... if you have to, stick with the simplest implementation that avoids the most common errors (side channels, timing attacks, etc. etc.), and focus on the simplest use cases that have the largest amount of history.

Of course, there's an entire class of crypto-bugs these days are found at the higher levels, in the "use of encryption" (ex: SSL3.0, TLS1.0, TLS1.1, etc. etc.). Nonce reuse, IV generation, in-advertent side channels due to padding oracles, etc. etc.

Even a "perfect" implementation of CBC will come across these crypto-bugs (and is why modern algorithms do focus on AEAD from the ground up these days, which largely avoids the problem).

So yes, there are a lot of traps and footguns in the world of encryption. But that's true of programming in general. Eventually, someone will want to make their own encryption, maybe the library doesn't work out just right (as is in this case: the Crypto-library doesn't have GCM mode implemented), and other modes need to be used. The use of CBC seems to be done properly here for static-HTML encryption.


> What am I supposed to tell a young programmer who is interested in playing around with encryption on their own?

Tell them to stop if they’re trying to do it for anything real, or do it as a toy protect that never sees the light of day. And if they select the latter, then they may as well try GCM.

Tell them to leave encryption writing to the professionals.


This entire subthread started because the github repo here used CBC mode (which was supported by the original writer's crypto library), rather than GCM mode (which was unsupported).

We're well deep into a "professional" discussion about the pros and cons of particular implementation details of cryptography.

------------

Go back to the top. Look at the Github code. See that it uses a crypto-library. What should have the original writer have done differently?

The answer is absolutely not "write their own implementation of GCM". They chose correctly: using a well known, well supported CBC mode of operation with AES. (And IMO, _IF_ CBC mode were unsupported, the correct move would have been to write CBC themselves, as it is far less complex than GCM, which includes GHASH and other such side-channel issues).

There's context to everything. From my understanding of this current situation, the CBC choice was perfectly valid.


This GCM sideshow is a consequence of the project using crypto-js, which is not fit for purpose. If your cryptography library doesn't support any AEADs, replace it with one that does. This is a browser project, so all it actually needs is WebCrypto. The whole discussion on this thread has been super weird. Don't ever encrypt with non-authenticated cipher modes.


AES-GCM is part of the Web Cryptography API and supported in all modern browsers, so no need to implement anything.


You're misleading a lot of people with poor comments about implementation vs. design correctness. Design of systems that use crypto requires a specific kind of attention to detail.


Thanks for the feedback! We had an issue opened around that topic where it seems to me that the weak points of CBC do not apply in the context of StatiCrypt. If you have more thoughts on this I'd love to hear them.

Relevant comment here: https://github.com/robinmoisson/staticrypt/issues/19#issueco...


Leave it to full-stack devs to take care of cryptography


As a newer dev later in life, I love to learn about inter-disciplinary snobbery/riffing. I learned some of what I know from a full stack dev, who was convinced anyone who couldnt do everything "was shit" and "should be something easier like a web developer or IT guy" instead. I'm excited to learn that pure disciplines also look down on full stack devs


There is tons of different types of programming out there.


I'm going to plug my project PageCrypt here too, which predates StatiCrypt, has a web UI that's good for one-off page encryption, and does use the WebCrypto API.

https://github.com/MaxLaumeister/pagecrypt

https://www.maxlaumeister.com/pagecrypt/


PageCrypt is excellent. I wrote a tutorial a while back on Reddit about how to use it for password-protected Anki cards. Just to give that extra layer of security.


Yep, looks great! If I had known about it at the time I might not have written StatiCrypt. I added it to the "Alternatives to StatiCrypt"[1] section of the Readme years ago when I discovered it :)

[1] https://github.com/robinmoisson/staticrypt#alternatives-to-s...


The downside to this method is that since the resulting cryptographic hash and salt have to be in the resulting file, so there is nothing stopping someone from pulling the hash/salt out and bruteforcing it locally (as opposed to being able to ratelimit login attempts on a server) if they are so inclined and have the required resources.. which may not be that much in the way of resources as the tool uses 1000 iterations of PBKDF2[1]. This is magnitudes lower than what OWASP recommends[2].

I wouldn't personally put anything you want secure behind this.

[1]: https://github.com/robinmoisson/staticrypt/blob/5dac008ba644... [2]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Stor...


Right but all that's happening here is stretching. So the difference only matters in the middle. Regardless of the strategy bad guys will guess "1234" or "sesame" (too easy) and they won't guess a random 128-bit key I just generated with my hexadecimal dice (impossible). This weakness only means it's easier than expected to guess your password is "suckitelon" or "GoCowboys1978" or whatever

We put a lot of effort into teaching people to do better stretching, but it feels like the result was they put even more passwords on things, rather than using the breathing room to get off passwords. If your project for 2023 is "Use a fancier password hash" instead of "Get rid of passwords" you are Doing It Wrong™.

If you wanted to do something like this seriously, consider hmac-secret extension to FIDO, which means Security Keys (so the things you'd use to log in to say Google or Facebook) can present an arbitrary always-identical secret value which would be more than adequate to secure such a thing unlike a human memorable password. Today hmac-secret is mostly used to authenticate to an off-line laptop PC or similar.


Last I heard WebAuthn didn't have access to hmac-secret or prf outside of a Chrome canary. Has this changed recently?


Your 128 key is difficult for users to enter manually.


Strong cryptographic keys should never be input using a keyboard and fingers, there are much better interfaces for that (security keys, passkeys, password managers).


Is it a downside if the password is a 100 characters-long string?


I was thinking that I keep my secrets at hand in my password manager of the Keepass family. I sync the db from my laptop to my mobile devices.

The 100 characters password would be in the password manager but the secrets I would work with fit into the password manager, so I have no need to use a separate channel. I could upload the db, download it and just remember the master password.

The only use case for this technology is when the secret is the file itself and it can't fit in the password manager. An image, a file, etc.


Depends which characters they are.

But in practise nobody uses 100 character long strings. Security is the intersection of real users with systems, not imaginary ones.


> But in practise nobody uses 100 character long strings.

Multiple websites I use daily have that length of password or longer.


A fellow cryptographer in the security board on stack exchange has shared their opinion on this: https://security.stackexchange.com/questions/3959/recommende...

TLDR: No. In fact, theoretically stretching the password is more effective than increasing iterations. However, I don't see him citing the fact some password hashing functions have limited input size, which means at some point having a larger password doesn't really change anything.



Hi HN! Author of the tool here. Just woke up to a few emails pointing me to this thread. Thanks for the interest and added eyeballs!

I'll answer some of the comments here and address the newly opened issues during the day. To answer a few questions that seem common skimming this thread:

* CBC vs GCM: we had a conversation regarding this topic where it seemed CBC weaknesses do not apply in StatiCrypt context. I'd love to hear your thoughts if you have any - issue is here[1].

* WebCrypto: I've been wanting to use WebCrypto instead of crypto-js for years now. It's been in my "Important but not urgent" bucket (since crypto-js should be secure too), the interface is different so I want to make sure I do it correctly and life happened, so I never got farther than drafts. Thank you for the PRs, I hope to get to it soon!

* "static" means no server-side logic (not no JS): I first made StatiCrypt to solve my own issue of wanting to password protect an html page I could host on a static file host (Netlify, Github pages...). The whole point is to not have a server or DB, so we can't use Basic auth etc.

* Iteration count for PBKDF2: will increase today

As I write in the FAQ[2] I do my best to implement things correctly but I'm not a cryptographer - any feedback to make the tool better or more secure is very welcome!

[1] https://github.com/robinmoisson/staticrypt/issues/19#issueco...

[2] https://github.com/robinmoisson/staticrypt#is-it-secure


My recent solution to this problem -- for an entire static site -- was to use HTTP Basic authentication with CloudFlare Pages: https://github.com/garrison/cloudflare-pages-shared-password


I was digging through the comments for someone to point this out. I’m honestly curious why people are using these overly complex options when a solution has been built into the HTTP standard for decades (and, in fact, is heavily abused for many APIs).

And it’s superior in many ways, since the file is never delivered until authentication has been completed.


with basic authentication, you cannot embed the decryption key into the url via a hash fragment.

This means you must have a secondary channel to communicate to the user about the password, and the server must also know the password.

So depending on your use-case, the basic auth isn't suitable. For example, mega : https://en.wikipedia.org/wiki/Mega_(service) , in which you want to ensure that the decrypted data is _not_ accessible to the server, so the key is not stored nor sent to the server!


Sure you can, it’s been built-in to the HTTP spec since RFC1738[1]. You just do:

//<user>:<pass>@<url>[:<port>][/<location>]/

It doesn’t work on IE classic, but should still be perfectly valid on Chrome, Safari, Firefox, etc.

1 - https://www.ietf.org/rfc/rfc1738.txt


In a real-life scenario, the server can access the key and collect it back (just farming location.hash)


Person A doesn't know about basic auth - primarily because it isn't a hip way to solve the problem - so person A spends a bunch of time understanding, installing, and using something unnecessarily convoluted. When presented with evidence of a simpler solution, they balk because humans value things they have spent time on especially when it is their own solution, their own "discovery".


Author here - the specific use case is when you _don't_ have or want server-side logic or a DB. For example on static hosting (hence the name StatiCrypt) like Github pages, Netlify, etc.

That means nothing to maintain, no server cost, no serverless functions to rely on, etc.

But when that's not a constraint there are many different options that might make more sense.


Sure, I’ll give you that use case.

It’s just weird how many people are jumping on this as some new technique when Basic Auth is fully usable in many other cases.


For anyone else wondering what the KDF is, it appears to be PBKDF2 with 1000 iterations:

https://github.com/robinmoisson/staticrypt/blob/5dac008ba644...


There was some recent discussion about KDF for password managers. Bitwarden increased rounds from 100k to 600k for new accounts. But keep in mind, in order to get the encrypted store you either need to authenticate online or steal it from a device that previously authenticated. An attacker needs to bruteforce the master password quickly as the stored password are useless if they are changed. There's layers of security and a limited window of opportunity.

Here, the encrypted document is encouraged to be hosted publicly. There isn't any authentication before the encrypted document is downloaded. If the document remains sensitive long term, then we need to protect it from attack using computers that will exist >10 years from now.

Since this tool doesn't have layered security, and the contents likely remain sensitive long term the single security layer should be stronger.

We can hand wave this and say that the user should pick a strong password or only store minimally sensitive documents but most won't and there's nothing here to inform or encourage them to do so. (Even single character passwords are allowed...)


Here's one w/ 100_000. Perhaps it's missing a couple zeroes.

https://github.com/yjs/y-webrtc/blob/master/src/crypto.js#L2...


The use case for a document like this is a little different from .htaccess. This is something you can share, email, host, etc and have some security in transit and at rest. Yes, .htaccess password protects on the web server, but that is one specific use case and requires a lot of machinery and specific environment. JavaScript is everywhere. And this is a static HTML document in the sense that there is no server side rendering.


I'm not seeing much of a use case tbh, in the sense that I cannot see myself going for something like this.

If it's really sensitive I wouldn't put it on an accessible page and then have to worry about password sharing, server-side security/possible spoofing, etc etc

So if I can share the password securely and/or have it somewhere safe, why don't I use that system for the content itself? because of html formatting, storage, etc? seems like a much easier problem to solve than the threat model of sensitive stuff shared on the web.

I'm trying to imagine concrete situations that I'd go for something like this and I cannot quite think of any. Silo-ing stuff behind a login is so much more versatile. Generally not putting sensitive stuff on the web works so well when you can send essentially anything point-to-point using so many widely used protocols including Signal, LINE or Whatsapp, or a simple email, which you can use to send encrypted 7z files for good measure.


I would not use this for anything. Existing tools are better in every way. It is a cool idea though.


Why not use a password protected word document if those are your use-cases?


You definitely could. But I think there are more computers with web browsers than there are computers that can read encrypted Microsoft Word documents.


On client systems, PDF readers are as ubiquitous as browsers. An encrypted PDF should do the job better than Word.


Similar use case. I would not use this for anything important. If you just want to shuffle data around with something similar just use something like KeePassX, lets you attach files. Much more configurable with regards to KDF parameters and such.


But doesn't Word use CBC? Apparently from upthread that's E_ALWAYS_BAD.


Ahh, well, this is actually a field where I know a lot. Since it is my job. Let me distill up thread.

1. Don’t implement the underlying crypto yourself

2. CBC is hard to get right

3. There are a lot of esoteric attacks and if you have a nation state attacking you they could exploit them, but they won’t because they will just put some crap on your systems and do it easier.

Also this is a very simple use case to get authenticated CBC correct with. So, the real answer is “don’t do this, but it is probably okay in this one use case, assuming they didn’t implement all of this themselves (e.g the crypto algorithms themselves)

You should still listen to tptacek though. Use an authenticated crypto mode :)


Use apache to serve your page and htpasswd to provide basic authentication. Easy 1994 answer


I thought that too, but this appears like it works to password protect an html file that's *not* being served off a server. This ought to work with a file on disk too.


The more general solution is to use a password-protected archive.


But then it's not self contained and every time you want to access the archive you also need a program that can decrypt/encrypt that format. Using a self-contained HTML file means it's currently the most portable way to encrypt something and have it available anywhere you can get the file on.

By the way, I don't advocate for this idea. But still, I find it a pretty ingenious way to store encrypted data portably. Essentially what is happening is the file contains the encrypted data and the algorithm to decrypt it with the correct passphrase/key.


> you also need a program that can decrypt/encrypt that format

that's just a self-extracting archive, which is a very common idea and easily created as well (very popular with both shareware distribution).

The problem is that you have to trust the extracting code to not install anything else malicious. The browser provides the perfect sandbox - you cannot install malware into the system via javascript in the browser, and thus you can trust it to run.


And you have to trust that running processes on dish aren't going to make a copy of it. Or that it won't end up stuck in the cache. This is (theoretically) only decrypted in memory (your browser may save a preview image of the tab, I believe Firefox mobile does this, not sure about desktop browsers)


Also, your self-extracting archive isn't platform independent, and likely can't be used at all on mobile platforms (iOS, Android) that don't offer to trying anyway files ootb (and are probably the wrong architecture anyways)


Also NGINX auth basic option. I use it on my personal website.

https://docs.nginx.com/nginx/admin-guide/security-controls/c...



I saw a super simple example of this back in the 90s when I was first leaning web development (was in Web Development for Dummies or some such).

The page prompted the user for the password, and then used the password to generate the URL for the hidden page and redirected the user there. Of course this wasn’t over HTTPS, and you got a 404 if you entered the wrong password, but it was still a neat hobbyist trick for the era. I think the author even wagered that readers couldn’t defeat it.


That was very popular back then. Besides the 404, it has the side effect that if that URL leaked, then your password system broke. With this implementation leaking the page is not a big deal (assuming you're not using it to store state secrets or anything like that)


I'll echo the other sentiments here that I wouldn't consider this "static HTML", and I was expecting something about .htaccess. That said, this seems to be a strengthening of what used to be somewhat common "protection", namely variations consisting of:

- Password in plaintext in the source e.g. "if(password=='hunter2') ..."

- No correlation between password and page contents (either the content is merely hidden and shown when the password check succeeds, or lightly obfuscated/encrypted using key independent of knowing the password)

- Hiding content by using "display:none" or similar


> I wouldn't consider this "static HTML"

why not? this could be served on a server which does not have any dynamic execution capabilities, and does not require the server to handle any other request other than just the HTML.


In my experience, that's normally called "static hosting", not "static HTML".


Take one shot if you thought this was going to be about .htaccess and a second if you’re confused why it wasn’t.


i did think it was going to be, but it seems to be talking about password protecting things like github pages, where you don't control either the server or the filesystem, and therefore have no way to add the .htaccess file.


This solution has no street cred


Pagecrypt does the same thing, but with the Web Crypto API. More (including a sample repo) at https://render.com/blog/static-site-auth-pagecrypt.


Similar here: https://github.com/sowbug/quaid

It works with a GPG-encrypted file. I figured that was safer than developing my own encryption format. As it is, any vulnerability in the decryption process is equivalent to a vulnerability in GPG.


Thanks for creating this. I was just thinking why not use gpg for this?


No discussion of what KDF is used on the readme page is a giant red flag.

Whether using pbkdf2, argon2 or something else, along with what the work factor is, is critical to security in this application.


I just finished building a similar tool: https://utiliti.dev/private-note/

Currently it’s just for plaintext, but might allow for more complex things like the website.

The password is stored in the url after the # so it’s never sent to the server.

I was going to post this project after I build some more utilities.


Is the note supposed to self-destruct even when it's attempted to be opened with incorrect data in the URL hash? (so it hasn't actually been read yet)


Today it does, but might need to find a way around it.


It will leak all over browser's history, though.


Is this a problem? It will only leak on the sender and receivers browser history.


Hah, this is another thing I've been thinking about recently :) It's cool to see tools like this popping up.

An approach I've played around with, but never sat down to actually build, was to use service workers to be able to encrypt large sites -- the entire site would be encrypted locally from the command line using libsodium's `crypto_secretbox_easy`[0], and all of the data would be concatenated into a series of padded files to try and somewhat hide the number of files/size of the site. That key would be merged with a lookup table that would also be encrypted with a separate key, that would be stuck in the url hash. Then once the site was decrypted (ie, the secretbox key was sent to the service worker from the url) the service worker would intercept any requests the the front-end made and decrypt the original map that would tell it which file chunks to fetch, and then it would decrypt them on the fly and cache them for the duration of the session. Whenever you updated the site, the files would be re-encrypted with another key, but the "user facing" key in the URL would stay the same.

The linked project (and other projects people have linked) are a lot more portable, but I was thinking less about being able to have a single file that I carried around and more about being able to put up a full website where I could have something approximating user accounts (ie, possibly having multiple lookup tables that could be decrypted separately per-user), and where I could build full-featured projects that could fetch their own resources.

But, I haven't ever gotten around to actually building it, I only ever built some tests to make sure that it would technically work, and I got nervous about rolling my own solution even though I was just going through the libsodium APIs.

----

[0]: I've seen a couple of people recommend using Web Crypto instead, which I've avoided because I thought libsodium ported to the browser was harder to shoot myself in the foot with; is that a bad instinct?


A more lightweight solution would be to hash the password and have a copy of that file at this url.

windows.location = hash(password);


Client side decryption has the benefit that no machine or router in the middle can view or cache the decrypted page without the password.


You mean, unless they tamper with the Javascript to capture the password (or the derived key).


I designed a similar tool with that threat model in mind: the resulting HTML/JS is as minimal as possible so it’s possible to inspect before entering the password. Of course assumes safe browser and client machine.. https://github.com/dividuum/html-vault


Kind of possible. With enough content, would you actually spot a "}; and some highly obfuscated code in the middle of the blob? (My guess is a no)


You're right. Probably not. But at least the chance exists as there isn't a multi-KB obfuscated JS dependency somewhere that would be a lot harder to handle.


Valid if you actually host the file online. Local files are generally not allowed to make requests. Not sure if local storage methods are usable without http/s.


Their solution intends for the static file to be copied offline


Wow I like that. So simple and elegant. Could be combined with a username to produce unique urls per user.


A miss configured http server that allows directory listing would compromise everything.

Also now all intermediate things that have access to the hashed url would suddenly have access to a secure piece of information.

Don’t be clever with security


You can always put an index.html to prevent that mistake.


A more lightweight solution would be to let the Webserver (nginx, https, whatever) password-protect the site.

No JavaScript required a d highly efficient


Requiring some sort of VM or container to run this webserver on? Is it possible that's way more onerous than a static html file which can be emailed around? Are you sure nginx is even going to be around in, say, 15 years? (think password protected PDF but in html and allowing for JavaScript.)


> you sure nginx is even going to be around in, say, 15 years?

Considering it's been around longer than that, yes I think it will be around in 15 years


The bank I use recently switched to using a similar method for sending their statements. The html page they send via email is password protected. I dig into how they do it (also to ensure they aren’t phoning anywhere when I open the report) and they encrypt the report content using a similar method to the one shared here. It’s neat and I have to give them kudos for doing something neat like that as opposed to sending a pdf with a password on it.

Why html though? They do have some js and interactive functionality in there. I can slice and dice my monthly report within the single html page they send which is actually quite handy (and there’s the default “show me my transactions in a list with opening and closing balance” option as well).


It IS a bad idea. 1) The attacker get access to the server and changes the file on the server, one that writes an html/js that redirects to https://example.com/malicious.html with the same interface and captures your secret password. Proceeds to access your file with your password.

2) The code calls back home in xhr.open('POST', 'https://zlgpaemmniviswibzuwt.supabase.co/rest/v1/rpc/increme...', true); i don't want YOU to know when i open a file, or encrypt a file.

3) The surface attack of the browser is HUGE, there are many escape the sandbox vulns, same origin bypass, zero day exploits that can be exploited, take a look at the cve database of chromium, using the browser the way it is proposed is a big mistake.

Finally, the code is not audited, may have cryptographic weakness as pointed in other comments. The solution you made could be good for a class assignment, or to learn how to use cryptojs, but from the security standpoint is a mistake to use it for anything serious.

If you are security conscious, you should use VeraCrypt/bitlocker, a simple rar/zip with password, even a pdf/.docx with password, or use a secure server with SSL, sftp?.


I have a similar project called Sasha.html.

https://github.com/dav1app/sasha.html

The idea is to export any file as an HTML file with the data as an encrypted string hard coded within the HTML. This way, no specific software is required to decrypt the file, just open it on the browser, type the password and download or view your file.

I built this to have a easy way to send encrypted files to any device and open it without having to install external tools.


There is a difference between this kind of password and the password for a server.

With the OP system the attacker gets potentially way more attempts to access the page and gets to try in the future with whatever tech comes out. On the plus side it is E2E encrypted!

With a traditional server they can rate limit attempts on the password. But they probably don’t encrypt it using YOUR password (unless it is a password manager etc.) so an attacker could get the plaintext if there is a breach.


Should have used the WebCrypto API instead of the crypto-js npm package. https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_...


I spent some time Googling this about 6 months ago. Lots of tutorials on Crypto-js, not so many (and almost zero "here's a super simple implementation") for WebCrypto API. I can understand if this is a hobby project why you'd lean into one rather than the other, I probably would have done the same.


Not going to pretend that I know what the most of the stuff mean, or if it is even safe enough, but I've followed the MDN articles and put together this TypeScript snippet [1]. Maybe somebody could comment on it?

Also sorry for the long link. Is there any accepted way to post a shorted URL?

Edit: added a corrected version [2]

[1]: https://www.typescriptlang.org/play?#code/DYUwLgBAbiDGYHsBOE...

[2]: https://www.typescriptlang.org/play?#code/FAiGGcE8DsGMAIBmBX...


If you call "encrypt" more than once in that code, you'll leak the authentication key. Every invocation of GCM encryption needs a unique nonce. Cryptography nerds will chastise you for using a random nonce (there theoretically isn't enough room in the GCM nonce space to safely encrypt large numbers of message with random nonces), but the alternative (using a counter) is even more hazardous. This problem motivates a lot of people to use other AEADs like XChapoly, which has an extended nonce space that safely admits random nonces. Isn't cryptography fun?


It's still considered safe to call AES-GCM with a single key and a randomly generated nonce 2^32 times [1], most practical systems don't come anywhere near this limit. And there's AES-GCM-SIV that solves nonce reuse (mostly), though it's not available in the Web Cryptography API at the moment.

1: https://csrc.nist.gov/csrc/media/Projects/crypto-publication...


I don't personally care, and probably wouldn't even sev:info a random GCM nonce in an assessment, but I would also choose extended-nonce Chapoly in preference to GCM in part because of this issue.


From what I shallowly researched; GCM's nonce seems limited to 12 bytes by convention only. That nonce reuse is so fatal seems absurd to me.

Would "salting" the key safely tackle the problem?

Put explicitly;

  send <- nonce || salt || ciphertext
  recv -> decrypt(ciphertext, nonce, pbkdf(pass) || salt)
[edit: apply salt outside of the kdf]


As I remember it, the balance of the bytes in the AES block are used for the counter. At any rate, the convention is essentially universal.


Sounds above my (current) head.

Here I thought GCM was some modern foolproof/footgunless design.


It is sort of infamously footgunny.


Oh yes! Thank you for the feedback. I've added a new version where the `iv` and the `salt` is random. Maybe a followup question: Because you need both the `iv` and `salt` to decrypt the message is it ok in an E2E scenario to send all three: `iv`, `salt` and the encrypted message?


I didn't look to see what "salt" means in your design, but the idiom for using GCM in message encryption is to send ciphertexts that take the form `nonce || ciphertext`, and to decrypt by reading the nonce off the front of the message.


Sounds like a golden opportunity for someone with a developer blog to make useful content


WebCrypto only works in secure contexts (https), which is a significant limitation for some use cases. But I agree it should be an option.



What is the major difference? Isn't crypto-js still secure?


One is a Javascript package, the other is a browser library following a spec that is implemented by all the major browser companies.

Web Crypto is faster and has many more devs working in the different implementations between all the companies and doesn't require any includes.


I don't know what "secure" means. Is their implementation of OFB correct? Probably. But using OFB mode is itself a problem. From what I can see, crypto-js implements no authenticated modes, and exposes all sorts of crufty old things nobody should be using. The parent comment suggesting WebCrypto is correct in this case. Avoid crypto-js.


Why use a library (thus incurring the need for the user to download more JS) instead of using what is already in their browser?


It might be. Whereas the native lib should be.

Just levels of trust. I'd happily use the former if the latter didn't exist.


Similar to portable-secret? https://github.com/mprimi/portable-secret


Does anyone know a 'simple enough' way of placing a static site behind an auth wall (say, oAuth)? Just today I wrote down about what I use https://chanux.me/blog/post/static-site-with-auth/

I note down two other possible methods in the post. Would love to hear about more info.


Azure App Service provides an integrated optional auth wall for the apps deployed, without adding extra code. It may be a bit overkill to deploy a static site on the App Service, but it's supported. See here: https://learn.microsoft.com/en-us/azure/app-service/overview...


A technique I’ve used in the past is just turn off directory listing and shove protected content behind a directory named using the hash of the password. Then after hashing the password provided by the user, the directory either exists or it doesn’t.

Edit: I wouldn’t recommend this for anything serious, but for some blog posts of my wife’s that we wanted to be relatively private, it was good enough.


Anyone remembers .htaccess files from Apache? Good times.


Those would be quite nice, if web browsers were good HTTP clients.

The user experience with basic auth is not so good. The dialogs give little way to customize and providing information for user. No support for logout or any form of password changes.


Isn't that sort of a feature? Customizable HTTP auth dialogs are a security liability, as they blur the line between content and browser.


Yes and no. There is some part of it which helps to identify where you are connecting to. Meanwhile those dialogs aren't modal anymore, which allows access to https certificate info, but it is still complicated to provide trust for average users.


> The user experience with basic auth is not so good.

Apache actually also has an OpenID Connect module (it's certified and everything), which you can enable to have it work as a relying party: https://github.com/zmartzone/mod_auth_openidc

Basically, the actual UI will be handled by another system that you might be using, for example, in my case that might be a self-hosted Keycloak instance: https://www.keycloak.org/

I'd say that Keycloak is a pretty good solution in general, because it does some of the heavy lifting for you, maybe its shorter release cycle not being the best thing ever, though. I think IdentityServer also tried to fill this niche, but they went full on commercial recently, without OSS offerings.

As a sidenote, I also use mTLS for some personal resources and basicauth is still wonderfully easy to setup without a single point of failure for handling the authentication. A caveat might be that in practice people who try to use mTLS for app development shoot themselves in the foot, because that doesn't play nicely with reverse proxies etc.

Oh and also, to reduce needless disk IO, using a single config file approach as opposed to .htaccess can make Apache a bit more performant and easier to reason about: https://httpd.apache.org/docs/current/mod/core.html#allowove...


IdentityServer have a Community Edition that is suitable for OSS projects.


Hmm, from what I can tell, this appears to be sort of true: https://duendesoftware.com/products/CommunityEdition

However it's the kind of license that's gated behind a manual signup:

> If you would like to request a Community Edition license, please provide:

> Confirmation that you understood the qualifying criteria

> Your category (for-profit, non-profit, charity)

> Your (company) name

> Your address

> Your contact email

This seems to be reflected in that you cannot find any official Docker images, for example: https://hub.docker.com/search?q=identityserver

Contrast that to: https://hub.docker.com/r/keycloak/keycloak (admittedly, the Bitnami containers are better, since those provide good documentation right in Docker Hub, instead of being lazy like Keycloak did and just putting it on their site)

Overall, the licensing situation with IdentityServer seems interesting, perhaps a bit more restrictive than Keycloak or other competitors: https://leastprivilege.com/2020/10/01/the-future-of-identity...

Regardless, to me being able to download software without messing about with signups and justifying why I need it feels like a good litmus test for some of the culture and community behind it. Regardless, I don't think that there are any truly excellent solutions in this space out there.

Then again, you see basically the same with the likes of OpenLDAP, FreeIPA and others that still don't quite compete with Microsoft's AD. There's a lot of problems (identity and device management, authentication/authorization gateways etc.) that could have great OSS solutions for them, if at the end of the day everything didn't circle back to money. Oh well.


Logout is possible: The server has to send http status 401 if I recall correctly. The browser treats this as logout.


This requires some server side handling. The web browser could have a logout button so I can end my session. Probably that should be combined with a configured target page for the user.

And well, my experience there is about 20 years old, but back then sending 401 wasn't enough. You had to send a different auth request, which then would pop up a new password dialog first. Indoubt 401 is enough today as well, as the browser can't distinguish whether a specific resource is restricted or whether the whole session should be invalidated.


Wow. This is really cool. I wonder if it would be possible to kind of build a site with personal pages etc for different user each encrypted by their own password

Like domain.com/user1 and domain.com/user2 each are encrypted but with their own passwords. So user 1 can only visit his own page and not other users.

(I know there would be much easier to just do a traditional webapp but as a concept it is super cool).


I am using it in exactly that context and it is good enough for the level of security I need to achieve. I understand this can be brute forced, but this is a risk I am willing to take given the level of "secrecy" of the content.


Hah! I did a similar thing for a fun friday afternoon security challenge with AES256 encryption on static website using the answer to challenges as the key here if anyone wanna play with it... :)

https://challenge.surge.sh/

It should take you no less than 10 minutes ;)


Shameless plug, I did something similar in a markdown processor (which processes any folder with some markdown into an html website). You can either protect some of the pages, or the entire thing

https://github.com/cfe84/plaf


Or... http authentication.


You can email or hand out physical copies on usb would be one use case where server auth can't compete.


Aren't existing encryption tools more flexible for sending out copies via those methods? e.g. by making more cipher suites available? Or by allowing you to encrypt with the intended recipients' public keys, so you don't need to worry about distributing the passphrase securely? And might allow better integration with password managers, as the OS would recognise the files as being encrypted?


Supposedly browsers are more ubiquitous on the recipient side than whatever other tools you might use.


I was thinking of editing my comment to add "Or be able to support filetypes other than HTML", but I suppose in our glorious "everything is the browser" future, filetypes other than HTML are as anachronistic as 8" floppy disks.

Sigh.


FWIW, you can use data URLs to include any type of file in your HTML, and with <a download='filename' href='data:…'> browsers will allow you to save the file.


How do you use that on something like GitHub pages?


Turns out when you give up the work of hosting your up stuff, you also give up some of the benefits from doing so.


Which is why a project that returns some of those benefits is genuinely cool and useful.


And even can give benefits by storing it encrypted at rest.


You can do the same thing with SingleFileZ [1] which can protect saved pages with a password. It relies on the zip specification to store the encrypted resources (html, images, css, etc.).

Here's an example of protected page saved with SingleFileZ: https://gildas-lormeau.github.io/private/ (password: thisisapage)

[1] https://github.com/gildas-lormeau/SingleFileZ


I had more simple and dumb approach in past. Lets say you have some password like "super-secret". Just place a secret "html" with content under the directory with the same name as your password. In public page have some prompt with password. When user enter password, you either redirect, dynamically inject iframe with your page. Since there is no way to list files/dirs of http server, it does pretty good job.


I wouldn't consider that awfully secure...

Your local browser will be saving your 'password' unencrypted in the cache and history, and maybe syncing to other devices.

Any workplace security system will probably be keeping records of your 'password'.

Any mistake on the web server could turn on directory listing, revealing the password.

Some other user of the web server, even a sandboxed low privilege process, will often have the ability to list directories.


The title is a bit misleading. The title implies ONLY HTML when it uses some JS. I guess it's not possible (in anyway I know) without some JS, but still.


hmmm ... let me google that for you:

"The 'Basic' HTTP Authentication Scheme"

* https://www.rfc-editor.org/rfc/rfc7617

or

* https://en.wikipedia.org/wiki/Basic_access_authentication


Can someone explain a bit more how “remember me” works? If symmetric encryption requires a “key” to decrypt - one might expect that you will need to store that key to auto-decrypt. But here it claims that the key is stored salted and hashed. But to auto-decrypt - you need to reverse that salt and hash because you need that key back. How would that work?


The passphrase+salt get hashed. This hashed-passphrase is what decrypts the file.

You can store the hashed-passphrase in local storage. This means you can auto-decrypt again. You don't need the original passphrase.

If bad guys get your computer, they can read the file. However, they cannot trivially work out what your original passphrase was, so it protects you a little if you reused your passphrase elsewhere.


Thanks!


If this can be repurposed to just password protect a specific section of a page, then can it be repurposed to decrypt JavaScript embedded/encrypted in that section performs an "authenticated" API calls.

This could be useful if you want to allow making edits and publishing this to a server, but keep authentication entirely client-side.


Oh neat, this is a neat concept. Always wanted a secure way to password protect a page without server side coding, and this is definitely the closest we've gotten in years.

Not sure I'd trust it myself, but it definitely seems like a good option for those with limited hosting options (like GitHub pages or Neocities) and a need to secure some content.


Phishing attachments/links abuse tools like this FYI, not impugning it in the least bit though.

It deters automated sandbox analysis by asking the user to use a password in the email body. For zip,pdf,etc... you can easily tell when they're encrypted and block/alert on them but not so with non-standard methods like this.


I do see this useful in cases where you might want to keep something private from the server-side and are not too concerned about server-side tampering.

Then could use basic auth to protect access from outside and a tool like this to protect access from inside.


HTTP Basic Auth is a few headers away.

I just configured it in my CapRover instance for a deployed app to have my db GUI


Hey look it’s a “static” HTML file that decrypts itself using AES 256 CBC. Sure, that sounds static and simple and good for the web. What could go wrong.

Some obvious limitations:

-requires users to re-authenticate for every protected file on your site

-assets loaded by the page not password protected

-uses a single password globally shared by everyone who wants to read the doc

-archiving the page itself is useless unless you also archive the password, which must be done out of band

-this doesn’t compose or integrate with the web: none of the common tools you would use to download, spider, archive, spindle, mutilate web pages know how to deal with this

It’s certainly one solution. The http basic auth built into every popular webserver is another, with none of the above limitations. But I guess that would require spending 5 minutes learning and implementing some server config, and why do that when you can further ruin the web by shoving yet more user hostile complexity into client side JavaScript.

Sorry to sound bitter I just feel this is a really bad idea if you think about it for any length of time.


> The http basic auth built into every popular webserver is another, with none of the above limitations.

The obvious advantage of OP's approach is that one doesn't need to control the server that serves the HTML (e.g., static site served via GitHub pages would be enough)


On the web, all pages on a domain can be encrypted with the same password. If the user selects the "remember me" checkbox, they won't have to authenticate on each page.

This also works without a webserver. It can be a local file that is opened in a browser.


People might also be interested in self-contained Wikis

https://en.wikipedia.org/wiki/TiddlyWiki


> StatiCrypt uses the crypto-js library to generate a static, password protected page that can be decrypted in-browser.

Bad idea. Your browser comes with web crypto, including AES256 and PBKDF2. See MDN.


Does anybody know if image resources also is encrypted or is it just the html content?


This is a great utility and has a lot of meaningful use-cases. Thanks for puting this out there!


I would just use tweetnacl's secretbox for this instead of custom crypto.


I came here for the comments and sure enough I wasn’t disappointed :-)


why reinventing the wheel? .htacess and others


Requirement of npm for this? No thank you


Why not use WebCrypto?


uses cryptojs


Ok


If you have to run javascript it is not a static page anymore. I've seen a lot of this particularly weird overloading of "static HTML" lately. Static HTML, or static webpage, is meant to describe the experience from the person who is trying to look at the page. It does not describe the experience of the dev.

Using a dynamic script or application to generate a static html page that requires no JS execution is a static page.

Using a static HTML page and then hiding it behind javascript execution is explicitly not a static HTML page.

Use .htaccess. And if you can't, reconsider the choices you made that restrict your abilities so significantly.


>If you have to run javascript it is not a static page anymore. I've seen a lot of this particularly weird overloading of "static HTML" lately. Static HTML, or static webpage, is meant to describe the experience from the person who is trying to look at the page. It does not describe the experience of the dev.

I don't think this has been the common meaning of "static page" for at least 10 years.

From Wikipedia [0]:

>A static web page (sometimes called a flat page or a stationary page) is a web page that is delivered to the user's web browser exactly as stored

Static refers to the fact the files are served without any application-level processing on the server side beyond simple file serving. A page can depend on CSS/JS/WASM and still be a static page.

[0] https://en.wikipedia.org/wiki/Static_web_page


>A static web page (sometimes called a flat page or a stationary page) is a web page that is delivered to the user's web browser exactly as stored"

Right. "exactly as stored" It doesn't matter who or what wrote the HTML. I could do it by hand or maybe use a WYSIWYG editor to make it or maybe it's generated by a script. The point is that there's an html file sitting on disk and the server delivers it without modification and it's viewable in the browser as it. It's just the webserver sending the contents of the file.

Whereas this "Password protected a static HTML page" cannot be viewed in the browser without a "web application", the javascript, dynamically changing the file. It's pretty clear cut. So it fails the test of the very link you posted:

>in contrast to dynamic web pages which are generated by a web application


>>A static web page (sometimes called a flat page or a stationary page) is a web page that is delivered to the user's web browser exactly as stored"

>Right. "exactly as stored" It doesn't matter who or what wrote the HTML. I could do it by hand or maybe use a WYSIWYG editor to make it or maybe it's generated by a script.

Yes, agree.

>The point is that there's an html file sitting on disk and the server delivers it without modification and it's viewable in the browser as it.

No, I don't think that's the widely understood interpretation of "static web page."

Even the definition you agree with just says the file is delivered as-is. It says nothing about how the browser renders the page.


It's really not "clear cut." By the definition which you agree with, an HTML file containing JavaScript is served to the client without modification, and is viewable in any modern browser with a functioning JavaScript engine. The "web application" in reference is a server-sided application which controls and renders the content, as can be assumed based on context from the rest of that page. Namely:

> Any personalization or interactivity has to run client-side, which is restricting.


OP said static HTML page though, that very much means something only using static as opposed to dynamic HTML.

https://en.wikipedia.org/wiki/Dynamic_HTML

"DHTML allows scripting languages to change variables in a web page's definition language, which in turn affects the look and function of otherwise "static" HTML page content after the page has been fully loaded and during the viewing process. Thus the dynamic characteristic of DHTML is the way it functions while a page is viewed, not in its ability to generate a unique page with each page load.

By contrast, a dynamic web page is a broader concept, covering any web page generated differently for each user, load occurrence, or specific variable values. This includes pages created by client-side scripting and ones created by server-side scripting (such as PHP, Python, JSP or ASP.NET) where the web server generates content before sending it to the client."


The title says “static HTML page” not “static web page”.

JavaScript is not HTML.


Neither is CSS, if you want to be pedantic.


I agree


I would call any server side or client side code which alters the HTML non-static. A static HTML page is just an HTML file which is stored on the server and displayed as-is in the browser.


That’s not a generally accepted as the definition of a static page.

https://docs.aws.amazon.com/AmazonS3/latest/userguide/Websit...

> You can use Amazon S3 to host a static website. On a static website, individual webpages include static content. They might also contain client-side scripts.

By contrast, a dynamic website relies on server-side processing, including server-side scripts, such as PHP, JSP, or ASP.NET. Amazon S3 does not support server-side scripting, but AWS has other resources for hosting dynamic websites. To learn more about website hosting on AWS, see Web Hosting.


Naturally Amazon doesn't care about client site code, as they are not in the browser business. But JavaScript is in fact typically called "dynamic" content. Insofar it is not static.


Exactly. I don't know where the confusion is, but I'm saying that it doesn't matter how the page is generated. It is sent to the website visitor as just HTML. When the user views it it is all there, just html. No changes.

For example, I have a perl script that generates a set of .html files every night to show new additions to my library. They are static .html files on disk and never modified before the user views them. Just because a perl script (not connected to the web server in any way) made it does not "taint" the HTML so it is not static. The program, or the person, that wrote the HTML does not matter. All that matters is that it's just static unchanging HTML.


Everyone is allowed to give their own meaning to words, but you can't expect to just change a widely agreed meaning to match it. "Static" means it is just a set of files. Not that the page is stationary. Do you think that a blink tag in a HTML page makes it non-static? A hover effect? An :after pseudoelement in CSS?

If your perl script is saving the HTML to the disk, then yeah, your website is static. If it's generating HTML on the fly, it's dynamic. That's the widely agreed upon meaning, and that's it.


Just a set of files? Awesome! I had no idea that unix itself was a static OS.


I think your definition of a static web page is different from the common one?

In my head, a static page is something that's served verbatim, without some backend generating it or inserting things into a template.

Wikipedia seems to agree with that view: https://en.wikipedia.org/wiki/Static_web_page


That's exactly what I said. It doesn't matter if I wrote the HTML by hand or I had a perl, or bash, or compiled c program make it. It's just an HTML file.


You're incorrect

CSS and JavaScript and images are also static, it doesn't have to be just HTML

If you have a perl or bash or c backend generating html in response to http requests it's not static


You're assuming this is a backend. It is not. Do you consider generating an HTML file in a WYSIWYG editor like, say, "Dreamweaver" that outputs an .html no longer static because it was made by a program? That's absurd.


No, that's static, that's why I said "in response to an http request"


HTML includes the content of style and script elements (among many others) and the results of whatever they do.

The combination of all of these direct a browser in how to render a page.

Traditionally, a static site or static page is one whose data can be delivered to the client directly as stored, with no server-side alterations or generation.

This is/was a meaningful distinction because a server that can stream stored data is fundamentally much simpler than one that executes programs. Such a server can run in different contexts, be optimized in different ways, and satisfies constraints that allow further optimizations downstream.


Lol no. If you HTML is generated, it's by definition not static anymore.

Otherwise, all of the React server side rendering is static HTML.


Oh, so when I make an .html file in Dreamweaver WYSIWYG HTML editor and save the .html file to my websever ~/www/ dir it is automatically not a static page anymore because I used a program to generate it? This is a very wild and weird interpretation of 'static site' that does not fit with existing usage.


You are the last person to have a say in that considering your incorrect take that JS can't be static. You're clearly misaligned with common usage of terms.


When javascript started being able to change HTML significantly the result was literally named "Dynamic HTML". So it is clear this is not a static HTML page. The only thing left to argue is weather it is a static web site. People who got into web dev after 2010 will say yes, people who watched HTML/JS evolve over it's lifetime will say no.


This is the most bizarre argument. Of course you can use build tools to generate static web pages.

The issue is whether they are being generated dynamically by a server. This project isn't. You run a build-tool once and then can post the .html page anywhere. It's static.


No, because React is dynamically generating the HTML on the fly.

If you pre-compile the HTML using something like Jekyll, so that the webserver is just serving HTML files without any dynamic/on-the-fly processing at request time, then it's considered static.


React server side rendering: most likely dynamic

React on the client side: static, as you still got the same files served, how they dynamically alter the page runtime doesn't count.


You can put client-side JavaScript fine in “just an HTML file”?


It can be html, css, javascript, whatever.

'Static' means it's only made of static files that can be served by just a vanilla Web server (i.e. just by serving static files).


> If you have to run javascript it is not a static page anymore

I understand static pages as files sent to a browser without having to be generated server-side.


Right. This concept has existed for decades, and people should figure out what it actually means before complaining.


I think "static" and "dynamic" can reasonably be used to describe both server-side and client-side behaviour, and I don't think that the dual meaning is particularly new.

Yes, "Dynamic HTML" vs "Static HTML" to refer to JS-dependent and JS-free pages respectively has been around since the dawn of javascript, but my copy of the 1996 O'Reilly CGI Programming on the World Wide Web by Gundavaram contains sentences like

"Virtual, or dynamic, document creation is at the heart of CGI" (p.4)

"A common use for [server redirection] is to return a generic document that contains static information. [...] Suppose you have an HTML file (thanks.html) like the one below, that you want to display after the user fills out one of your forms: [...] You could use the programs discussed earlier to return static documents, but [...] it is much quicker and simpler to [redirect with a "Location" header]." (pp.44-45)

...without mentioning javascript anywhere.


You're interpreting the title as "a static HTML page with password protection", but the way I understood it is "statically hostable version of a static HTML page with password protection", which seems to be exactly what this implements. I'm probably biased because I would have frequently had use for this, and all the paid offerings mention "private hosting of static pages", but I feel you're a bit uncharitable with your interpretation.

If you want to just have a free small private static page which is easily updated by public CI offerings this is a great solution, and the title transports this clearly.


That is not correct. As long as the javascript is run on the client-side, it's a static page. Static simply means the files on the server are sent directly to the client. The client can then do whatever they want with the files.

The expression "dynamic" was trending a decade or two ago to vaguely refer to javascript+HTML5. It was never the opposite of "static" in this domain.


Title is still accurate, the tool is for password protecting a static HTML page. The page starts out as a static HTML page. Then this tool generates something that has password protected it.


This is nonsense semantics. Words and phrases change meaning over time.


It didn’t even “change meaning”

As far back as I remember (and I played with JavaScript when it was first introduced in beta for Netscape), a “static web page server” always meant that there was no backend server generating pages on the fly.


[flagged]


If that's wrong, so is the name of every "static site generator". Also: please don't make the same point in two separate top-level comments.


A static site generator can be used to create an .html file that's dependent on the user to execute a javscript web application to show the content. That makes that generated page dynamic. It doesn't change the nature of the static site generator.


What if the user uses curl to view the page. Is it now static?


That depends entirely on the page. Does it require javascript execution so that the contents are there? Then it's not. If you can open the file and read the text and see the URLs, yes. But if the page was generated by a script without it ever being a file on the webserver's disk, no.


funnily enough in certain circles "static pages" rather mean "static on server side" but "dynamic on client side".


The js is static too


Wouldn't .htaccess solve this issue?


No, this is strictly client-side, ie. encryption and decryption happen in the user's browser.


This doesn't require a web server - it works for files opened locally as well.


Does anyone know of a way to do something like this but with OAuth? E.g. log in with GitHub to view these docs.


I'd say it's not possible in pure HTML + JS (as in, encryption/ access control performed entirely there).

The solution (with a server) is: https://oauth2-proxy.github.io/oauth2-proxy/




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: