Git 仓库过滤敏感信息,通过配置 clean/smudge 过滤器的方式


有没有遇到过这种困扰?本地配置文件里藏着数据库密码、密钥等敏感信息,一不小心就提交到 Git 仓库了,既不安全又容易泄露隐私。

今天就给大家分享 Git 仓库脱敏方法,重点讲在开发过程中最实用的「Git 内置过滤器」方案------不用改代码、不用手动改配置,提交自动脱敏、拉取自动还原,全程无感操作。

一、先搞懂核心需求:本地能用,仓库安全

目标很简单:

  • 本地开发时,能用到完整的配置(含敏感信息),不影响正常开发;
  • 提交到 Git 仓库时,敏感信息自动变成占位符,不会泄露;
  • 最好自动化,不用每次提交前手动改配置,省事。

💡提前划重点

下面 3 种方法里,优先选「方法 3:Git 内置过滤器」。前两种适合已开发完的项目,过滤器方案适合正在开发的项目,零侵入还省心。

3 种脱敏方法对比

方法 1:创建配置模板文件(简单但繁琐)

比如,有个配置文件叫 settings.yaml,里面有敏感信息。做法很简单:

  1. 复制一份 settings.yaml,改名叫 settings_example.yaml
  2. 手动把 settings_example.yaml 里的敏感信息删掉(比如把密码改成 xxxxxx),作为模板给团队参考;
  3. .gitignore 里加一行settings.yaml,让 Git 忽略这个真实配置文件,避免提交。

缺点:每次新增配置项,都要手动同步模板文件,团队协作时容易漏更。

方法 2:把敏感信息存到环境变量(脱离项目但需额外配置)

核心思路:把密码、密钥这些敏感信息,存到电脑的「环境变量」里(和项目分开),项目里只留"占位符"。

举个例子, settings.yaml 可以写成这样:

yaml 复制代码
database:
  host: ${DB_HOST}  # 占位符:本地环境变量里找 DB_HOST
  username: ${DB_USER}  # 占位符:本地环境变量里找 DB_USER
  password: ${DB_PASSWORD}  # 敏感密码:从环境变量读取
secret:
  token_key: ${TOKEN_SECRET}  # 密钥:从环境变量读取

缺点:每个开发者都要手动在自己电脑上配置环境变量,新成员加入时步骤多,容易配错。

方法 3:Git 内置过滤器(自动化首选)

这是 Git 自带的功能,相当于给配置文件加了"自动开关":

  • 提交代码时(git add):触发 clean 过滤器,自动把敏感信息换成占位符;
  • 拉取代码时(git pull):触发smudge 过滤器,自动把占位符换回本地的真实敏感信息。

优点:不用改项目代码、不用装额外工具,开发者完全不用管,提交/拉取全程自动处理,不影响开发效率。

二、自动化配置过滤器脚本(直接用)

手动配置过滤器步骤有点多,我写了个脚本,一键就能搞定所有配置。不想看手动步骤的朋友,直接用这个脚本就行!

当然,若读者对手动配置感兴趣,又或想了解脚本的工作原理,可跳过此部分,往后阅读:。

使用步骤(超简单)

  1. 打开你的 Git 项目根目录(就是和 .git 文件夹同级的目录);
  2. 新建一个文件,命名为 git_desensitize.sh
  3. 把下面的代码复制粘贴进去;
  4. 执行命令:bash git_desensitize.sh,等待脚本执行完成。

后续在其他地方拉取项目,只需把原项目的 .git/config 文件复制过去,删除要复原的「需脱敏文件」(如之前的 settings.yaml),再执行 git checkout-index --all --force 就能恢复本地配置。

bash 复制代码
#!/bin/bash
# ==============================================================
# Git 配置文件全自动脱敏脚本
# 使用说明:1.修改顶部【配置区】的files和fields 
#          2.执行脚本:bash git_desensitize.sh
# 核心特性:一键生成clean/smudge过滤器 + 绑定.gitattributes
# ==============================================================
​
# ========== 【唯一配置区】 ==========
# 要脱敏的文件列表
files=(
  "settings.yaml"
  # "src/config/.env"
  # "application.yml"
)
​
# 要脱敏的字段名列表
fields=(
  "password"
  "secret"
  "issuer"
  "addr"
  "host"
  "port"
  "ip"
  "domain"
  "send_email"
  "app_key"
  "access_key"
  "secret_key"
)
​
# ========== 【常量定义】 ==========
FILTER_NAME="auto_sensitive_filter"          # Git过滤器名称
GIT_ATTRIBUTES=".gitattributes"              # 绑定文件名称
BASE_PLACEHOLDER="******"                    # 占位符基础前缀
​
# ========== 【0.前置一键校验】 ==========
check_env(){
  [ ! -d ".git" ] && echo -e "\033[31m请在Git项目根目录执行!\033[0m" && exit 1
  [ ! $(command -v git) ] && echo -e "\033[31m未检测到Git环境!\033[0m" && exit 1
  [ ! $(command -v sed) ] && echo -e "\033[31m未检测到sed命令!\033[0m" && exit 1
} && check_env
​
# ========== 【1.扫描提取敏感值+去重+生成映射】 ==========
echo -e "\n\033[36m===== 扫描目标文件,提取敏感值 =====\033[0m"
declare -A FIELD_COUNTER                     # 字段计数器:记录每个字段有效序号
declare -a VALUE_MAPPING                     # 核心映射表:存储 原始值|字段名|序号
declare -A DUPLICATE_CHECK_MAP               # 去重校验MAP:key=字段名_原始值,实现双重去重
​
# 初始化字段计数器
for field in "${fields[@]}"; do
  FIELD_COUNTER[$field]=0
done
​
# 遍历所有目标文件
for file in "${files[@]}"; do
  if [ ! -f "$file" ]; then
    echo -e "\033[33m文件 $file 不存在,跳过\033[0m"
    continue
  fi
​
  # 遍历所有脱敏字段
  for field in "${fields[@]}"; do
    all_origin_values=$(sed -n "s/^[[:space:]]*${field}[[:space:]]*[:=][[:space:]]*["']*(.*)["']*[[:space:]]*$/\1/p" "$file")
    
    # 遍历所有提取到的原始值
    while IFS= read -r origin_value; do
      if [ -z "$origin_value" ]; then
        continue
      fi
      
      # 去重:字段名+原始值 作为KEY,完全一致则判定为重复
      unique_check_key="${field}_${origin_value}"
      if [[ -n "${DUPLICATE_CHECK_MAP[$unique_check_key]}" ]]; then
        echo -e "\033[33m重复:$file -> $field = $origin_value\033[0m"
        continue
      fi
​
      # 非重复数据:序号自增 + 标记去重KEY + 存入映射表
      ((FIELD_COUNTER[$field]++))
      seq_num=${FIELD_COUNTER[$field]}
      DUPLICATE_CHECK_MAP[$unique_check_key]=1
      VALUE_MAPPING+=("$origin_value|$field|$seq_num")
      echo -e "\033[32m提取:$file -> $field$seq_num = $origin_value\033[0m"
    done <<< "$all_origin_values"
  done
done
​
# 校验是否成功获取到原始值
if [ ${#VALUE_MAPPING[@]} -eq 0 ]; then
  echo -e "\033[31m错误:未扫描到任何有效字段值\033[0m"
  exit 1
fi
​
# ========== 【2.生成clean/smudge规则】 ==========
echo -e "\n\033[36m===== 生成clean/smudge规则 =====\033[0m"
CLEAN_RULE=""
SMUDGE_RULE=""
​
for item in "${VALUE_MAPPING[@]}"; do
  # 解析映射项:原始值|字段名|序号
  val=$(echo "$item" | cut -d'|' -f1)
  field=$(echo "$item" | cut -d'|' -f2)
  num=$(echo "$item" | cut -d'|' -f3)
  placeholder="${BASE_PLACEHOLDER}_${field}${num}"
  
  # 转义所有特殊字符
  val_esc=$(echo "$val" | sed 's/[/*$&|]/\&/g')
  ph_esc=$(echo "$placeholder" | sed 's/[/*$&|]/\&/g')
  
  # 生成sed规则:将原始值替换为占位符
  CLEAN_RULE+=" -e 's/${val_esc}/${ph_esc}/g'"
  SMUDGE_RULE+=" -e 's/${ph_esc}/${val_esc}/g'"
done
​
echo -e "\033[32m生成${#VALUE_MAPPING[@]}条\033[0m"
​
# ========== 【3.配置Git全局过滤器】 ==========
echo -e "\n\033[36m===== 配置Git全局过滤器 =====\033[0m"
# 发起配置命令
git config filter.$FILTER_NAME.clean "sed $CLEAN_RULE"
git config filter.$FILTER_NAME.smudge "sed $SMUDGE_RULE"
git config filter.$FILTER_NAME.required true
​
# 检查配置是否成功
filter_check=$(git config --get filter.$FILTER_NAME.clean)
smudge_check=$(git config --get filter.$FILTER_NAME.smudge)
required_check=$(git config --get filter.$FILTER_NAME.required)
if [ -n "$filter_check" ] && [ -n "$smudge_check" ] && [ "$required_check" = "true" ]; then
  echo -e "\033[32m配置成功\033[0m"
else
  echo -e "\033[31m过滤器配置失败,请手动执行脚本内命令\033[0m"
  exit 1
fi
​
# ========== 【4.绑定文件到.gitattributes】 ==========
echo -e "\n\033[36m===== 绑定文件过滤规则 =====\033[0m"
for file in "${files[@]}"; do
  if ! grep -q "^${file}[[:space:]]*filter=${FILTER_NAME}$" $GIT_ATTRIBUTES 2>/dev/null; then
    echo "${file} filter=${FILTER_NAME}" >> $GIT_ATTRIBUTES
    echo -e "\033[32m绑定:$file -> $FILTER_NAME\033[0m"
  else
    echo -e "\033[33m提示:$file 已存在过滤规则\033[0m"
  fi
done
​
git add $GIT_ATTRIBUTES
echo -e "\033[32m修改成功:$GIT_ATTRIBUTES\033[0m"
​
​
# ========== 【5.重新追踪应用】 ==========
echo -e "\n\033[36m===== 重新追踪文件 =====\033[0m"
for file in "${files[@]}"; do
  if [ -f "$file" ]; then
  if git diff --cached --quiet "$file" >/dev/null 2>&1; then
     git rm -r --cached "$file"
  fi
    git add "$file"
  fi
done
​
echo -e "\033[32m完成\033[0m"
exit 0

三、想了解原理?手动配置步骤

如果想搞懂背后的原理,或者需要自定义配置,可以跟着下面的步骤手动操作。

其实核心就 3 步:配置过滤器、绑定文件、验证效果。

第一步:配置过滤器

推荐只让过滤器在当前项目生效(避免影响其他项目),操作很简单:

  1. 打开项目里的.git 文件夹(这个文件夹是隐藏的,需要显示隐藏文件);
  2. 找到 config 文件,用记事本或编辑器打开;
  3. 在文件末尾添加下面的内容:
ini 复制代码
[filter "sensitive_filter"]  # 过滤器名字,自己随便起,后面要用到
    clean = sed -e 's/123456/****_pwd1/g' -e 's/666123/****_pwd2/g'
    smudge = sed -e 's/****_pwd1/123456/g' -e 's/****_pwd2/666123/g'
    required = true

解释下这几行的意思(理解就行):

  • s/原内容/替换内容/g:sed 的替换语法,s 表示文本替换,g 表示全局匹配替换(出现多次全都替换);
  • clean = ...:提交时执行的命令,把真实密码(比如 123456)换成占位符(比如 ****_pwd1);
  • smudge = ...:拉取时执行的命令,把占位符(****_pwd1)换回真实值(123456);
  • -e:可以拼接多个替换规则,比如同时替换密码和密钥(支持正则表达式);

💡注意

.git/config 是本地文件,不会提交到远程仓库!

团队其他成员需要在自己的本地项目里,也配置这个文件(可以把你的 config 文件发给他们复制)。
👀细心的你有没有发现?

这样写配置,如果要脱敏的字段很多,那就要重复写很长的一串规则啊!

还是用我写的自动脚本吧~~!

补充做法

  1. 想让 git 全局项目都生效,那就要修改 Git 全局配置文件 .gitconfig;一般windows 的 git 全局配置文件位置为 C:\Users\你的用户名.gitconfig

  2. 也可以用命令行进行配置

    • 全局配置生效:

      python 复制代码
      # 配置 clean 过滤器
      git config --global filter.sensitive_filter.clean "sed -e 's/123456/****_pwd1/g' -e 's/666123/****_pwd2/g'"
      ​
      # 配置 smudge 过滤器
      git config --global filter.sensitive_filter.smudge "sed -e 's/****_pwd1/123456/g' -e 's/****_pwd2/666123/g'"
      ​
      # 强制生效:无过滤规则则拒绝提交/拉取
      git config --global filter.sensitive_filter.required true
      • filter.sensitive_filter.clean:此处第二段的 sensitive_filter 是自定义过滤器名称。
    • 仅当前项目配置生效:去掉参数 --global 即可。

第二步:创建 .gitattributes 文件(告诉Git哪些文件要过滤)

我们需要创建一个 .gitattributes 文件,告诉 Git"哪些文件需要用上面配置的过滤器"。这个文件要提交到仓库,让团队所有人都能用同样的规则。

  1. 在项目根目录新建文件,命名为 .gitattributes
  2. 写入下面的内容(根据你的配置文件调整):
ini 复制代码
# 格式:【需要过滤的文件】 filter=过滤器名称
​
# 单文件精准匹配(推荐,精准脱敏)
settings.yaml filter=sensitive_filter
​
# 通配符匹配(批量脱敏,适合同类型配置文件)
*.yml filter=sensitive_filter
​
# 排除不需要过滤的文件(可选)
README.md !filter
​
# 注意:过滤器名称 `sensitive_filter` 必须和步骤 1 中配置的完全一致(大小写敏感)

写完后,执行下面的命令提交这个文件:

sql 复制代码
git add .gitattributes
git commit -m "添加敏感文件过滤规则"

第三步:验证配置是否成功

查看已配置的过滤规则,执行以下命令查看:

csharp 复制代码
# 查看全局过滤器配置
git config --global --get-regexp filter
​
# 查看当前项目过滤器配置
git config --get-regexp filter
​
# 查看所有 Git 配置(含过滤器)
git config --list

第四步:使用验证

如果你的配置文件之前已经被 Git 追踪过(比如已经提交过),需要先把它从 Git 暂存区移除(不会删除本地文件):

bash 复制代码
git rm --cached "被脱敏文件的地址"

然后验证脱敏效果:

csharp 复制代码
# 将文件加入暂存区(触发 clean 脱敏)
git add "被脱敏文件的地址"
​
# 查看暂存区中文件的脱敏效果
git diff --cached "被脱敏文件的地址"
​
# 提交到本地仓库
git commit -m "feat: 提交脱敏后的配置文件"

补充:用正则实现"任意敏感值自动脱敏"

如果你的配置文件里敏感值经常变,不想每次都手动加替换规则,可以用正则表达式写 clean 规则,实现"只要是某个字段,就自动替换成占位符"。

比如这样配置过滤器:

ini 复制代码
[filter "sensitive_filter"]
    clean = sed -e 's/(password:\s*).*/\1******/g' -e 's/(secret:\s*).*/\2******/g' -e 's/(token:\s*).*/\1******/g'
    smudge = sed -e 's/password: 1******/password: my_db_pass_123/g' -e 's/secret: 2******/secret: my_jwt_7890/g'
    required = true

说明:这种方式适合敏感字段固定,但值经常变的场景,不过 smudge 规则还是要手动维护真实值,不如自动化脚本省心。

四、总结一下

如果是正在开发的项目,优先用「Git 内置过滤器」+「自动化脚本」,一键配置,全程自动脱敏/还原,不影响开发;如果是已开发完的项目,用模板文件或环境变量的方式更简单。

核心原则就是:敏感信息只留在本地,仓库里只存脱敏后的内容,既安全又省心!

相关推荐
千寻girling2 小时前
面试官 : “ 说一下 Vue 的 8 个生命周期钩子都做了什么 ? ”
前端·vue.js·面试
Heo2 小时前
Vue3 应用实例创建及页面渲染底层原理
前端·javascript·面试
C_心欲无痕2 小时前
nodejs - express:流行的 Web 应用框架
前端·node.js·express
sophie旭2 小时前
webpack异步加载原理梳理解构
前端·javascript·webpack
小小荧2 小时前
Vue 原生渲染真要来了?Lynx引擎首次跑通Vue
前端·javascript
千寻girling2 小时前
面试官 : ” 说一下 Vue 中的 setup 中的 props 和 context “
前端·vue.js·面试
KLW752 小时前
vue中 v-cloak指令
前端·javascript·vue.js
技术不打烊2 小时前
InnoDB 核心原理拆解:缓冲池、Redo Log、MVCC 的底层逻辑
后端·mysql
武子康2 小时前
大数据-201 决策树从分裂到剪枝:信息增益/增益率、连续变量与CART要点
大数据·后端·机器学习