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如何在日志中隐藏明文密码的更多相关文章

  1. 在Wifi网络中嗅探明文密码(HTTP POST请求、POP等)

    全世界,现在大约50%的网站没有使用SSL加密,天朝尤其多. 我们都知道通过HTTP发送的数据都是明文,没有使用任何加密,即使是在数据敏感的登录页面. 本文的目的是:如果你在不熟悉的网络环境中,要注意 ...

  2. python练习 根据日志中的ip和url排序

    #!/usr/bin/env python #coding:utf-8 def open_file(file_name): res={} with open(file_name) as f: for ...

  3. Python习题-统计日志中访问次数超过限制的IP

    #1.1分钟之内ip访问次数超过200次的,就给他的ip加入黑名单#需求分析: #1.读日志,1分钟读一次 #2.获取这1分钟之内所有访问的ip #3.判断ip出现的次数,如果出现200次,那么就加入 ...

  4. 用python将Mybatis日志中的Preparing与Parameters转换为可执行SQL并且美化格式

    一.源代码下载 https://gitee.com/rmtic/SqlLog 说明: 1.可以处理oracle中正则表达中的'?',解决参数'?'冲突问题 2.如果要处理mysql和处理oracle中 ...

  5. 如果你的application.properties中还存在明文密码----加密Spring Boot中的application.properties

    1 概述 什么?都2020年了还在Spring Boot的配置文件中写明文密码? 虽然是小项目,明文也没人看. 明文简单快捷方便啊!!! 你看直接用户名root密码123456多么简单!!! ... ...

  6. 获取windows凭证管理器明文密码

    1.运行cmdkey /list查看windows保存凭证 方法1.mimikaz mimikatz vault::cred 2.利用powershell尝试获取 windows 普通凭据类型中的明文 ...

  7. 转:获取windows凭证管理器明文密码

    1.运行cmdkey /list查看windows保存凭证 方法1.mimikaz mimikatz vault::cred 2.利用powershell尝试获取 windows 普通凭据类型中的明文 ...

  8. mimikatz+procdump 提取 Windows 明文密码

    0x00 原理 获取到内存文件 lsass.exe 进程 (它用于本地安全和登陆策略) 中存储的明文登录密码. 0x01 操作 Windows10/2012 以下的版本:1.上传 procdump 执 ...

  9. 孤荷凌寒自学python第二十四天python类中隐藏的私有方法探秘

    孤荷凌寒自学python第二十四天python类中隐藏的私有方法探秘 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 今天发现了python的类中隐藏着一些特殊的私有方法. 这些私有方法不管我 ...

  10. Python之向日志输出中添加上下文信息

    除了传递给日志记录函数的参数(如msg)外,有时候我们还想在日志输出中包含一些额外的上下文信息.比如,在一个网络应用中,可能希望在日志中记录客户端的特定信息,如:远程客户端的IP地址和用户名.这里我们 ...

随机推荐

  1. React后台管理系统07 首页布局

    注释掉App.tsx中的几个路由组件: 将Home.tsx中的代码使用ant Design网站中的布局进行替换 复制的代码如下: import { DesktopOutlined, FileOutli ...

  2. 鸿蒙星空的太白星 | WebView给元服务调用JS API指明方向

    ​漆黑深夜夜凉如水,繁星盛开于无垠苍穹.清风徐来,一片薄云,夜空顿然失色,有些阴霾.天空中最亮的星,太白星,在薄云中依然闪耀,如同海上迷雾中的灯塔,为迷失方向的船只指明方向. 元服务是华为提供的一种面 ...

  3. PostgreSQL 12 文档: 部分 I. 教程

    部分 I. 教程 欢迎来到PostgreSQL教程.下面的几章将为那些新接触PostgreSQL.关系数据库概念和 SQL 语言的读者给出一个简单介绍.我们只假定读者拥有关于如何使用计算机的一般知识. ...

  4. iis7以上 ssl 证书导入

    证书导入 开始 -〉运行 -〉MMC: 启动控制台程序,选择菜单"文件"中的"添加/删除管理单元"-> "添加",从"可用的 ...

  5. Debian12配置NTP时间同步

    环境 查看系统版本:lsb_release -a 配置NTP时间同步 下面的配置需要用到管理员权限,可以使用su切换到管理员权限. 查看/修正 时区 查看系统时区:timedatectl 如果时区不是 ...

  6. 介绍一个简易的MAUI安卓打包工具

    介绍一个简易的MAUI安卓打包工具 它可以帮助进行MAUI安卓的打包. 虽然也是用MAUI写的,但是只考虑了Windows版本,mac还不太会. 没什么高级的功能,甚至很简陋,它能做的,只是节省你从M ...

  7. Java面试题全集(二)

    1. ⾸先CopyOnWriteArrayList内部也是⽤过数组来实现的,在向CopyOnWriteArrayList添加元素时,会复制⼀个新的数组,写操作在新数组上进⾏,读操作在原数组上进⾏ 2. ...

  8. 【overcome error】dereferencing pointer to incomplete type

    @ 目录 前言 解决 代码情况 分析问题 尾声 前言 这个问题是我在学习数据结构链栈部分遇到的,英文报错如题所示,中文意思是:取消引用不完整类型的指针,在百度一圈也没明白,(百度搜索,看一个和全看基本 ...

  9. quarkus依赖注入之五:拦截器(Interceptor)

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本文是<quarkus依赖注入> ...

  10. cesium中限制地图浏览范围

    https://blog.csdn.net/qq_42740164/article/details/119375782?ops_request_misc=%257B%2522request%255Fi ...