一种企业友好的检测和防止代码中出现机密信息的方法。

作者:Sec-Labs | 发布时间:

项目地址

https://github.com/Yelp/detect-secrets

detect-secrets项目简介

相关技术点

  • 正则表达式
  • git
  • Python

项目用途

detect-secrets是一个用于检测代码库中机密信息的Python模块,旨在为企业客户提供以向后兼容的方式,系统地防止新机密信息进入代码库,检测是否明确绕过这种防止机制,并提供机密信息列表以滚动和迁移至更安全的存储。该工具通过运行周期性的差分输出来识别任何新的机密信息是否已提交,以此避免了挖掘所有git历史记录的开销以及每次扫描整个代码库的需要。它的使用方法有:

  • 创建一个当前存储在git存储库中的潜在机密信息基线
  • 将新机密信息添加到基线中
  • 从基线中删除不再在代码库中的机密信息
  • 标记结果并缩小检查列表
  • 自定义插件和过滤器以满足特定需求
  • 基于文件或文件类型进行扫描

该工具可以帮助企业客户快速检测和处理可能存在的机密信息泄露风险,提升代码安全性

detect-secrets

关于

detect-secrets是一个名副其实的模块,用于在代码库中检测密码

与其他专注于查找密码的类似软件不同,此软件专为企业客户而设计:提供一个向后兼容、系统化的方法:

  1. 防止新密码进入代码库,
  2. 检测是否明确绕过此类防范措施,
  3. 提供一个密码列表,以便将其移至更安全的存储。

这样,您就可以创建一个关注点分离:接受您的大型存储库中*当前可能隐藏的密码(这就是我们所说的_基线_),但防止此问题变得更大,而不必处理将现有密码移走所需的巨大工作量。

它通过定期运行与启发式的正则表达式语句之间的差分输出来实现这一点,以确定是否提交了任何密码。这样,它避免了挖掘所有git历史记录的开销,以及每次扫描整个存储库的必要性。

有关最近更改的详细信息,请参见CHANGELOG.md

如果您想要贡献,请参见CONTRIBUTING.md

有关更详细的文档,请查看我们的其他文档

示例

快速开始:

创建当前在git存储库中找到的潜在密码的基线。

$ detect-secrets scan > .secrets.baseline

或,从不同的目录运行它:

$ detect-secrets -C /path/to/directory scan > /path/to/directory/.secrets.baseline

扫描非git跟踪的文件:

$ detect-secrets scan test_data/ --all-files > .secrets.baseline

将新密码添加到基线:

这将重新扫描您的代码库,并:

  1. 更新/升级您的基线以与最新版本兼容,
  2. 将它找到的任何新密码添加到您的基线中,
  3. 删除不再在您的代码库中的任何密码

这也将保留您标记的密码。

$ detect-secrets scan --baseline .secrets.baseline

对于早于版本0.9的基线,只需重新创建它。

警报新增的密码:

仅扫描暂存文件:

$ git diff --staged --name-only -z | xargs -0 detect-secrets-hook --baseline .secrets.baseline

扫描所有已跟踪文件:

$ git ls-files -z | xargs -0 detect-secrets-hook --baseline .secrets.baseline

查看所有已启用的插件:

$ detect-secrets audit

提供自定义插件:

from detect_secrets.plugins.common import File
from detect_secrets.plugins.base import BasePlugin

class ExamplePlugin(BasePlugin):
    """Example plugin to detect 'password' in file contents."""

    secret_type = 'Example Secret'

    def analyze(self, file: File, *args: Any) -> List[Secret]:
        """Checks the given file for secrets.

        :param File file: The contents of the file to analyze.
        :return: A list of `Secret` objects found.
        """
        secrets = []

        if 'password' in file.filename or 'password' in file.content:
            secrets.append(
                Secret('Example Secret', file.filename, 'password')
            )

        return secrets

安装

从源代码安装:

$ git clone https://github.com/Yelp/detect-secrets
$ cd detect-secrets
$ pip install -r requirements.txt
$ pip install -e .

从PyPI安装:

$ pip install detect-secrets

依赖

  • Python (3.5, 3.6, 3.7, 3.8)
  • Git

作者

许可证

Apache 2.01. 您想将机密信息添加到基线中吗?如果是,请使用 **detect-secrets scan**。 2. 您想警报不在基线中的新机密信息吗?如果是,请使用 **detect-secrets-hook**。 3. 您正在分析基线本身吗?如果是,请使用 **detect-secrets audit**。

将机密信息添加到基线中

$ detect-secrets scan --help
usage: detect-secrets scan [-h] [--string [STRING]] [--only-allowlisted]
                           [--all-files] [--baseline FILENAME]
                           [--force-use-all-plugins] [--slim]
                           [--list-all-plugins] [-p PLUGIN]
                           [--base64-limit [BASE64_LIMIT]]
                           [--hex-limit [HEX_LIMIT]]
                           [--disable-plugin DISABLE_PLUGIN]
                           [-n | --only-verified]
                           [--exclude-lines EXCLUDE_LINES]
                           [--exclude-files EXCLUDE_FILES]
                           [--exclude-secrets EXCLUDE_SECRETS]
                           [--word-list WORD_LIST_FILE] [-f FILTER]
                           [--disable-filter DISABLE_FILTER]
                           [path [path ...]]

Scans a repository for secrets in code. The generated output is compatible
with `detect-secrets-hook --baseline`.

positional arguments:
  path                  Scans the entire codebase and outputs a snapshot of
                        currently identified secrets.

optional arguments:
  -h, --help            show this help message and exit
  --string [STRING]     Scans an individual string, and displays configured
                        plugins' verdict.
  --only-allowlisted    Only scans the lines that are flagged with `allowlist
                        secret`. This helps verify that individual exceptions
                        are indeed non-secrets.

scan options:
  --all-files           Scan all files recursively (as compared to only
                        scanning git tracked files).
  --baseline FILENAME   If provided, will update existing baseline by
                        importing settings from it.
  --force-use-all-plugins
                        If a baseline is provided, detect-secrets will default
                        to loading the plugins specified by that baseline.
                        However, this may also mean it doesn't perform the
                        scan with the latest plugins. If this flag is
                        provided, it will always use the latest plugins
  --slim                Slim baselines are created with the intention of
                        minimizing differences between commits. However, they
                        are not compatible with the `audit` functionality, and
                        slim baselines will need to be remade to be audited.

plugin options:
  Configure settings for each secret scanning ruleset. By default, all
  plugins are enabled unless explicitly disabled.

  --list-all-plugins    Lists all plugins that will be used for the scan.
  -p PLUGIN, --plugin PLUGIN
                        Specify path to custom secret detector plugin.
  --base64-limit [BASE64_LIMIT]
                        Sets the entropy limit for high entropy strings. Value
                        must be between 0.0 and 8.0, defaults to 4.5.
  --hex-limit [HEX_LIMIT]
                        Sets the entropy limit for high entropy strings. Value
                        must be between 0.0 and 8.0, defaults to 3.0.
  --disable-plugin DISABLE_PLUGIN
                        Plugin class names to disable. e.g.
                        Base64HighEntropyString

filter options:
  Configure settings for filtering out secrets after they are flagged by the
  engine.

  -n, --no-verify       Disables additional verification of secrets via
                        network call.
  --only-verified       Only flags secrets that can be verified.
  --exclude-lines EXCLUDE_LINES
                        If lines match this regex, it will be ignored.
  --exclude-files EXCLUDE_FILES
                        If filenames match this regex, it will be ignored.
  --exclude-secrets EXCLUDE_SECRETS
                        If secrets match this regex, it will be ignored.
  --word-list WORD_LIST_FILE
                        Text file with a list of words, if a secret contains a
                        word in the list we ignore it.
  -f FILTER, --filter FILTER
                        Specify path to custom filter. May be a python module
                        path (e.g.
                        detect_secrets.filters.common.is_invalid_file) or a
                        local file path (e.g.
                        file://path/to/file.py::function_name).
  --disable-filter DISABLE_FILTER
                        Specify filter to disable. e.g.
                        detect_secrets.filters.common.is_invalid_file

阻止不在基线中的机密信息

$ detect-secrets-hook --help
用法:detect-secrets-hook [-h] [-v] [--version] [--baseline FILENAME]
                           [--list-all-plugins] [-p PLUGIN]
                           [--base64-limit [BASE64_LIMIT]]
                           [--hex-limit [HEX_LIMIT]]
                           [--disable-plugin DISABLE_PLUGIN]
                           [-n | --only-verified]
                           [--exclude-lines EXCLUDE_LINES]
                           [--exclude-files EXCLUDE_FILES]
                           [--exclude-secrets EXCLUDE_SECRETS]
                           [--word-list WORD_LIST_FILE] [-f FILTER]
                           [--disable-filter DISABLE_FILTER]
                           [filenames [filenames ...]]

位置参数:
  filenames             要检查的文件名。

可选参数:
  -h, --help            显示此帮助消息并退出
  -v, --verbose         详细模式。
  --version             显示版本信息。
  --json                将 detect-secrets-hook 输出打印为 JSON
  --baseline FILENAME   通过“detect-secrets scan”生成的基线明确忽略机密信息

插件选项:
  针对每个机密扫描规则集配置设置。默认情况下,除非明确禁用,否则将启用所有插件。

  --list-all-plugins    列出将用于扫描的所有插件。
  -p PLUGIN, --plugin PLUGIN
                        指定自定义机密检测器插件的路径。
  --base64-limit [BASE64_LIMIT]
                        设置高熵字符串的熵限制。值必须介于 0.0 和 8.0 之间,默认为 4.5。
  --hex-limit [HEX_LIMIT]
                        设置高熵字符串的熵限制。值必须介于 0.0 和 8.0 之间,默认为 3.0。
  --disable-plugin DISABLE_PLUGIN
                        要禁用的插件类名称。例如
                        Base64HighEntropyString

过滤器选项:
  针对引擎标记机密信息后过滤机密信息进行筛选的设置。

  -n, --no-verify       禁用通过网络调用的其他验证机制。
  --only-verified       仅标记可以验证的机密信息。
  --exclude-lines EXCLUDE_LINES
                        如果行与此正则表达式匹配,则将其忽略。
  --exclude-files EXCLUDE_FILES
                        如果文件名与此正则表达式匹配,则将其忽略。
  --exclude-secrets EXCLUDE_SECRETS
                        如果机密信息与此正则表达式匹配,则将其忽略。
  -f FILTER, --filter FILTER
                        指定自定义过滤器的路径。可能是 Python 模块路径(例如
                        detect_secrets.filters.common.is_invalid_file)或本地文件路径(例如
                        file://path/to/file.py::function_name)。
  --disable-filter DISABLE_FILTER
                        指定要禁用的过滤器。例如
                        detect_secrets.filters.common.is_invalid_file

我们建议将其设置为 pre-commit 钩子。一种方法是使用 pre-commit 框架:

# .pre-commit-config.yaml
repos:
-   repo: https://github.com/Yelp/detect-secrets
    rev: v1.4.0
    hooks:
    -   id: detect-secrets
        args: ['--baseline', '.secrets.baseline']
        exclude: package.lock.json

内联允许列表

有时,我们想要排除假阳性,而不创建基线来阻止提交。您可以通过添加以下注释来执行此操作:

secret = "hunter2"      # pragma: allowlist secret

//  pragma: allowlist nextline secret
const secret = "hunter2";

审核基线中的机密信息

$ detect-secrets audit --help
用法:detect-secrets audit [-h] [--diff] [--stats]
                      [--report] [--only-real | --only-false]
                      [--json]
                      filename [filename ...]

通过审核基线,分析员可以标记结果并针对其环境优化插件,以获得最高信噪比。

位置参数:
  filename      对给定的基线文件进行审核,以区分假阳性和真阳性之间的区别。

可选参数:
  -h, --help    显示此帮助消息并退出
  --diff        允许比较两个基线文件,以有效区分各种插件配置之间的差异。
  --stats       显示已保存到基线文件中的交互式审核会话的结果。
  --report      显示检测到的机密信息报告

报告:
  显示包含所有结果和所做决策的摘要。与报告模式(--report)一起使用。

  --only-real   报告中仅包括真实机密信息
  --only-false  报告中仅包括假阳性

分析:
  基于基线中标记的结果,量化插件的成功情况。与统计模式(--stats)一起使用。

  --json        以机器可读格式输出结果。

配置

此工具通过插件过滤器系统运作。- 插件:在代码中查找秘密

  • 过滤器:忽略误报以提高扫描精度

您可以根据需要调整这两者以满足您的精度/召回需求。

插件

我们采用了三种不同的策略来尝试在代码中查找秘密:

  1. 基于正则表达式的规则

    这是最常见的插件类型,对于结构良好的秘密效果很好。这些秘密可以选择性地验证,从而提高扫描精度。然而,仅依靠这些可能会对扫描的召回率产生负面影响。

  2. 熵检测器

    通过各种启发式方法搜索“看起来像秘密”的字符串。这对于非结构化的秘密很有用,但可能需要调整以调整扫描精度。

  3. 关键字检测器

    这忽略了秘密值,并搜索通常与分配硬编码值的秘密相关的变量名称。这对于“看起来不像秘密”的字符串(例如,le3tc0de密码)非常有用,但可能需要调整过滤器以调整扫描精度。

想要找到我们目前无法捕获的秘密吗?您也可以(轻松地)开发自己的插件,并与引擎一起使用!有关更多信息,请查看插件文档

过滤器

detect-secrets带有几个不同的内置过滤器,可能适合您的需求。

--exclude-lines

有时,您希望能够全局允许某些行在您的扫描中,如果它们匹配特定的模式。您可以指定一个正则表达式规则,如下所示:

$ detect-secrets scan --exclude-lines 'password = (blah|fake)'

或者您可以指定多个正则表达式规则,如下所示:

$ detect-secrets scan --exclude-lines 'password = blah' --exclude-lines 'password = fake'

--exclude-files

有时,您希望能够忽略扫描中的某些文件。您可以指定一个正则表达式模式来实现这一点,如果文件名符合此正则表达式模式,则不会被扫描:

$ detect-secrets scan --exclude-files '.*\.signature$'

或者您可以指定多个正则表达式模式,如下所示:

$ detect-secrets scan --exclude-files '.*\.signature$' --exclude-files '.*/i18n/.*'

--exclude-secrets

有时,您希望能够忽略扫描中的某些秘密值。您可以指定一个正则表达式规则,如下所示:

$ detect-secrets scan --exclude-secrets '(fakesecret|\${.*})'

或者您可以指定多个正则表达式规则,如下所示:

$ detect-secrets scan --exclude-secrets 'fakesecret' --exclude-secrets '\${.*})'

内联白名单

有时,您希望对特定行应用排除,而不是全局排除它。您可以使用内联白名单,如下所示:

API_KEY = 'this-will-ordinarily-be-detected-by-a-plugin'    # pragma: allowlist secret

这些注释支持多种语言。例如:

const GoogleCredentialPassword = "something-secret-here";     //  pragma: allowlist secret

您也可以使用:

# pragma: allowlist nextline secret
API_KEY = 'WillAlsoBeIgnored'

这可能是一种方便的方式,让您忽略秘密,而无需重新生成整个基线。如果您需要明确搜索这些允许列出的秘密,您也可以这样做:

$ detect-secrets scan --only-allowlisted

想要编写更多自定义逻辑以过滤误报?请查看我们的过滤器文档中如何进行此操作。

扩展

单词列表

--exclude-secrets标志允许您指定要排除的秘密值的正则表达式规则。但是,如果您想指定大量单词列表,可以使用--word-list标志。

要使用此功能,请确保安装了pyahocorasick包,或者只需使用:

$ pip install detect-secrets[word_list]

然后,您可以使用以下方式使用它:

$ cat wordlist.txt
not-a-real-secret
$ cat sample.ini
password = not-a-real-secret

# 会显示结果
$ detect-secrets scan sample.ini

# 找不到结果
$ detect-secrets scan --word-list wordlist.txt

无意义检测器

无意义检测器是一个简单的ML模型,试图确定秘密值是否实际上是无意义的,假设真正的秘密值不像单词一样。

要使用此功能,请确保安装了gibberish-detector包,或使用:

$ pip install detect-secrets[gibberish]

有关如何训练模型的更多信息,请查看gibberish-detector包。将包含一个预先训练的模型(通过处理RFC进行种子处理)以供轻松使用。

您还可以指定自己的模型,如下所示:

$ detect-secrets scan --gibberish-model custom.model

这不是默认插件,因为这将忽略诸如password之类的秘密。

注意事项

这并不意味着完全可以防止秘密进入代码库。只有适当的开发人员教育才能真正做到这一点。这个pre-commit hook只是实现了几种试图防止明显的提交秘密的启发式方法。

不会被阻止的事情:- 多行机密信息

  • 默认密码不会触发“KeywordDetector”(例如 login = "hunter2"

常见问题解答

通用

  • 即使我在git repo中,仍然遇到“未检测到git repository”警告。

    检查您的git版本是否 >= 1.8.5。如果不是,请升级它然后再试一次。 更多细节请参阅此处

Windows

  • 创建基线后,detect-secrets audit显示“不是有效的基线文件!”。

    确保您的基线文件的文件编码为UTF-8。 更多细节请参阅此处

标签:工具分享