unverbuggt / mkdocs-encryptcontent-plugin

A MkDocs plugin that encrypt/decrypt markdown content with AES
https://unverbuggt.github.io/mkdocs-encryptcontent-plugin/
MIT License
123 stars 15 forks source link

option to use sessionStorage instead of localStorage for remember_password #31

Closed unverbuggt closed 1 year ago

unverbuggt commented 2 years ago

Hi,

while the remember_password function in convenient it implies a security risk.
If Alice f.ex. reads a page which she decrypted with the right password and suddeny Mallory enters the room and Alice was fast enough to close the browser, then Mallory would still be able check the browser history, find the page she last opened and it would be decrypted for him if remember_password was enabled. the default_expire_dalay won't change much about that.

If sessionStorage were used instead of localStorage in this case it woule be better, because the right after Alice had closed the Browser the password would be forgotten by the browser. I guess it depends on the browser implementation, but i think it isn't even necessary to write sessionStorage to disk where other applications could read it (but also on most OSes other applications could also read the memory of the browser it they had high enough priviledges).

again some small changes in plugin.py:

@@ -66,6 +66,7 @@ class encryptContentPlugin(BasePlugin):
         ('hljs', config_options.Type(bool, default=True)),
         ('mermaid2', config_options.Type(bool, default=True)),
         ('remember_password', config_options.Type(bool, default=False)),
+        ('session_storage', config_options.Type(bool, default=False)),
         ('fallback_path', config_options.Type(string_types, default='/')),
         ('default_expire_dalay', config_options.Type(int, default=int(24))),
         ('tag_encrypted_page', config_options.Type(bool, default=True)),
@@ -131,6 +132,7 @@ class encryptContentPlugin(BasePlugin):
             'hljs': self.config['hljs'],
             'mermaid2': self.config['mermaid2'],
             'remember_password': self.config['remember_password'],
+            'session_storage': self.config['session_storage'],
             'fallback_path': self.config['fallback_path'],
             'default_expire_dalay': int(self.config['default_expire_dalay']),
             'encrypted_something': self.config['encrypted_something'],

and decrypt-contents.tpl.js:

@@ -39,20 +39,36 @@ function setItemExpiry(key, value, ttl) {
         value: encodeURIComponent(value),
         expiry: now.getTime() + ttl,
     }
+  {% if session_storage -%}
+    sessionStorage.setItem('encryptcontent_' + encodeURIComponent(key), JSON.stringify(item))
+  {%- else %}
     localStorage.setItem('encryptcontent_' + encodeURIComponent(key), JSON.stringify(item))
+  {%- endif %}
 };

 /* Delete key with specific name in localStorage */
 function delItemName(key) {
+  {% if session_storage -%}
+    sessionStorage.removeItem('encryptcontent_' + encodeURIComponent(key));
+  {%- else %}
     localStorage.removeItem('encryptcontent_' + encodeURIComponent(key));
+  {%- endif %}
 };

 /* Get key:value from localStorage */
 function getItemExpiry(key) {
+  {% if session_storage -%}
+    var remember_password = sessionStorage.getItem('encryptcontent_' + encodeURIComponent(key));
+  {%- else %}
     var remember_password = localStorage.getItem('encryptcontent_' + encodeURIComponent(key));
+  {%- endif %}
     if (!remember_password) {
         // fallback to search default password defined by path
+      {% if session_storage -%}
+        var remember_password = sessionStorage.getItem('encryptcontent_' + encodeURIComponent("{{ fallback_path }}"));
+      {%- else %}
         var remember_password = localStorage.getItem('encryptcontent_' + encodeURIComponent("{{ fallback_path }}"));
+      {%- endif %}
         if (!remember_password) {
             return null
         }
@@ -61,7 +77,7 @@ function getItemExpiry(key) {
     const now = new Date()
     if (now.getTime() > item.expiry) {
         // if the item is expired, delete the item from storage and return null
-        localStorage.removeItem('encryptcontent_' + encodeURIComponent(key))
+        delItemName(key)
         return null
     }
     return decodeURIComponent(item.value)

but again just a suggestion. I also experience the page decryption to happen slightly fasten when using sessionStorage, but this could be an illusion.

CoinK0in commented 2 years ago

Hi,

You'r right, using the browser's local storage creates the situation described above. That's why I added a "Warning" in the README part of the "remember password" function. Using the storage session, covers this lack of security and after a few tests the whole is functional even with the storage in session. I can't remember why I originally changed from "cookie" to "local" without using "session", it seemed to me that caused a problem with the global password. Apparently this is not/no longer the case.

We could talk about the convenience advantage provided by local storage, but with a possible switch, the problem no longer seems to be one. I therefore think, by default, to define the storage of passwords in a "session" (Security first), and leave the possibility, for comfort, to store in the "local".

Afterwards I remain convinced that the best way is not to store the passwords directly in the browser and to use a password manager with their autofill function.

CoinK0in commented 1 year ago

Your suggestions for this security enhancement change are incorporated into the just released Version 2.3.0.

Thanks