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...)
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.
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.
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.
> 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.
>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.
>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
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.
>>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.
"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."
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.
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.
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.
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.
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.
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.
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.
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.
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)
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.
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.
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.
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.
> 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.
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)
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.
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?
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.
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.
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
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.
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.)
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?
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?
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.
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.
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.
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.
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.
You definitely could. But I think there are more computers with web browsers than there are computers that can read encrypted Microsoft Word documents.
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.
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 :)
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.
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
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
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).
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.
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.
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.
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].
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.
> 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.
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.
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.
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.
... 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.
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.
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.
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.
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.
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.
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.
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.
Strong cryptographic keys should never be input using a keyboard and fingers, there are much better interfaces for that (security keys, passkeys, password managers).
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.
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.
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.
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.
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.
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.
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 :)
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.
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.
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.
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".
> 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.
> 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 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 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.
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.
293 comments
[ 2.9 ms ] story [ 285 ms ] threadhttps://github.com/robinmoisson/staticrypt/blob/5dac008ba644...
https://github.com/yjs/y-webrtc/blob/master/src/crypto.js#L2...
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...)
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.
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.
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.
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.
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
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
> Any personalization or interactivity has to run client-side, which is restricting.
>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.
JavaScript is not 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."
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
Otherwise, all of the React server side rendering is static HTML.
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 on the client side: static, as you still got the same files served, how they dynamically alter the page runtime doesn't count.
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.
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
'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).
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.
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.
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.
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.
I understand static pages as files sent to a browser without having to be generated server-side.
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.
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.
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.
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...
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.
Sigh.
windows.location = hash(password);
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
No JavaScript required a d highly efficient
Considering it's been around longer than that, yes I think it will be around in 15 years
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]: tptacek ↗ 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? trusz ↗ 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? tptacek ↗ 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. majou ↗ 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. tptacek ↗ 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. majou ↗ Sounds above my (current) head. tptacek ↗ It is sort of infamously footgunny. ThePhysicist ↗ 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. tptacek ↗ 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.
Would "salting" the key safely tackle the problem?
Put explicitly;
[edit: apply salt outside of the kdf]Here I thought GCM was some modern foolproof/footgunless design.
1: https://csrc.nist.gov/csrc/media/Projects/crypto-publication...
Web Crypto is faster and has many more devs working in the different implementations between all the companies and doesn't require any includes.
Just levels of trust. I'd happily use the former if the latter didn't exist.
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 :)
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.
https://github.com/cfe84/plaf
https://security.stackexchange.com/questions/184305/why-woul...
(also, use the built-in WebCrypto API instead of the crypto-js package)
I bet that most programmers cannot implement a Galois Field in Javascript. Meanwhile, CBC is just "encrypt then xor".
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.
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.
Literally one of the most common things to fuck up when doing crypto yourself.
The things you are describing are difficult and dangerous for developers to build.
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.
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.
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.
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.
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.
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.
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).
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.
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'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.
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
> 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.
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 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.
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.
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.
... 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".
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.
It seems that the hmac validation makes the GCM unnecessary in their thread model
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...
Relevant comment here: https://github.com/robinmoisson/staticrypt/issues/19#issueco...
The solution (with a server) is: https://oauth2-proxy.github.io/oauth2-proxy/
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...
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.
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.
But in practise nobody uses 100 character long strings. Security is the intersection of real users with systems, not imaginary ones.
Multiple websites I use daily have that length of password or longer.
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.
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.
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).
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.
https://github.com/MaxLaumeister/pagecrypt
https://www.maxlaumeister.com/pagecrypt/
[1] https://github.com/robinmoisson/staticrypt#alternatives-to-s...
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.
[0]: https://www.privacyprotect.dev/
I don't get it.
Great UI, though. But I still don't get it.
[0] https://developer.mozilla.org/en-US/docs/Web/API/FileSystem [1] https://mprimi.github.io/portable-secret
- run encryption locally
- do not upload anything
You may not realize that’s the case because it’s a web form
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.
https://ithemes.com/blog/what-is-the-htaccess-file/#password...
> 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.
Curious. Can you say more about your use case, even if only in general terms?
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.
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 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)
This also works without a webserver. It can be a local file that is opened in a browser.