UNPKG

staticrypt

Version:

Password protect a static HTML file without a backend - StatiCrypt uses AES-256 wiht WebCrypto to encrypt your input with your long password and put it in a HTML file with a password prompt that can decrypted in-browser (client side).

298 lines (258 loc) 15.1 kB
<!DOCTYPE html> <html class="staticrypt-html"> <head> <meta charset="utf-8" /> <title>/*[|template_title|]*/0</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <!-- do not cache this page --> <meta http-equiv="cache-control" content="max-age=0" /> <meta http-equiv="cache-control" content="no-cache" /> <meta http-equiv="expires" content="0" /> <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" /> <meta http-equiv="pragma" content="no-cache" /> <style> .staticrypt-hr { margin-top: 20px; margin-bottom: 20px; border: 0; border-top: 1px solid #eee; } .staticrypt-page { width: 360px; padding: 8% 0 0; margin: auto; box-sizing: border-box; } .staticrypt-form { position: relative; z-index: 1; background: #ffffff; max-width: 360px; margin: 0 auto 100px; padding: 45px; text-align: center; box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24); } .staticrypt-form input[type="password"], input[type="text"] { background: inherit; border: 0; box-sizing: border-box; /* This ensures padding is included in the total width */ font-size: 14px; outline: 0; padding: 15px 30px 15px 15px; /* Adjust the padding to ensure there is space for the icon */ width: 100%; } .staticrypt-password-container { position: relative; outline: 0; background: #f2f2f2; width: 100%; border: 0; margin: 0 0 15px; box-sizing: border-box; } .staticrypt-toggle-password-visibility { cursor: pointer; height: 20px; opacity: 60%; padding: 13px; position: absolute; right: 0; top: 50%; transform: translateY(-50%); width: 20px; } .staticrypt-form .staticrypt-decrypt-button { text-transform: uppercase; outline: 0; background: /*[|template_color_primary|]*/ 0; width: 100%; border: 0; padding: 15px; color: #ffffff; font-size: 14px; cursor: pointer; } .staticrypt-form .staticrypt-decrypt-button:hover, .staticrypt-form .staticrypt-decrypt-button:active, .staticrypt-form .staticrypt-decrypt-button:focus { background: /*[|template_color_primary|]*/ 0; filter: brightness(92%); } .staticrypt-html { height: 100%; } .staticrypt-body { height: 100%; margin: 0; } .staticrypt-content { height: 100%; margin-bottom: 1em; background: /*[|template_color_secondary|]*/ 0; font-family: "Arial", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .staticrypt-instructions { margin-top: -1em; margin-bottom: 1em; } .staticrypt-title { font-size: 1.5em; } label.staticrypt-remember { display: flex; align-items: center; margin-bottom: 1em; } .staticrypt-remember input[type="checkbox"] { transform: scale(1.5); margin-right: 1em; } .hidden { display: none !important; } .staticrypt-spinner-container { height: 100%; display: flex; align-items: center; justify-content: center; } .staticrypt-spinner { display: inline-block; width: 2rem; height: 2rem; vertical-align: text-bottom; border: 0.25em solid gray; border-right-color: transparent; border-radius: 50%; -webkit-animation: spinner-border 0.75s linear infinite; animation: spinner-border 0.75s linear infinite; animation-duration: 0.75s; animation-timing-function: linear; animation-delay: 0s; animation-iteration-count: infinite; animation-direction: normal; animation-fill-mode: none; animation-play-state: running; animation-name: spinner-border; } @keyframes spinner-border { 100% { transform: rotate(360deg); } } @media screen and (-webkit-min-device-pixel-ratio: 0) { .staticrypt-form input[type="password"], input[type="text"] { font-size: 16px; } } </style> </head> <body class="staticrypt-body"> <div id="staticrypt_loading" class="staticrypt-spinner-container"> <div class="staticrypt-spinner"></div> </div> <div id="staticrypt_content" class="staticrypt-content hidden"> <div class="staticrypt-page"> <div class="staticrypt-form"> <div class="staticrypt-instructions"> <p class="staticrypt-title">/*[|template_title|]*/0</p> <p>/*[|template_instructions|]*/0</p> </div> <hr class="staticrypt-hr" /> <form id="staticrypt-form" action="#" method="post"> <div class="staticrypt-password-container"> <input id="staticrypt-password" type="password" name="password" placeholder="/*[|template_placeholder|]*/0" autofocus /> <img class="staticrypt-toggle-password-visibility" alt="/*[|template_toggle_show|]*/0" title="/*[|template_toggle_show|]*/0" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNS4yIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTM4LjggNS4xQzI4LjQtMy4xIDEzLjMtMS4yIDUuMSA5LjJTLTEuMiAzNC43IDkuMiA0Mi45bDU5MiA0NjRjMTAuNCA4LjIgMjUuNSA2LjMgMzMuNy00LjFzNi4zLTI1LjUtNC4xLTMzLjdMNTI1LjYgMzg2LjdjMzkuNi00MC42IDY2LjQtODYuMSA3OS45LTExOC40YzMuMy03LjkgMy4zLTE2LjcgMC0yNC42Yy0xNC45LTM1LjctNDYuMi04Ny43LTkzLTEzMS4xQzQ2NS41IDY4LjggNDAwLjggMzIgMzIwIDMyYy02OC4yIDAtMTI1IDI2LjMtMTY5LjMgNjAuOEwzOC44IDUuMXpNMjIzLjEgMTQ5LjVDMjQ4LjYgMTI2LjIgMjgyLjcgMTEyIDMyMCAxMTJjNzkuNSAwIDE0NCA2NC41IDE0NCAxNDRjMCAyNC45LTYuMyA0OC4zLTE3LjQgNjguN0w0MDggMjk0LjVjOC40LTE5LjMgMTAuNi00MS40IDQuOC02My4zYy0xMS4xLTQxLjUtNDcuOC02OS40LTg4LjYtNzEuMWMtNS44LS4yLTkuMiA2LjEtNy40IDExLjdjMi4xIDYuNCAzLjMgMTMuMiAzLjMgMjAuM2MwIDEwLjItMi40IDE5LjgtNi42IDI4LjNsLTkwLjMtNzAuOHpNMzczIDM4OS45Yy0xNi40IDYuNS0zNC4zIDEwLjEtNTMgMTAuMWMtNzkuNSAwLTE0NC02NC41LTE0NC0xNDRjMC02LjkgLjUtMTMuNiAxLjQtMjAuMkw4My4xIDE2MS41QzYwLjMgMTkxLjIgNDQgMjIwLjggMzQuNSAyNDMuN2MtMy4zIDcuOS0zLjMgMTYuNyAwIDI0LjZjMTQuOSAzNS43IDQ2LjIgODcuNyA5MyAxMzEuMUMxNzQuNSA0NDMuMiAyMzkuMiA0ODAgMzIwIDQ4MGM0Ny44IDAgODkuOS0xMi45IDEyNi4yLTMyLjVMMzczIDM4OS45eiIvPjwvc3ZnPg==" /> </div> <label id="staticrypt-remember-label" class="staticrypt-remember hidden"> <input id="staticrypt-remember" type="checkbox" name="remember" /> /*[|template_remember|]*/0 </label> <input type="submit" class="staticrypt-decrypt-button" value="/*[|template_button|]*/0" /> </form> </div> </div> </div> <script> // these variables will be filled when generating the file - the template format is '/*[|variable_name|]*/0' const staticryptInitiator = /*[|js_staticrypt|]*/ 0; const templateError = "/*[|template_error|]*/0", templateToggleAltShow = "/*[|template_toggle_show|]*/0", templateToggleAltHide = "/*[|template_toggle_hide|]*/0", isRememberEnabled = /*[|is_remember_enabled|]*/ 0, staticryptConfig = /*[|staticrypt_config|]*/ 0; // you can edit these values to customize some of the behavior of StatiCrypt const templateConfig = { rememberExpirationKey: "staticrypt_expiration", rememberPassphraseKey: "staticrypt_passphrase", replaceHtmlCallback: null, clearLocalStorageCallback: null, }; // init the staticrypt engine const staticrypt = staticryptInitiator.init(staticryptConfig, templateConfig); // try to automatically decrypt on load if there is a saved password window.onload = async function () { const { isSuccessful } = await staticrypt.handleDecryptOnLoad(); // if we didn't decrypt anything on load, show the password prompt. Otherwise the content has already been // replaced, no need to do anything if (!isSuccessful) { // hide loading screen document.getElementById("staticrypt_loading").classList.add("hidden"); document.getElementById("staticrypt_content").classList.remove("hidden"); document.getElementById("staticrypt-password").focus(); // show the remember me checkbox if (isRememberEnabled) { document.getElementById("staticrypt-remember-label").classList.remove("hidden"); } } }; // toggle password visibility const toggleIcon = document.querySelector(".staticrypt-toggle-password-visibility"); // these two icons are coming from FontAwesome const imgSrcEyeClosed = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNS4yIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTM4LjggNS4xQzI4LjQtMy4xIDEzLjMtMS4yIDUuMSA5LjJTLTEuMiAzNC43IDkuMiA0Mi45bDU5MiA0NjRjMTAuNCA4LjIgMjUuNSA2LjMgMzMuNy00LjFzNi4zLTI1LjUtNC4xLTMzLjdMNTI1LjYgMzg2LjdjMzkuNi00MC42IDY2LjQtODYuMSA3OS45LTExOC40YzMuMy03LjkgMy4zLTE2LjcgMC0yNC42Yy0xNC45LTM1LjctNDYuMi04Ny43LTkzLTEzMS4xQzQ2NS41IDY4LjggNDAwLjggMzIgMzIwIDMyYy02OC4yIDAtMTI1IDI2LjMtMTY5LjMgNjAuOEwzOC44IDUuMXpNMjIzLjEgMTQ5LjVDMjQ4LjYgMTI2LjIgMjgyLjcgMTEyIDMyMCAxMTJjNzkuNSAwIDE0NCA2NC41IDE0NCAxNDRjMCAyNC45LTYuMyA0OC4zLTE3LjQgNjguN0w0MDggMjk0LjVjOC40LTE5LjMgMTAuNi00MS40IDQuOC02My4zYy0xMS4xLTQxLjUtNDcuOC02OS40LTg4LjYtNzEuMWMtNS44LS4yLTkuMiA2LjEtNy40IDExLjdjMi4xIDYuNCAzLjMgMTMuMiAzLjMgMjAuM2MwIDEwLjItMi40IDE5LjgtNi42IDI4LjNsLTkwLjMtNzAuOHpNMzczIDM4OS45Yy0xNi40IDYuNS0zNC4zIDEwLjEtNTMgMTAuMWMtNzkuNSAwLTE0NC02NC41LTE0NC0xNDRjMC02LjkgLjUtMTMuNiAxLjQtMjAuMkw4My4xIDE2MS41QzYwLjMgMTkxLjIgNDQgMjIwLjggMzQuNSAyNDMuN2MtMy4zIDcuOS0zLjMgMTYuNyAwIDI0LjZjMTQuOSAzNS43IDQ2LjIgODcuNyA5MyAxMzEuMUMxNzQuNSA0NDMuMiAyMzkuMiA0ODAgMzIwIDQ4MGM0Ny44IDAgODkuOS0xMi45IDEyNi4yLTMyLjVMMzczIDM4OS45eiIvPjwvc3ZnPg=="; const imgSrcEyeOpened = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1NzYgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNS4yIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTI4OCAzMmMtODAuOCAwLTE0NS41IDM2LjgtMTkyLjYgODAuNkM0OC42IDE1NiAxNy4zIDIwOCAyLjUgMjQzLjdjLTMuMyA3LjktMy4zIDE2LjcgMCAyNC42QzE3LjMgMzA0IDQ4LjYgMzU2IDk1LjQgMzk5LjRDMTQyLjUgNDQzLjIgMjA3LjIgNDgwIDI4OCA0ODBzMTQ1LjUtMzYuOCAxOTIuNi04MC42YzQ2LjgtNDMuNSA3OC4xLTk1LjQgOTMtMTMxLjFjMy4zLTcuOSAzLjMtMTYuNyAwLTI0LjZjLTE0LjktMzUuNy00Ni4yLTg3LjctOTMtMTMxLjFDNDMzLjUgNjguOCAzNjguOCAzMiAyODggMzJ6TTE0NCAyNTZhMTQ0IDE0NCAwIDEgMSAyODggMCAxNDQgMTQ0IDAgMSAxIC0yODggMHptMTQ0LTY0YzAgMzUuMy0yOC43IDY0LTY0IDY0Yy03LjEgMC0xMy45LTEuMi0yMC4zLTMuM2MtNS41LTEuOC0xMS45IDEuNi0xMS43IDcuNGMuMyA2LjkgMS4zIDEzLjggMy4yIDIwLjdjMTMuNyA1MS4yIDY2LjQgODEuNiAxMTcuNiA2Ny45czgxLjYtNjYuNCA2Ny45LTExNy42Yy0xMS4xLTQxLjUtNDcuOC02OS40LTg4LjYtNzEuMWMtNS44LS4yLTkuMiA2LjEtNy40IDExLjdjMi4xIDYuNCAzLjMgMTMuMiAzLjMgMjAuM3oiLz48L3N2Zz4="; toggleIcon.addEventListener("click", function () { const passwordInput = document.getElementById("staticrypt-password"); if (passwordInput.type === "password") { passwordInput.type = "text"; toggleIcon.src = imgSrcEyeOpened; toggleIcon.alt = templateToggleAltHide; toggleIcon.title = templateToggleAltHide; } else { passwordInput.type = "password"; toggleIcon.src = imgSrcEyeClosed; toggleIcon.alt = templateToggleAltShow; toggleIcon.title = templateToggleAltShow; } }); // handle password form submission document.getElementById("staticrypt-form").addEventListener("submit", async function (e) { e.preventDefault(); const password = document.getElementById("staticrypt-password").value, isRememberChecked = document.getElementById("staticrypt-remember").checked; const { isSuccessful } = await staticrypt.handleDecryptionOfPage(password, isRememberChecked); if (!isSuccessful) { alert(templateError); } }); </script> </body> </html>