Skip to content

Commit

Permalink
use URL fragment for logout/auto-decrypt (closes #156)
Browse files Browse the repository at this point in the history
  • Loading branch information
robinmoisson committed Mar 29, 2023
1 parent cc37c5c commit 228af74
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 27 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ staticrypt test.html --engine webcrypto
**Encrypt a file and get a shareable link containing the hashed password** - you can include your file URL or leave blank:

```bash
# you can also pass '--share' without specifying the URL to get the `?staticrypt_pwd=...`
# you can also pass '--share' without specifying the URL to get the `#staticrypt_pwd=...`
staticrypt test.html MY_LONG_PASSWORD --share https://example.com/test_encrypted.html --engine webcrypto
# => https://example.com/test_encrypted.html?staticrypt_pwd=5bfbf1343c7257cd7be23ecd74bb37fa2c76d041042654f358b6255baeab898f
# => https://example.com/test_encrypted.html#staticrypt_pwd=5bfbf1343c7257cd7be23ecd74bb37fa2c76d041042654f358b6255baeab898f
```

**Encrypt all html files in a directory** and replace them with encrypted versions (`{}` will be replaced with each file name by the `find` command - if you wanted to move the encrypted files to an `encrypted/` directory, you could use `-o encrypted/{}`):
Expand Down Expand Up @@ -106,7 +106,7 @@ The password argument is optional if `STATICRYPT_PASSWORD` is set in the environ
you can use: "statycrypt -s". [string]
--share Get a link containing your hashed password that
will auto-decrypt the page. Pass your URL as a
value to append "?staticrypt_pwd=<hashed_pwd>",
value to append "#staticrypt_pwd=<hashed_pwd>",
or leave empty to display the hash to append.
[string]
--short Hide the "short password" warning.
Expand Down Expand Up @@ -175,7 +175,7 @@ If no value is provided the stored password doesn't expire, you can also give it

#### "Logging out"

You can clear StatiCrypt values in localStorage (effectively "logging out") at any time by appending `staticrypt_logout` to the URL query parameters (`mysite.com?staticrypt_logout`).
You can clear StatiCrypt values in localStorage (effectively "logging out") at any time by appending `staticrypt_logout` to the URL fragment (`mysite.com#staticrypt_logout`).

#### Encrypting multiple pages

Expand Down
2 changes: 1 addition & 1 deletion cli/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ function parseCommandLineArguments() {
.option("share", {
describe:
'Get a link containing your hashed password that will auto-decrypt the page. Pass your URL as a value to append '
+ '"?staticrypt_pwd=<hashed_pwd>", or leave empty to display the hash to append.',
+ '"#staticrypt_pwd=<hashed_pwd>", or leave empty to display the hash to append.',
type: "string",
})
.option("short", {
Expand Down
2 changes: 1 addition & 1 deletion cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ async function runStatiCrypt() {

const hashedPassword = await cryptoEngine.hashPassphrase(password, salt);

console.log(url + "?staticrypt_pwd=" + hashedPassword);
console.log(url + "#staticrypt_pwd=" + hashedPassword);
}

// TODO: remove in the next major version bump. This is to allow a security update to some versions without breaking
Expand Down
49 changes: 40 additions & 9 deletions example/example_encrypted.html
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@
const decode = codec.init(cryptoEngine).decode;

// variables to be filled when generating the file
const encryptedMsg = '1c9cbdfdb99a165b6459b329f37250ec99c30c53c5cca71d28e19314268896ec613dc0443a36e38dc5a983145dda064fc7716024e87c32ae5dbe03a888731e53e0869a0e739134d91fb343c0b11c759a2a9f9ac755544274af0fad8959919ad91e285405f9255b9ad8ce83f3f8e13e367c0b061b1bd84df78440a63196469743d34a887819d31111750f0de9d915e85020ca3b4cd209e5ad82678c572fc677642a8884fff4943b6366d4172d515519db96b6476f23066d36ae40b1b3bdd3c69a',
const encryptedMsg = 'df7428ed075decd3c4ff9d8ab6d2bea4410854c863d705789fb22e14b7da7ee20e94c593047abb9ba34d6519eebc879d2097bb918c0af0d4e248959849fb9c6bbb93aba054806c8773d1e4b63ec317185ad5462a9919dda986716c67bb57a89a044de3e25707cded482657c4a0208e9916aaa9d839f090eaaeb95603e05db11fe4bc37c4d98b9170124ce1c7ca18fe39c2f179e23eee61ba7d79cb3145e8833936c62adeffce1f5e129745c89541faa8100bfde4733bfa9c0ecf04768b3d1889',
salt = 'b93bbaf35459951c47721d1f3eaeb5b9',
labelError = 'Bad password!',
isRememberEnabled = true,
Expand Down Expand Up @@ -600,6 +600,31 @@
localStorage.removeItem(rememberExpirationKey);
}

/**
* Clear storage if we are logging out
*
* @returns - whether we logged out
*/
function logoutIfNeeded() {
const logoutKey = "staticrypt_logout";

// handle logout through query param
const queryParams = new URLSearchParams(window.location.search);
if (queryParams.has(logoutKey)) {
clearLocalStorage();
return true;
}

// handle logout through URL fragment
const hash = window.location.hash.substring(1);
if (hash.includes(logoutKey)) {
clearLocalStorage();
return true;
}

return false;
}

/**
* To be called on load: check if we want to try to decrypt and replace the HTML with the decrypted content, and
* try to do it if needed.
Expand All @@ -614,11 +639,8 @@
// show the remember me checkbox
document.getElementById('staticrypt-remember-label').classList.remove('hidden');

// if we are login out, clear the storage and terminate
const queryParams = new URLSearchParams(window.location.search);

if (queryParams.has("staticrypt_logout")) {
clearLocalStorage();
// if we are login out, terminate
if (logoutIfNeeded()) {
return false;
}

Expand Down Expand Up @@ -652,9 +674,18 @@
return false;
}

function decryptOnLoadFromQueryParam() {
function decryptOnLoadFromUrl() {
const passwordKey = "staticrypt_pwd";

// get the password from the query param
const queryParams = new URLSearchParams(window.location.search);
const hashedPassphrase = queryParams.get("staticrypt_pwd");
const hashedPassphraseQuery = queryParams.get(passwordKey);

// get the password from the url fragment
const hashRegexMatch = window.location.hash.substring(1).match(new RegExp(passwordKey + "=(.*)"));
const hashedPassphraseFragment = hashRegexMatch ? hashRegexMatch[1] : null;

const hashedPassphrase = hashedPassphraseFragment || hashedPassphraseQuery;

if (hashedPassphrase) {
return decryptAndReplaceHtml(hashedPassphrase);
Expand All @@ -665,7 +696,7 @@

// try to automatically decrypt on load if there is a saved password
window.onload = async function () {
let hasDecrypted = await decryptOnLoadFromQueryParam();
let hasDecrypted = await decryptOnLoadFromUrl();

if (!hasDecrypted) {
hasDecrypted = await decryptOnLoadFromRememberMe();
Expand Down
4 changes: 2 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ <h4>
<div class="form-group">
<label class="no-style">
<input type="checkbox" id="remember" checked>
Add "Remember me" checkbox (append <code>?staticrypt_logout</code> to your URL to logout)
Add "Remember me" checkbox (append <code>#staticrypt_logout</code> to your URL to logout)
<small>
<abbr class="text-muted"
title="The password will be stored in clear text in the browser's localStorage upon entry by the user. See &quot;More options&quot; to set the expiration (default: none)">
Expand Down Expand Up @@ -616,7 +616,7 @@ <h2>Encrypted HTML</h2>

/**
* Register something happened - this uses a simple Supabase function to implement a counter, and allows use to drop
* google analytics. We don't store the IP or any personal information.
* google analytics.
*
* @param action
*/
Expand Down
47 changes: 39 additions & 8 deletions lib/password_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,31 @@
localStorage.removeItem(rememberExpirationKey);
}

/**
* Clear storage if we are logging out
*
* @returns {boolean} - whether we logged out
*/
function logoutIfNeeded() {
const logoutKey = "staticrypt_logout";

// handle logout through query param
const queryParams = new URLSearchParams(window.location.search);
if (queryParams.has(logoutKey)) {
clearLocalStorage();
return true;
}

// handle logout through URL fragment
const hash = window.location.hash.substring(1);
if (hash.includes(logoutKey)) {
clearLocalStorage();
return true;
}

return false;
}

/**
* To be called on load: check if we want to try to decrypt and replace the HTML with the decrypted content, and
* try to do it if needed.
Expand All @@ -245,11 +270,8 @@
// show the remember me checkbox
document.getElementById('staticrypt-remember-label').classList.remove('hidden');

// if we are login out, clear the storage and terminate
const queryParams = new URLSearchParams(window.location.search);

if (queryParams.has("staticrypt_logout")) {
clearLocalStorage();
// if we are login out, terminate
if (logoutIfNeeded()) {
return false;
}

Expand Down Expand Up @@ -283,9 +305,18 @@
return false;
}

function decryptOnLoadFromQueryParam() {
function decryptOnLoadFromUrl() {
const passwordKey = "staticrypt_pwd";

// get the password from the query param
const queryParams = new URLSearchParams(window.location.search);
const hashedPassphrase = queryParams.get("staticrypt_pwd");
const hashedPassphraseQuery = queryParams.get(passwordKey);

// get the password from the url fragment
const hashRegexMatch = window.location.hash.substring(1).match(new RegExp(passwordKey + "=(.*)"));
const hashedPassphraseFragment = hashRegexMatch ? hashRegexMatch[1] : null;

const hashedPassphrase = hashedPassphraseFragment || hashedPassphraseQuery;

if (hashedPassphrase) {
return decryptAndReplaceHtml(hashedPassphrase);
Expand All @@ -296,7 +327,7 @@

// try to automatically decrypt on load if there is a saved password
window.onload = async function () {
let hasDecrypted = await decryptOnLoadFromQueryParam();
let hasDecrypted = await decryptOnLoadFromUrl();

if (!hasDecrypted) {
hasDecrypted = await decryptOnLoadFromRememberMe();
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "staticrypt",
"version": "2.5.0",
"version": "2.6.0",
"description": "Based on the [crypto-js](https://github.com/brix/crypto-js) library, StatiCrypt uses AES-256 to encrypt your input with your passphrase and put it in a HTML file with a password prompt that can decrypted in-browser (client side).",
"main": "index.js",
"files": [
Expand Down
2 changes: 1 addition & 1 deletion scripts/index_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ <h4>
<div class="form-group">
<label class="no-style">
<input type="checkbox" id="remember" checked>
Add "Remember me" checkbox (append <code>?staticrypt_logout</code> to your URL to logout)
Add "Remember me" checkbox (append <code>#staticrypt_logout</code> to your URL to logout)
<small>
<abbr class="text-muted"
title="The password will be stored in clear text in the browser's localStorage upon entry by the user. See &quot;More options&quot; to set the expiration (default: none)">
Expand Down

0 comments on commit 228af74

Please sign in to comment.