How to password protect a static HTML page with no JS
I recently saw Robin Moisson's method of password protecting a statically served HTML page. It's quite neat! But it does rely on JavaScript. That got me wondering if there was a way to encrypt a static page only using CSS?
And... I think I've done it!
I'll warn you now, this is a deeply stupid way to solve the problem.
Here's a screencast of the demo in action:
Type the password and the page decrypts!!!!!
This abuses some interesting CSS features.
First, you can display the <style>
element on the page.
HTML<style>
style {
display: block;
}
</style>
Secondly, you can make the CSS editable by the user with contenteditable
HTML<style contenteditable="true">...</style>
As soon as a user types into the CSS, it is applied to the page. No need for JS.
So if a user types in a password...?
Let's step back a bit. How can we encrypt text using CSS? I know... WEBFONTS!
A WOFF2 webfont is a Brotli compressed file. If I've read the spec correctly (LOL!) removing a chunk of a small file should render the file too damaged to read.
It's possible to convert the WOFF2 into Base64 and use it in the CSS:
CSS@font-face {
font-family:'encrypt_sans';
src:url('data:application/font-woff2;charset=utf-8;base64,d09GMg...') format('woff2');
}
Cut a chunk out of the middle of that font, and use that missing piece as the password. Foolproof!
But - I hear you say - how to encrypt text using a font? Well, that's easy!
That cleverclogs Eli Grey has a font which encrypts text. It's magical. Well, OK, it's ROT13. Obviously, any substitution cipher can probably be broken using frequency analysis.
Of course, it is possible to use font ligatures to obfuscate the text even more. See Bullshit Sans as an example
So, there you have it. A way to sort of encrypt a statically served HTML file without using JavaScript.
Demo and Source
Possible Improvements
This was a demo hastily put together while hungover one weekend. There's lots of room for improvement.
The UI abuses CSS to hide some of the boilerplate involved. It could be made to look nicer.
There's no way to generate an "encrypted" font. Ideally someone (not me!) would take a plaintext and generate a scrambled and ligatured font to automagically do this.
It is inaccessible to screen readers. The font doesn't change the underlying text.
Brotli compressed WOFF2 files might be recoverable even after substantial damage.
This is really really stupid.
Alan Bell said on mastodon.ie:
@Edent I am impressed and horrified. Possibly even horrified and impressed.
Nicola said on twitter.com:
You had me at "this is a deeply stupid way to solve the problem."
Denilson says:
Back in the 1990s and early 2000s, someone created a "clever" way to put static pages behind a password, using a little bit of JavaScript to make it look nice. Essentially, the tool would ask for a password, and then try to load a page containing the password as the name. If found, that page would likely redirect to another page under some "secret" directory.
This worked because if no one had any link to the "secret" pages, no one would know they exist or their path. Even though it used JavaScript, the password was stored server-side, with no easy way to brute-force it (other than doing too many HTTP requests). It was better than most (stupid) JavaScript-based password prompts (that could be bypassed by disabling JavaScript or by looking at the source-code). The password ended up in plaintext in both server logs and browser history and anywhere else, because the password was the filename. Remember, this was a time of Geocities and many other free hosting sites. HTTPS usage was very limited, and even "Dynamic HTML" was at its infancy.
https://web.archive.org/web/20000823234706/http://www.pagetutor.com/keeper/
Elegant codes says:
A combination of your idea with Denilson comment, simpler to use:
@font-face { font-family:'encrypt_sans'; src:url('encrypted_fonts/this_is_where_to_put_the_password.woff') format('woff2'); }
Kra says:
@Elegant codes: Only if you can hide static files. It is simpler to replace the URL /something-encrypted with /something-decrypted.
We can also embed pictures like screenshots and QR codes in the font and only show them when some certain text is entered.
Kra says:
@Kra: w.r.t. QR codes, the pixels must be distributed among different ligatures and the characters be arranged as if it is solving a jigsaw puzzle.
Code élégant says:
@kra I mean that, instead of replacing a part of the woff font as this action is prone to error and the font can be "repaired" (thus allowing decryption), the web page could start in this state:
@font-face { font-family:'encrypt_sans'; src:url('encrypted_fonts/this_is_where_to_put_the_password.woff') format('woff2'); } with instruction to replace "this_is_where_to_put_the_password" by the actual password (which is the name of the font). Listing of directory "/encrypted_fonts/" being denied, obviously 😉
Kra says:
@Code élégant Still the same problem. Your new method comes with the shortcoming that the font is no longer (mostly)-public and you must disable directory listing. /shrug
CoopCoding says:
I wonder if you could also use Subresource Integrity with a css link element somewhow? Where the hash is the password. 🤔
More comments on Mastodon.