Python如何在日志中隐藏明文密码

python,如何,日志,隐藏,明文,密码 · 浏览次数 : 63

小编点评

## Using logging modules in Python to hide sensitive data from logs This document provides an overview of hiding sensitive data from logs using the Python logging module. **Logging Basics:** * `logging` module provides a way to record messages with timestamps, names, and other details. * `logging.config` is used to configure the logging system. * `formatter` class formats log records and `filter` class allows selective filtering of logs. **Filtering Sensitive Data:** * `SensitiveFilter` class can be used to mask sensitive data in log messages. * It replaces specific patterns in the message with a designated placeholder, such as "***". * This helps to prevent unauthorized access to sensitive information. **Hiding Password in Logs:** * `SensitiveFormatter` is a customized formatter that uses `SensitiveFilter` to mask the `password` field. * It ensures that password information is not printed in logs. **Example:** ```python import logging # Configuring logging logging.config.dictConfig({"version": 1, "formatters": {"default": SensitiveFormatter}}) # Logging with password masking LOG = logging.getLogger(__name__) LOG.info("password=123456") # Output without password ``` **Benefits of using logging with filtering:** * Sensitive data is hidden from logs. * Improves security by preventing unauthorized access to sensitive information. * Makes logs more readable and easier to analyze. **Note:** * This is a simplified example, and the actual implementation may vary depending on your specific requirements. * Ensure that the patterns used for filtering are appropriate for your use case.

正文

Python如何在日志中隐藏明文密码

前言

在项目开发中,有的时候会遇到一些安全需求,用以提升程序整体的安全性,提高外来非法攻击的门槛,而在日志中隐藏明文密码打印便是最典型的安全需求之一。

在Python中,明文密码往往发生于命令执行参数、debug日志、依赖库打印等场景中。对于程序自身的明文密码打印,很轻易地就能通过修改相应代码行的方式修复,而对于非程序自身打印,比如依赖库、外部命令等,则比较棘手,无法通过直接修改代码的方式解决。其实,在Python中,logging日志模块提供了一些自定义方法以过滤特定字符串,绝大多数的Python程序均使用logging模块作为其日志记录系统,如果开发者已经得知相关明文密码打印的规则,且使用logging模块记录日志,那么使用在logging模块中过滤特定字符串的方法不失为一个很好的选择。

概念

logging日志模块是python的一个内置模块,该模块定义了一些函数和类,为上层应用程序或库实现了一个强大而又灵活的日志记录系统。

logging模块将日志的处理分为四个层次,分别是:

  • logger:logger向上层应用程序暴露接口,程序通过调用logger打印日志,比如logger.info,logger.error等等;
  • handler:handler用于将logger创建的日志记录输出至适合的目的地,比如标准输出、错误、文件等;
  • filter:filter对如何将日志记录输出提供了更细粒度的控制;
  • formatter:formatter指定了最终日志记录输出的格式。

如上,filter以及formatter层次均提供了对日志行为扩展的手段,针对明文密码打印问题,我们可以通过自定义filter或者formatter,使用特定规则过滤明文密码字段的方式实现。

LogRecord

LogRecord是日志的基本单元,每次应用程序调用Logger打印日志时,logging模块都会自动创建一个LogRecord实例,其记录了日志文本、参数、模块、行数乃至进程ID、线程ID等等有用的信息。

>>> type(record)
<class 'logging.LogRecord'>
>>> record.msg
'password=123456 %s %s'
>>> record.args
('1', '2')
>>> record.created
1697184354.6492243
>>> record.levelname
'INFO'
>>> record.name
'__main__'
>>> record.process
200

上面列出了一些LogRecord对象的属性,这些属性大部分也同样是最后格式化日志输出的参数。

filter

filter一般用作匹配并过滤部分日志,判断匹配条件的日志是否允许打印,它提供了一个filter方法,使用布尔值作为返回值,如果返回true则表示允许打印,否则表示不允许。

filter方法以LogRecord作为参数,这也表示除了过滤指定日志的功能以外,也能够对日志做更精细的控制。

class Filter(object):
    """
    Filter instances are used to perform arbitrary filtering of LogRecords.
    """
    def filter(self, record: LogRecord) -> bool:
        """
        Determine if the specified record is to be logged.

        Returns True if the record should be logged, or False otherwise.
        If deemed appropriate, the record may be modified in-place.
        """

formatter

formatter负责将LogRecord转化为最终的输出字符串,它主要是使用args来渲染msg,除此之外,如果LogRecord包含异常堆栈,那么也会打印出来。

formatter方法以LogRecord作为参数,并返回渲染处理后的字符串,当自定义formatter类时,我们能够既能够处理渲染前的LogRecord,也能修改渲染后的字符串。

class Formatter(object):
    """
    Formatter instances are used to convert a LogRecord to text.
    """
    def format(self, record: LogRecord) -> str:
        """
        Format the specified record as text.

        The record's attribute dictionary is used as the operand to a
        string formatting operation which yields the returned string.
        Before formatting the dictionary, a couple of preparatory steps
        are carried out. The message attribute of the record is computed
        using LogRecord.getMessage(). If the formatting string uses the
        time (as determined by a call to usesTime(), formatTime() is
        called to format the event time. If there is exception information,
        it is formatted using formatException() and appended to the message.
        """

使用formatter实现明文密码隐藏

import re
import logging
import logging.config

# 自定义formatter类
class SensitiveFormatter(logging.Formatter):
    """Formatter that removes sensitive information in urls."""
    @staticmethod
    def _mask_passwd(s) -> str:
        return re.sub(r'(?<=password=)\S+', r'***', s)

    def format(self, record) -> str:
        s = super().format(record)
        return self._mask_passwd(s)

LOGGING_CONFIG = {
    "version": 1,
    "formatters": {
        "default": {
            "()": SensitiveFormatter,
            "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
        }
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "formatter": "default",
            "stream": "ext://sys.stdout"
        },
    },
    "loggers": {},
    "root": {
        "level": "DEBUG",
        "handlers": [
            "console",
        ]
    }
}

logging.config.dictConfig(LOGGING_CONFIG)
LOG = logging.getLogger(__name__)

LOG.info('password=123456')
# 2023-10-13 16:58:50,443 - __main__ - INFO - password=***

使用filter实现明文密码隐藏

import re
import logging
import logging.config

# 自定义filter类
class SensitiveFilter(logging.Filter):
    def __init__(self, patterns):
        super().__init__()
        self._patterns = patterns

    def _mask(self, msg):
        if not isinstance(msg, str):
            return msg
        for pattern in self._patterns:
               msg = re.sub(pattern, r'***', msg)
        return msg

    def filter(self, record):
        record.msg = self._mask(record.msg)
        if isinstance(record.args, dict):
            for k in record.args.keys():
                record.args[k] = self._mask(record.args[k])
        elif isinstance(record.args, tuple):
            record.args = tuple(self._mask(arg) for arg in record.args)
        return super().filter(record)

LOGGING_CONFIG = {
    "version": 1,
    "filters": {
        "default": {
            "()": SensitiveFilter,
            "patterns": [
                r'(?<=password=)\S+',
            ],
        },
    },
    "formatters": {
        "default": {
            "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
        }
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "formatter": "default",
            "filters": [
                "default",
            ],
            "stream": "ext://sys.stdout"
        },
    },
    "loggers": {},
    "root": {
        "level": "DEBUG",
        "handlers": [
            "console",
        ]
    }
}

logging.config.dictConfig(LOGGING_CONFIG)
LOG = logging.getLogger(__name__)

LOG.info('password=123456')
# 2023-10-13 16:59:22,545 - __main__ - INFO - password=***

附录

Hiding Sensitive Data from Logs with Python (relaxdiego.com)

logging — Logging facility for Python — Python 3.12.0 documentation

与Python如何在日志中隐藏明文密码相似的内容:

Python如何在日志中隐藏明文密码

Python如何在日志中隐藏明文密码 前言 在项目开发中,有的时候会遇到一些安全需求,用以提升程序整体的安全性,提高外来非法攻击的门槛,而在日志中隐藏明文密码打印便是最典型的安全需求之一。 在Python中,明文密码往往发生于命令执行参数、debug日志、依赖库打印等场景中。对于程序自身的明文密码打

【Azure 应用服务】Azure Function Python函数中,如何获取Event Hub Trigger的消息Event所属于的PartitionID呢?

问题描述 在通过Azure Function消费Event Hub中的消息时,我们从Function 的 Trigger Details 日志中,可以获得当前Funciton中处理的消息是哪一个分区(PartitionID), 偏移量Offset,序列号SequenceNumber 等信息。 但是在

实践探讨Python如何进行异常处理与日志记录

本文分享自华为云社区《Python异常处理与日志记录构建稳健可靠的应用》,作者:柠檬味拥抱。 异常处理和日志记录是编写可靠且易于维护的软件应用程序中至关重要的组成部分。Python提供了强大的异常处理机制和灵活的日志记录功能,使开发人员能够更轻松地管理代码中的错误和跟踪应用程序的执行过程。在本文中,

《最新出炉》系列入门篇-Python+Playwright自动化测试-47-自动滚动到元素出现的位置

1.简介 在我们日常工作中或者生活中,经常会遇到我们的页面内容较多,一个屏幕范围无法完整展示内容,我们就需要滚动滚动条去到我们想要的地方,如下图页面,我们虽然在豆瓣首页,但是内容并不完整,如果我们想要直接点击电影模块中的选电影按钮,是需要往下滑动的。当页面超过屏幕的高度时候,需要滚动到元素出现的位置

python入门基础(15)--模块和python中数学、日期、时间类模块。

接上篇,当我们创建了很多类,比如 图书馆里的藏书,分社会科学类,艺术类、生活类、农业类、工业类等,而工业类又分为轻工业、重工业、信息工业,然后再细分。当分的越来越细时,程序就会越来越大。如何管理,便成了程序开发过程中一个重要的环节。于是可以按照图书馆分类管理的思想,对程序代码进行管理。 将一个应用程

Python日志模块:实战应用与最佳实践

**本文详细解析了Python的logging模块,从基本介绍到实际应用和最佳实践。我们通过具体的代码示例解释了如何高效地使用这个模块进行日志记录,以及如何避免常见的陷阱,旨在帮助读者更好地掌握这个强大的工具。** ![file](https://img2023.cnblogs.com/other/

基于Llama2模型的开源模型

2023年7月18日Meta开源了Llama2,在2万亿个Token上训练,可用于商业和研究,包括从7B到70B模型权重、预训练和微调的代码。相比Llama1,Llama2有较多提升,评估结果如下所示: 基于Llama2模型的开源模型如下所示: 1.WizardCoder Python V1.0 h

Python史上最全种类数据库操作方法,你能想到的数据库类型都在里面!甚至还有云数据库!

本文将详细探讨如何在Python中连接全种类数据库以及实现相应的CRUD(创建,读取,更新,删除)操作。我们将逐一解析连接MySQL,SQL Server,Oracle,PostgreSQL,MongoDB,SQLite,DB2,Redis,Cassandra,Microsoft Access,El

Python常见面试题011. 如何在Python中动态创建类?

011. 如何在Python中动态创建类? 说在前面 答案是type 你印象中的type是用来查看对象的类型的 li = [] type(li) # 得到list 对自定义的类是这样的 class Person: pass wuxianfeng = Person() type(wuxianfeng)

Python 引用不确定的函数

本文详细介绍了Python引用不确定的函数的表示方法、如何在Python中引用不确定的函数、如何在Python中调用不确定函数方法。