Open gersondiesel opened 3 years ago
Hey Gerson,
Thank you for reaching out about this. One thing I've seen users do is allow their browser to save the password in its built-in password manager. That way, the next time the user visits the page, they can just click "submit" and the password is already filled in for them.
What do you think, is that a useful workaround for your use case?
If that workaround isn't ideal, I'd be down to chat some more about this!
Thanks, -Max
Hello, Mr Max! Thank you for your return for my question. I will give this solution for my clients and wait for the feedbacks!
Thank you again!
Best regards, Gerson
@gersondiesel I've noticed an idea on this issue: https://github.com/robinmoisson/staticrypt/issues/120. And below there's a gist https://gist.github.com/epicfaace/c1a4452401af14d35b60fe211f2c1559 for you to take a look at.
And for the expire time, you could also store timestamp into the localstorage and check at every page refresh, if expired, then remove the stored password and timestamp.
@wzdiyb Thank you for linking that. Cool solutions here for solving the "stale password" issue, and to encrypt an entire site. I think that answers the last unknowns I had about implementing this.
So this will be probably the first thing I implement if I come back to work on PageCrypt again in the future. In the mean time, pull requests welcome.
@wzdiyb Thank you for linking that. Cool solutions here for solving the "stale password" issue, and to encrypt an entire site. I think that answers the last unknowns I had about implementing this.
So this will be probably the first thing I implement if I come back to work on PageCrypt again in the future. In the mean time, pull requests welcome.
Hi MaxLaumeister,
it should be me to say thank you to you, the author of staticrypt and all contributors offering us such great tools/solutions to solve our problems.
I'd love to make a pull request or send you the template html of mine in the future (according to my schedule, it will last approx. 1-2 months), as i am working on a project and have currently no extra time.
Have a nice day~
@MaxLaumeister, @gersondiesel
Below is the demo of the modification. I tested on my side in several simple cases with success. But there still might be some problems/bugs to fix or some scenarios to think about. (Also, a better coding style is needed for my demo version).
Demo works:
The basic things are:
- At every page refresh/visit, check if password and expire time stored locally. If (yes) and (the time does not expire), then try decrypting it.
invalidPassEl
Element, so users won't know that the page already tried decrypting it before they do anything) Reason: when user successfully decrypted page 1 and passphrase stored locally, then the user loads another page 2 (if page 2 using a password other than page 1's), the passphrase stored (which belongs to page 1) should not be removed at loading time of the page 2 for a better user experience unless the user inputted the correct password for page 2(once password for page 2 inputted correctly, the new passphrase would be stored).
(It might be possible for users that they only want to take a glance at pages other than page 1, but just a glance. They might not concern about if the stored password could decrypt pages other than page 1. Let's make an example, user A encrypts his Profile page, he might see others peoples' profile pages, but whether the his stored passphrase could decrypt others' pages, it does not really matter. So, if visiting others encrypted pages could lead to removing his own password locally and letting him input the password again when he comes back to his own profile page, he would definitely be mad at it. )
(In my project, every page may have its own password; but this solution also works well when the entire website using the same password, 'cause in this case redirecting from one page to another page, the auto decrypting will do the job perfectly beforehand without inputting the password another time unless the password expires. )
catch (e) {
if (mode === '1') {
invalidPassEl.style.display = "inline";
passEl.value = "";
}
}
invalidPassEl
Element, so users won't know that the page already tried decrypting it before they do anything)
catch (e) {
if (mode === '0') {
// Wrong passphrase for this page, clear the stored passphrase and expire timestamp
clearPW_in_LocalStorage();
} else if (mode === '1') {
invalidPassEl.style.display = "inline";
passEl.value = "";
}
}
- Modification on doSubmit() function, add a parameter for it.
So that we could justify if the doSubmit() is called by the page itself(auto decrypting when page reloading) or by the user(clicking the button or pressing the enter key)
- Once doSubmit() is called by user and the password inputted is correct, then update/set the data - passphrase and expire time - in the LocalStorage.
Currently i've hardcoded the expire time into 60s. Also, it could be set by the parameter given by the user while calling the encrypting tool (like encrypt.py
)(i did not implemented version setting the expire time with parameter given by encrypting tool)
- Two extra functions for setting/clearing the data in LocalStorage are declared and implemented
For better handling the manipulation of LocalStorage relatively.
- Notice: Modification on
onclick
attribute of thesubmitPass
-Element
I've changed it from submitPass.onclick =doSubmit;
to submitPass.onclick = function() {doSubmit('1');}
instead of submitPass.onclick =doSubmit('1');
, because once it is set to doSubmit('1')
, it will automatically run, which is not what we except.
Below is the demo code in detail:
<script>
var pl = /*{{ENCRYPTED_PAYLOAD}}*/"";
var submitPass = document.getElementById('submitPass');
var passEl = document.getElementById('pass');
var invalidPassEl = document.getElementById('invalidPass');
var successEl = document.getElementById('success');
var contentFrame = document.getElementById('contentFrame');
if (pl === "") {
submitPass.disabled = true;
passEl.disabled = true;
alert("This page is meant to be used with the encryption tool. It doesn't work standalone.");
}
function doSubmit(mode) {
// @Param mode:int
// * 0: auto submit
// * 1: manual submit
try {
var local_passphrase = atob(localStorage.getItem("passphrase"));
var passEl_value = (mode === '0') ? local_passphrase : passEl.value;
var decrypted = decryptFile(CryptoJS.enc.Base64.parse(pl.data), passEl_value, CryptoJS.enc.Base64.parse(pl.salt), CryptoJS.enc.Base64.parse(pl.iv));
if (decrypted === "") throw "No data returned";
// Set default iframe link targets to _top so all links break out of the iframe
decrypted = decrypted.replace("<head>", "<head><base href=\".\" target=\"_top\">");
srcDoc.set(contentFrame, decrypted);
successEl.style.display = "inline";
passEl.disabled = true;
submitPass.disabled = true;
setTimeout(function() {
dialogWrap.style.display = "none";
}, 50);
if (mode === '1') {
// If the function is called by clicking or pressing enter, the passphrase entered by user should be stored locally along with the expire time
savePW_in_LocalStorage()
}
} catch (e) {
if (mode === '1') {
invalidPassEl.style.display = "inline";
passEl.value = "";
}
}
}
submitPass.onclick = function() {doSubmit('1');}
passEl.onkeypress = function(e){
if (!e) e = window.event;
var keyCode = e.keyCode || e.which;
invalidPassEl.style.display = "none";
if (keyCode == '13'){
// Enter pressed
doSubmit('1');
return false;
}
}
function decryptFile(contents, password, salt, iv) {
var _cp = CryptoJS.lib.CipherParams.create({
ciphertext: contents
});
var key = CryptoJS.PBKDF2(password, salt, { keySize: 256/32, iterations: 100 });
var decrypted = CryptoJS.AES.decrypt(_cp, key, {iv: iv});
return decrypted.toString(CryptoJS.enc.Utf8);
}
function savePW_in_LocalStorage() {
// Store the passphrase in the input field with the help of btoa() function
localStorage.setItem("passphrase", btoa(passEl.value));
// Store the expire time (currently hard coded it to 60 secs <-> 60000 milisecs)
const datetime_now_millisecs = new Date().getTime(); // Milliseconds since Epoch time
const datetime_expire_millisecs = datetime_now_millisecs + 60000;
localStorage.setItem("expire_time", datetime_expire_millisecs.toString());
}
function clearPW_in_LocalStorage() {
localStorage.removeItem("passphrase");
localStorage.removeItem("expire_time");
}
// Check if passphrase and expire time are stored locally
if (localStorage.getItem("passphrase") && localStorage.getItem("expire_time")) {
const datetime_now = new Date();
const datetime_expire = new Date(parseInt(localStorage.getItem("expire_time"), 10));
// Check if the passphrase already expires, if not, do a do a submit for auto decrypting
if (datetime_expire >= datetime_now) {
doSubmit('0');
} else {
// Otherwise, clear the stored passphrase and expire timestamp
clearPW_in_LocalStorage();
}
}
</script>
Hi! First of all - a big thank you to everyone involved in creating this original PageCrypt project! 🎉
I've created a modern version of this library with some improvements, including:
sessionStorage
API which is cleared when the browser is restarted).npm scripts
in JS projects for example.Again, thanks for the inspiration and sharing this project as open source! I'd be happy to get feedback and work together to make further improvements!
Thank you @Greenheart for this modern rewrite! I mentioned it on the project page so that anyone using my version knows that yours is out there too with these new features.
It looks like your version targets developers with a robust and scriptable API, whereas my version mostly targets average users with an easy to use web page where they can visit and encrypt one-off pages, so to me it seems for now that these forks serve separate niches. I appreciate the work you've done to modernize the code with Web Crypto, which was unfortunately not widely supported when I created this project in 2015.
I think the Web Crypto code and the session storage code are prime candidates to be integrated into a public-facing web app. This project isn't where my resources are focused right now, but if I revisit it I plan to apply those features so that end users can encrypt documents with them by visiting the PageCrypt project page.
In any case, thank you again for rewriting a modern version of this project!
Thanks @MaxLaumeister! Greatly appreciated! 😃
Indeed, I initially only targeted developers since it fits my most common use cases.
However, I'm thinking about adding a public webpage to make encryption more user friendly in the modern PageCrypt too: https://github.com/Greenheart/pagecrypt/issues/15
Can't promise anything in terms of when it will be finished - if anyone wants to collaborate on a PR let me know in the issue linked above or by opening a PR! :)
Quick update: I recently added native Web Crypto support to PageCrypt in a series of commits this November.
Hello! Thanks for this excelent tool! It is possible to preserve the page unlocked (unencrypted) for a while, like X hours or X days, to prevent the need for input password every time the page is refreshed?
Thanks, again!