【Python】必学!教你如何在日志中隐藏明文密码?看完包会的!(附带免费源码)

news2024/9/28 21:21:04

前言

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

在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全套学习资料已经上传,朋友们如果需要可以扫描下方CSDN官方认证二维码免费领取【保证100%免费】

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2168449.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

施耐德EcoStruxure Machine SCADA Expert(EMSE)数据监测(十八)

通过EMSE与sql数据库连接,可以实现一些过程数据的监测、存档,实现生产过程的可视化。 1.创建sql数据库表单 新建一个名为Table_Monitor的表单,添加三个元素:Re_Index 序号;Re_Date 时间;Re_Temper 温度(需要监测的数据) 2.EMSE内关联变量 2.1 先创建网格 2.2 选择数据…

unity CustomEditor的基本使用

CustomEditor用来自定义脚本的编辑面板 其基本使用方式 先准备一个类&#xff0c;继承MonoBehaviour 定义一个变量&#xff0c;然后准备一个类&#xff0c;继承自Editor 在CustomEditor中指定要去修改的类型&#xff0c;通过serializedObject.FindProperty(变量名)的方式来获…

Ubuntu下安装向日葵:闪退

下载 https://sunlogin.oray.com/download 初次安装 $ sudo dpkg -i SunloginClient_15.2.0.63064_amd64.deb 正在选中未选择的软件包 sunloginclient。 (正在读取数据库 ... 系统当前共安装有 234281 个文件和目录。) 准备解压 SunloginClient_15.2.0.63064_amd64.deb ..…

Java.动态代理

1.创建一个接口 package Mydynamicproxy1;public interface Star {public abstract String sing(String str);public abstract void dance(String str); }2.创建一个BigStar类&#xff0c;要实现Star这个接口 package Mydynamicproxy1;public class BigStar implements Star{…

甘肃非遗文化网站:Spring Boot开发实战

3 系统分析 当用户确定开发一款程序时&#xff0c;是需要遵循下面的顺序进行工作&#xff0c;概括为&#xff1a;系统分析–>系统设计–>系统开发–>系统测试&#xff0c;无论这个过程是否有变更或者迭代&#xff0c;都是按照这样的顺序开展工作的。系统分析就是分析系…

Java EE中的编码问题及解决方案

Java EE中的编码问题及解决方案 在Java EE开发中&#xff0c;处理字符编码是确保数据正确传输和显示的重要环节。不同的编码不一致会导致乱码&#xff0c;影响用户体验。本文将总结在Java EE中可能遇到的编码问题及其解决方案。 1. 输入数据编码问题 在表单提交时&#xff0c…

【中级通信工程师】终端与业务(三):电信业务

【零基础3天通关中级通信工程师】 终端与业务(三)&#xff1a;电信业务 本文是中级通信工程师考试《终端与业务》科目第三章《电信业务》的复习资料和真题汇总。终端与业务是通信考试里最简单的科目&#xff0c;有效复习通过率可达90%以上&#xff0c;本文结合了高频考点和近几…

代码随想录算法训练营第十六天|512.找树左下角的值 112. 路径总和 113. 路径总和ii 106.从中序与后序遍历序列构造二叉树

512.找树左下角的值 给定一个二叉树&#xff0c;在树的最后一行找到最左边的值。 示例 1: 示例 2: 思路&#xff1a; 递归三部曲&#xff1a; 参数和返回值&#xff1a;传入节点是参数&#xff0c;返回值是最终值int终止条件&#xff1a;遇到空节点直接返回&#xff0c;或者…

SD2.0 Specification之写保护

文章目录 1 机械开关写保护&#xff08;由主机负责实现效果&#xff09;2 卡内部写保护&#xff08;由卡负责实现&#xff09;3 密码写保护 本文章主要讲解关于SD2.0写保护功能的内容&#xff0c;基础概念和其它内容请参考以下文章。 SD2.0 Specification简述 SD卡支持3种写保护…

论文阅读《Co-clustering for Federated Recommender System》

论文概况 本文是2024 WWW的一篇联邦推荐论文&#xff0c;提出了一个基于特定类别物品相似度来进行聚类的联邦推荐框架。 Introduction 分析了经典聚类技术KMeans在联邦推荐设置中的不足&#xff0c;提出了一种新的共聚类联邦推荐机制CoFedRec&#xff0c;该机制在每个通信回合…

华为NAT ALG技术的实现

双向NAT技术&#xff1a;经过防火墙的2报文源IP地址和目的IP地址都同时被转换&#xff0c;外网发送报文给内网服务器&#xff0c;先转换目的IP地址&#xff0c;然后符合安全策略后&#xff0c;在替换源IP地址&#xff0c;然后将记录写入防火墙会话表&#xff0c;并发送出报文&a…

结构化知识抽取案例

假设我们有一个包含中文电影信息的数据库表 movies&#xff0c;其中包含以下字段&#xff1a; movie_id (电影ID)title (电影标题)year (上映年份)genre (类型)director (导演)rating (评分) 表中的部分数据如下&#xff1a; 知识抽取步骤 数据获取&#xff1a;从数据库中查…

WAF,全称Web Application Firewall,好用WAF推荐

WAF&#xff0c;全称Web Application Firewall&#xff0c;即Web应用防火墙&#xff0c;是一种网络安全设备&#xff0c;旨在保护Web应用程序免受各种Web攻击&#xff0c;如SQL注入、跨站脚本&#xff08;XSS&#xff09;、跨站请求伪造&#xff08;CSRF&#xff09;等。 WAF通…

第166天:应急响应-拒绝服务钓鱼指南DDOS压力测试邮件反制分析应用日志

案例一&#xff1a;内网应急-日志分析-爆破&横向&数据库 数据库 这里不同数据库日志不一样&#xff0c;我用mysql分析 首先MySQL数据库需要支持远程连接 GRANT ALL PRIVILEGES ON . TO root% IDENTIFIED BY 123.com WITH GRANT OPTION; 其次开启日志 -- 查看general…

Qt_线程介绍与使用

目录 1、QThread常用API 2、Qt线程安全 3、使用线程QThread 4、connect函数的第五个参数 5、Qt互斥锁 5.1 QMutexLocker 6、条件变量 7、信号量 结语 前言&#xff1a; 线程是应用程序开发非常重要的概念&#xff0c;在Qt中&#xff0c;用QThread类来实现多线程&a…

Spring项目中的统一结果返回

本文介绍的是通过Spring提供的接口进行结果统一封装&#xff0c;指的是成功返回的结果&#xff0c;不包含异常或者错误情况&#xff08;这一块移步到统一异常处理&#xff09;。另一种统一结果返回的方式&#xff0c;就是手动让每个接口的返回中类型都相同&#xff0c;这种方法…

ant design vue组件中table组件设置分组头部和固定总结栏

问题&#xff1a;遇到了个需求&#xff0c;不仅要设置分组的头部&#xff0c;还要在顶部有个统计总和的栏。 分组表头的配置主要是这个&#xff0c;就是套娃原理&#xff0c;不需要展示数据的直接写个title就行&#xff0c;需要展示数据的字段才需要详细的配置属性。 const co…

实现简易 vuedraggable 的拖拽排序功能

一、案例效果 拖拽计数4实现手动排序 二、案例代码 <draggable:list"searchResult.indicator":group"{ name: indicators }"item-key"field"handle".drag-handle-icon"><divclass"field-item"v-for"(item…

YOLOv8+注意力机制+PyQt5玉米病害检测系统完整资源集合

资源包含可视化的玉米病害检测系统&#xff0c;基于最新的YOLOv8注意力机制训练的玉米病害检测模型&#xff0c;和基于PyQt5制作的可视玉米病害系统&#xff0c;包含登陆页面和检测页面&#xff0c;该系统可自动检测和识别图片或视频当中出现的七类玉米病害&#xff1a;矮花叶病…

Java每日面试题(mysql优化)(day14)

目录 连接查询MySQL常用函数汇总SQL Select 语句的执行顺序数据库三范式MyISAM 存储引擎 与 InnoDB 引擎区别索引索引的优缺点索引的分类索引结构B树与B树的区别索引失效的几种情况 数据库锁MySql 优化 连接查询 MySQL常用函数汇总 SQL Select 语句的执行顺序 数据库三范式 第一…