使用 `pytest-mask-secrets` 保护测试中的敏感信息

36 篇文章 0 订阅
3 篇文章 0 订阅

在软件开发中,保持敏感数据的安全和隐私至关重要。应用程序日志是常见的信息泄露途径之一,因此需要小心保护,避免在日志中出现敏感信息。同样的关注和风险也适用于测试日志,因为它们可能泄露密码或访问令牌。CI 工作流中的工具通常提供机制来屏蔽日志中的敏感数据,这些工具使用简单且高效。然而,在某些情况下,这些工具可能不足以提供全面的保护。

## 为什么 CI 工作流的屏蔽机制可能不够

例如,GitHub Actions 在处理敏感信息方面表现不错。任何在工作流中定义的机密信息都会自动从捕获的输出中屏蔽,这种机制非常有效。然而,和所有 CI 系统一样,它也有其局限性。如果输出报告通过不同路径保存,例如保存到文件、生成 JUnit 报告或发送到远程日志存储,GitHub Actions 无法检查这些内容并屏蔽其中的敏感信息。

此外,测试并不总是在 CI 工作流中运行,即使在本地运行测试时,敏感信息也可能需要隐藏。想象一下,你在本地运行测试并分享日志以讨论问题,却在不经意间包含了带有访问令牌的 URL。

因此,在测试日志中处理敏感数据的机制在各个层次上都是必不可少的。最好的方法是在测试级别或测试框架中直接实现这一机制,确保敏感信息不会从源头泄露,从而防止它们在系统中上传递。

## 在适当的层次增加保护

直接在测试中维护敏感信息的屏蔽机制可能相当耗时且容易出错,通常感觉像是在打一场失败的仗。例如,想象一下,你需要设计一个带有令牌参数的 URL。这个 URL 必须在请求中使用时与在日志中显示的方式不同。

相比之下,在测试框架内拦截报告生成过程,提供了一个理想的机会来挂钩进程,并修改记录以消除敏感信息。这种方法对测试透明,无需修改测试代码,就像 CI 工作流中的秘密屏蔽功能一样——简单地运行它,然后无需再管理敏感信息。它自动化了过程,确保敏感数据得到保护,而不会增加测试设置的复杂性。

这正是 `pytest-mask-secrets` 所做的,当然前提是你在使用 pytest 进行测试执行。pytest 提供了丰富而灵活的插件系统,允许你在任何日志生成之前挂钩到进程中,此时所有数据都已收集完毕。这使得在输出记录之前搜索并删除敏感值变得非常容易。

## 实际演示:如何操作

为了说明其工作原理,最有效的方法是一个简单的示例。下面是一个可能不代表真实测试场景的简单测试,但它很好地展示了 `pytest-mask-secrets` 的功能。

import logging
import os

def test_password_length():
    password = os.environ["PASSWORD"]
    logging.info("Tested password: %s", password)
    assert len(password) > 18
```

在这个示例中,存在一个可能会失败的断言(事实上它确实会失败),并且包含了一个包含敏感信息的日志消息。虽然在日志中包含敏感信息似乎有些愚蠢,但想象一下一个场景,你的 URL 中包含一个令牌参数,并且启用了详细的调试日志记录。在这种情况下,类似 `requests` 的库可能会无意中以这种方式记录敏感信息。

现在进行测试。首先,设置测试所需的秘密信息:

```shell
(venv) $ export PASSWORD="TOP-SECRET"
```

接下来,运行测试:

```shell
(venv) $ pytest --log-level=info test.py
============================= test session starts ==============================
platform linux -- Python 3.12.4, pytest-8.3.2, pluggy-1.5.0
rootdir: /tmp/tmp.AvZtz7nHZS
collected 1 item                                                               

test.py F                                                                [100%]

=================================== FAILURES ===================================
_____________________________ test_password_length _____________________________

    def test_password_length():
        password = os.environ["PASSWORD"]
        logging.info("Tested password: %s", password)
>       assert len(password) > 18
E       AssertionError: assert 10 > 18
E        +  where 10 = len('TOP-SECRET')

test.py:8: AssertionError
------------------------------ Captured log call -------------------------------
INFO     root:test.py:7 Tested password: TOP-SECRET
=========================== short test summary info ============================
FAILED test.py::test_password_length - AssertionError: assert 10 > 18
============================== 1 failed in 0.03s ===============================
```

默认情况下,秘密值在输出中出现了两次:一次是在捕获的日志消息中,另一次是在失败的断言中。

但是,如果安装了 `pytest-mask-secrets` 会怎样呢?

```shell
(venv) $ pip install pytest-mask-secrets
```

并进行相应配置。它需要知道保存秘密信息的环境变量列表。通过设置 `MASK_SECRETS` 变量来完成此操作:

```shell
(venv) $ export MASK_SECRETS=PASSWORD
```

现在,重新运行测试:

```shell
(venv) $ pytest --log-level=info test.py
============================= test session starts ==============================
platform linux -- Python 3.12.4, pytest-8.3.2, pluggy-1.5.0
rootdir: /tmp/tmp.AvZtz7nHZS
plugins: mask-secrets-1.2.0
collected 1 item                                                               

test.py F                                                                [100%]

=================================== FAILURES ===================================
_____________________________ test_password_length _____________________________

    def test_password_length():
        password = os.environ["PASSWORD"]
        logging.info("Tested password: %s", password)
>       assert len(password) > 18
E       AssertionError: assert 10 > 18
E        +  where 10 = len('*****')

test.py:8: AssertionError
------------------------------ Captured log call -------------------------------
INFO     root:test.py:7 Tested password: *****
=========================== short test summary info ============================
FAILED test.py::test_password_length - AssertionError: assert 10 > 18
============================== 1 failed in 0.02s ===============================
```

现在,秘密值不会被直接输出,而是显示为星号。任务完成,测试报告中不再包含敏感数据。

## 总结

从示例中看,`pytest-mask-secrets` 似乎并没有比 GitHub Actions 默认执行的更多的操作,使得这些努力看起来有些多余。然而,如前所述,CI 工作流执行工具仅屏蔽捕获的输出中的敏感信息,而不会修改 JUnit 文件和其他报告。如果没有 `pytest-mask-secrets`,敏感数据仍可能暴露在这些文件中——这适用于任何由 pytest 生成的报告。另一方面,`pytest-mask-secrets` 不会屏蔽 `log_cli` 选项启用时的直接输出,因此 CI 工作流的屏蔽功能仍然有用。通常,最好结合使用这两个工具,以确保敏感数据得到充分保护。

  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

幻想多巴胺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值