CVE-2022-0338: Insertion of Sensitive Information into Log File (CWE-532)
value: Insertion of Sensitive Information into Log File in Conda loguru prior to 0.5.3.


CWE description: Information written to log files can be of a sensitive nature and give valuable guidance to an attacker or expose sensitive user information.
CVSS v3 base score: 4.3
Published: 2022-01-25T09:15Z
Repository: https://github.com/delgan/loguru
Language: Python
Files changed: docs/resources/recipes.rst, loguru/_logger.py
Commit date: 2022-01-24 15:52:28 +0100
Commit message: Document several security considerations and best practices

Diff: diff --git a/docs/resources/recipes.rst b/docs/resources/recipes.rst
index dea9bfc..1ba66a3 100644
--- a/docs/resources/recipes.rst
+++ b/docs/resources/recipes.rst
@@ -13,10 +13,11 @@ Code snippets and recipes for ``loguru``
 .. |warnings.warn| replace:: :func:`warnings.warn`
 .. |contextlib.redirect_stdout| replace:: :func:`contextlib.redirect_stdout`
 .. |copy.deepcopy| replace:: :func:`copy.deepcopy`
 .. |os.fork| replace:: :func:`os.fork`
 .. |multiprocessing| replace:: :mod:`multiprocessing`
+.. |pickle| replace:: :mod:`pickle`
 .. |traceback| replace:: :mod:`traceback`
 .. |Thread| replace:: :class:`~threading.Thread`
 .. |Process| replace:: :class:`~multiprocessing.Process`
 .. |Pool| replace:: :class:`~multiprocessing.pool.Pool`
 .. |Pool.map| replace:: :meth:`~multiprocessing.pool.Pool.map`
@@ -45,10 +46,70 @@ Code snippets and recipes for ``loguru``
 .. _zmq: https://github.com/zeromq/pyzmq
 
 .. _`GH#88`: https://github.com/Delgan/loguru/issues/88
 .. _`GH#132`: https://github.com/Delgan/loguru/issues/132
 
+
+Security considerations when using Loguru
+-----------------------------------------
+
+Firstly, if you use |pickle| to load log messages (e.g. from the network), make sure the source is trustable or sign the data to verify its authenticity before deserializing it. If you do not take these precautions, malicious code could be executed by an attacker. You can read more details in this article: `What’s so dangerous about pickles? <https://intoli.com/blog/dangerous-pickles/>`_
+
+.. code::
+
+    import hashlib
+    import hmac
+    import pickle
+
+    def client(connection):
+        data = pickle.dumps("Log message")
+        digest =  hmac.digest(b"secret-shared-key", data, hashlib.sha1)
+        connection.send(digest + b" " + data)
+
+    def server(connection):
+        expected_digest, data = connection.read().split(b" ", 1)
+        data_digest = hmac.digest(b"secret-shared-key", data, hashlib.sha1)
+        if not hmac.compare_digest(data_digest, expected_digest):
+            print("Integrity error")
+        else:
+            message = pickle.loads(data)
+            logger.info(message)
+
+
+You should also avoid logging a message that could be maliciously hand-crafted by an attacker. Calling ``logger.debug(message, value)`` is roughly equivalent to calling ``print(message.format(value))`` and the same safety rules apply. In particular, an attacker could force printing of assumed hidden variables of your application. Here is an article explaining the possible vulnerability: `Be Careful with Python's New-Style String Format <https://lucumr.pocoo.org/2016/12/29/careful-with-str-format/>`_.
+
+.. code::
+
+    SECRET_KEY = 'Y0UC4NTS33Th1S!'
+
+    class SomeValue:
+        def __init__(self, value):
+            self.value = value
+
+    # If user types "{value.__init__.__globals__[SECRET_KEY]}" then the secret key is displayed.
+    message = "[Custom message] " + input()
+    logger.info(message, value=SomeValue(10))
+
+
+Note that by default, Loguru will display the value of existing variables when an ``Exception`` is logged. This is very useful for debugging but could lead to credentials appearing in log files. Make sure to turn it off in production (or set the ``LOGURU_DIAGNOSE=NO`` environment variable).
+
+.. code::
+
+    logger.add("out.log", diagnose=False)
+
+
+Another thing you should consider is to change the access permissions of your log file. Loguru creates files using the built-in |open| function, which means by default they might be read by a different user than the owner. If this is not desirable, be sure to modify the default access rights.
+
+.. code::
+
+    def opener(file, flags):
+        return os.open(file, flags, 0o600)
+
+    logger.add("combined.log", opener=opener)
+
+
+
 Changing the level of an existing handler
 -----------------------------------------
 
 Once a handler has been added, it is actually not possible to update it. This is a deliberate choice in order to keep the Loguru's API minimal. Several solutions are possible, tough, if you need to change the configured ``level`` of a handler. Chose the one that best fits your use case.
 
diff --git a/loguru/_logger.py b/loguru/_logger.py
index e2c66fd..17d0810 100644
--- a/loguru/_logger.py
+++ b/loguru/_logger.py
@@ -197,12 +197,12 @@ class Logger:
     code. By reading the output logs of your application, you gain a better understanding of the
     flow of your program and you more easily track and debug unexpected behaviors.
 
     Handlers to which the logger sends log messages are added using the |add| method. Note that you
     can use the |Logger| right after import as it comes pre-configured (logs are emitted to
-    |sys.stderr| by default). Messages can be logged with different severity levels and using braces
-    attributes like the |str.format| method do.
+    |sys.stderr| by default). Messages can be logged with different severity levels and they can be
+    formatted using curly braces (it uses |str.format| under the hood).
 
     When a message is logged, a "record" is associated with it. This record is a dict which contains
     information about the logging context: time, function, file, line, thread, level... It also
     contains the ``__name__`` of the module, this is why you don't need named loggers.
 

Vulnerable code:     |sys.stderr| by default). Messages can be logged with different severity levels and using braces
    attributes like the |str.format| method do.
Fixed code: .. |pickle| replace:: :mod:`pickle`

Security considerations when using Loguru
-----------------------------------------

Firstly, if you use |pickle| to load log messages (e.g. from the network), make sure the source is trustable or sign the data to verify its authenticity before deserializing it. If you do not take these precautions, malicious code could be executed by an attacker. You can read more details in this article: `What’s so dangerous about pickles? <https://intoli.com/blog/dangerous-pickles/>`_

.. code::

    import hashlib
    import hmac
    import pickle

    def client(connection):
        data = pickle.dumps("Log message")
        digest =  hmac.digest(b"secret-shared-key", data, hashlib.sha1)
        connection.send(digest + b" " + data)

    def server(connection):
        expected_digest, data = connection.read().split(b" ", 1)
        data_digest = hmac.digest(b"secret-shared-key", data, hashlib.sha1)
        if not hmac.compare_digest(data_digest, expected_digest):
            print("Integrity error")
        else:
            message = pickle.loads(data)
            logger.info(message)


You should also avoid logging a message that could be maliciously hand-crafted by an attacker. Calling ``logger.debug(message, value)`` is roughly equivalent to calling ``print(message.format(value))`` and the same safety rules apply. In particular, an attacker could force printing of assumed hidden variables of your application. Here is an article explaining the possible vulnerability: `Be Careful with Python's New-Style String Format <https://lucumr.pocoo.org/2016/12/29/careful-with-str-format/>`_.

.. code::

    SECRET_KEY = 'Y0UC4NTS33Th1S!'

    class SomeValue:
        def __init__(self, value):
            self.value = value

    # If user types "{value.__init__.__globals__[SECRET_KEY]}" then the secret key is displayed.
    message = "[Custom message] " + input()
    logger.info(message, value=SomeValue(10))


Note that by default, Loguru will display the value of existing variables when an ``Exception`` is logged. This is very useful for debugging but could lead to credentials appearing in log files. Make sure to turn it off in production (or set the ``LOGURU_DIAGNOSE=NO`` environment variable).

.. code::

    logger.add("out.log", diagnose=False)


Another thing you should consider is to change the access permissions of your log file. Loguru creates files using the built-in |open| function, which means by default they might be read by a different user than the owner. If this is not desirable, be sure to modify the default access rights.

.. code::

    def opener(file, flags):
        return os.open(file, flags, 0o600)

    logger.add("combined.log", opener=opener)



    |sys.stderr| by default). Messages can be logged with different severity levels and they can be
    formatted using curly braces (it uses |str.format| under the hood).