引言

这个问题一开始真的难倒我了。

我接到的需求是,如何防止提交的成员不是成员本身?

这个需求乍一看其实很无厘头,因为权限的关系,能够提交推送的人,必然是团队内部成员,防止的人就是内部人员。

换句话来说,这就是防自己人的。

我其实看到就很无语了,防自己人干啥?又不是什么特别大的公司,有什么必要呢?

但是理由也说得过去,是因为有可能把自己写的 bug,栽赃在团队里其他的成员身上,也……好像说的过去。

所以我就在考虑如何解决这个问题。

过程

解决这个问题其实很曲折。

Git Hook——pre-receive实现

我一开始是在想,如何通过服务端的 git hook来验证用户来源名字和邮箱,遂百度了一堆 git钩子的方式,都是用 shell来编写的。

大概思路是这样的:

  • 首先读取 pre - receive钩子传入的旧版本号(oldrev)、新版本号(newrev)和引用名称(refname)。
  • 然后,对于在这个版本区间内的每个提交,它获取提交者的用户名(%an)和邮箱地址(%ae)。
  • 最后,它检查如果提交者是 yuesha但邮箱不是 hw1312@qq.com,就报错并阻止提交。

这里我甚至想定义一个 allowed_users数组,来存放团队成员的名字和邮箱。

代码大概长这样:

#!/bin/bash

# 定义允许的用户名和邮箱地址组合数组
declare -A allowed_users=(
    ["yuesha"]="hw1312@qq.com"
)

while read oldrev newrev refname; do
    # 遍历每个提交
    for commit in $(git rev - list $oldrev..$newrev); do
        author_name=$(git show - s -- format='%an' $commit)
        author_email=$(git show - s -- format='%ae' $commit)
        if [[ -n ${allowed_users[$author_name]} && ${allowed_users[$author_name]}!= $author_email ]]; then
            echo "Error: 提交的用户名和邮箱地址不匹配。对于用户 $author_name,邮箱地址应为相应的指定邮箱。"
            exit 1
        fi
    done
done

但是这里有一个很大的问题,脚本当中有 git rev - list $oldrev..$newrev这一句

在新版的 git当中是会报错的——不存在git rev命令

我找了很多解决方案,包括换命令 git log啊,折腾了好久,但是依然解决不掉这个报错:

remote: hooks/pre-receive: line 8: conditional binary operator expected
remote: hooks/pre-receive: line 8: expected `)'
remote: hooks/pre-receive: line 8: syntax error near `"hw1312@qq.com")'
remote: hooks/pre-receive: line 8: `        if [[ ($author_name == "yuesha" && $
author_email!= "hw1312@qq.com") ||'

remote: hooks/pre-receive: line 8: conditional binary operator expected
remote: hooks/pre-receive: line 8: expected )' remote: hooks/pre-receive: line 8: syntax error near "hw1312@qq.com")'
remote: hooks/pre-receive: line 8: `        if [[ ($author_name == "yuesha" && $
author_email!= "hw1312@qq.com") ||'

remote: hooks/pre-receive: line 8: conditional binary operator expected
remote: hooks/pre-receive: line 8: expected )' remote: hooks/pre-receive: line 8: syntax error near "hw1312@qq.com")'
remote: hooks/pre-receive: line 8: `        if [[ ($author_name == "yuesha" && $
author_email!= "hw1312@qq.com") ||'

remote: hooks/pre-receive: line 8: conditional binary operator expected
remote: hooks/pre-receive: line 8: expected )' remote: hooks/pre-receive: line 8: syntax error near "hw1312@qq.com")'
remote: hooks/pre-receive: line 8: `        if [[ ($author_name == "yuesha" && $
author_email!= "hw1312@qq.com") ||'

gitolite管控

然后我就在想啊,我以前学到过一个 git的工具,gitolite

之前说这个软件是用来对用户进行权限管控的,是不是能解决我的问题呢?

下载……安装……

这个有一个叫做 admingit项目,clone下来之后,修改权限信息,传入成员公钥,就可以实现管控了。

但是这里我有点迷糊。

真的能实现管控嘛?

我试着创建了一个仓库,设置了权限之后,另一个目录把这个仓库项目拉下来,之后提交,依然是可以提交的!

我突然发现——

GIT是可以自己设置用户名和密码的!!!

这就很糟糕了,成员一样可以随意修改自己的用户名,然后提交在同一个项目当中(毕竟这个项目他肯定有公钥,是合作开发的)

这样我们从远端拉取下来的 git历史记录里,依然是那个“假”成员提交的信息。

所以,gitolite管控对于我这个需求也是没有意义的了……

AI问计

我在这个天工的模型AI里问了问,给了我一些答案,但是我还是摸不着头脑……

但是吧,其实AI在这个时候已经给我答案了,只不过我这时候还看不懂……

一头雾水……

如果有人故意冒充别人在Git中进行操作,这可能会导致代码库的安全性受到威胁。为了防止这种情况的发生,可以采取以下措施:

1. 使用强密码和多因素身份验证

确保每个用户都使用强密码,并启用多因素身份验证(MFA)。这增加了攻击者冒充他人的难度,因为他们不仅需要密码,还需要访问用户的其他身份验证因素,如手机验证码或硬件令牌。

2. 限制访问权限

在Git仓库中,根据用户的角色和职责,限制他们的访问权限。例如,只允许特定的开发人员或团队有权访问和修改生产环境相关的分支,从而减少潜在的安全风险。

3. 使用SSH密钥进行身份验证

使用SSH密钥对进行身份验证,而不是简单的用户名和密码。SSH密钥提供了更高的安全性,因为私钥存储在用户的本地机器上,攻击者很难获取。

4. 监控和审查提交历史

定期监控和审查Git仓库的提交历史,特别是对于敏感分支。如果发现异常的提交或未经授权的访问,应立即采取措施,如撤销访问权限或回滚到之前的安全版本。

5. 使用GPG签名

GPG签名可以用于验证提交的真实性。通过为每个提交添加GPG签名,可以确保提交的作者身份是真实的,并且代码在传输和存储过程中没有被篡改。

6. 实施IP限制

如果可能,实施IP限制,只允许来自特定IP地址或IP地址范围的访问。这可以防止攻击者从不受信任的网络环境中访问Git仓库。

7. 教育和培训

对开发团队进行安全意识培训,教育他们如何识别和防止冒充攻击。这包括不共享密码、不点击可疑链接以及如何识别钓鱼邮件等。

8. 定期备份和恢复

定期备份Git仓库,以防止数据丢失和恢复受损的仓库。确保备份存储在安全的位置,并采取适当的措施来保护备份数据的安全性。

9. 使用代码审查工具

使用代码审查工具,如Gerrit或Pull Requests,对提交的代码进行审查。这可以帮助发现潜在的安全问题和冒充行为。

10. 审计和日志

Git提供了详细的日志记录和审计功能,可以追踪每一次代码的修改和提交。开发人员可以查看日志,了解每个提交的细节,及时发现和解决代码安全问题。

通过上述措施,可以大大降低攻击者冒充他人在Git中进行恶意操作的风险。同时,保持系统和软件的更新,以及遵循最佳实践,也是确保Git仓库安全的重要步骤。

柳暗花明

就在我一筹莫展的时候,翻 git大佬,廖雪峰大佬的博客的时候,看到了一句话。

有办法可查是否是冒充的

有办法可查是否是冒充的

什么办法呢??

既然都说肯定有,那我就去看各种评论区,毕竟评论区的大佬才是真牛逼。

然后在下面看到了这个

灵感来源

灵感来源

点开了这个链接,看到这位大佬的文章,很优秀!

https://spencerwoo.com/blog/wait-this-is-not-my-commit

就很棒!

github冒充身份

github冒充身份

用这里提到的GPG签名,就可以完全解决我的问题了。

GPG签名方法

下载GPG

首先去到官网:https://gpg4win.org/index.html

GPGwin的官网

GPGwin的官网

找到Gpg4win的下载链接

下载

下载

像我这种穷鬼肯定是直接免费下载了啊

然后点击$0,点击Download就开始下载了

安装并生成你的OpenPGP证书

下载完成之后,安装其实就是很傻瓜式的了

安装

安装

一路默认值回车就行

然后会让你打开个应用客户端,叫Kleopatra(这下面是我创建的密钥,你没有很正常)

Kleopatra

Kleopatra

https://spencerwoo.com/blog/wait-this-is-not-my-commit

如果是这个大佬的文章步骤的话,我个人觉得还是很繁琐的(我试过一次,要敲命令),所以我一般用图形化的方式来创建密钥

新建步骤1

新建步骤1

首先,打开”文件“菜单,创建一个OpenPGP密钥对(就是所谓的GPG证书)

创建步骤

创建步骤

输入名字和邮箱地址,然后回车(这里可以设置密码,那样会更安全,我演示就不设置了)

高级设置

高级设置

这里的高级设置里可以处理一些证书加密方式啊,检查更新之类的,不用管,直接点OK就行

就生成了一个证书

创建成功

创建成功

怎么样,,是不是超简单

导出证书ID(GPG 私钥 ID)

这里的证书ID,其实就是上面图片弹框出来的这个,但没法复制,不要紧,下面教你怎么复制导出。

查看证书细节

查看证书细节

点开细节(注意,不是导出)

复制证书ID

复制证书ID

可以查看并且复制这里的证书指纹(大佬文章说的GPG 私钥 ID

git配置

接下来需要将这个编号作为配置值写入到git里面,当然,你可以写全局,也可以写入单个项目里

二者任选其一即可

以下是全局配置:

// 设置GPG私钥ID
git config --global user.signingkey 换成你的证书ID
// 自动给每个提交commit签名
git config --global commit.gpgsign true

以下是项目配置:

// 设置GPG私钥ID
git config user.signingkey 换成你的证书ID
// 自动给每个提交commit签名
git config commit.gpgsign true

配置好之后,可以查看是否配置成功

这个命令可以在你的项目目录下执行,这样,默认会取你的项目配置,没有的话,去取全局配置

// 获取证书ID
git config user.signingkey
// 获取自动签名开关
git config commit.gpgsign

尝试提交及查看签名

本地项目正常提交

提交的方法跟平时是一模一样的

git commit -m "提交信息"

不同的点在于,如果你的证书是设置了密钥,那么提交的时候会提示你输入密码

如果像我,不设置密码的话,就是在提交那的时间会长了一点点而已。

查看提交的签名信息

这里用--show-signature参数来展示提交日志列表的签名信息

git log --show-signature

提交里有GPG证书Id

提交里有GPG证书Id

如果你嫌弃这个命令比较麻烦的话,我推荐你设置一个别名,可以参考我这里的设置方法

git config --global alias.signLog 'log --show-signature'

当然,也可以查看单独一条的提交内容

git cat-file -p 提交Hash

可以看到,git已经将每次提交的内容签名之后,将签名串放到提交正文里了。

提交信息里的签名串

提交信息里的签名串

上传GitHub平台的GPG签名验证

我这里是以GitHub为例进行阐述,如果是GitLab或者其他代码托管平台,操作也是类似的。

导出公钥内容

首先,我们只有一个ID肯定是无法让GitHub识别出来是你自己的。

所以,我们需要将对称加密的公钥导出来

跟上面一样,找到你的证书,点击“细节”

查看证书细节

查看证书细节

然后在弹出来的窗口里,找到“导出”按钮

导出公钥

导出公钥

在弹出来的框里面,就可以复制这个公钥了。

公钥内容复制

公钥内容复制

一定要注意!

这里的整段都要复制,包括前面的:

“-----BEGIN PGP PUBLIC KEY BLOCK-----”

和结尾的:

“-----END PGP PUBLIC KEY BLOCK-----”

一点不能漏

打开GitHub上传公钥文件

找到你的个人中心

github后台添加GPGkey

github后台添加GPGkey

上面的SSH keys代表你通过ssh方式上传代码的公钥

我们要配置的是下面,GPG keys

点击这里的New GPG key按钮

添加保存进去

添加保存进去

标题可以自己随意编写,但是内容一定要整段复制,一点不能漏

需要密码验证

需要密码验证

然后需要输入密码

添加成功

添加成功

显示保存成功,列表也看到这个公钥了

小绿锁

在将之前内容进行提交版本库之后,需要推送到远端,就是我们的GitHub平台上

然后就可以看到小绿锁了

很安全

小绿锁

小绿锁

附录-bug及解决方法

1. 提交显示“Unverified”

提交显示未被验证

提交显示未被验证

如上图所示,显示未验证,这里主要是你GitHub登录的邮箱和签名邮箱不一致导致的

Unverified

Unverified

需要去到添加GPG Keys的位置,找到你的信息

Unverified_msg

Unverified_msg

点开,添加你的其他邮箱信息

输入你的邮箱

输入你的邮箱

然后发送邮箱验证码

邮箱收到的邮件,点击验证即可

验证邮件

验证邮件

2. Git找不到GPG的签名执行文件

如果是失败了

可以执行下面的命令:

这里的路径,代表GPG可执行文件的路径

需要包含有执行文件名,如“D:/GnuPG/bin/gpg.exe

git config --global gpg.program 路径

设置后,git可以找到相关可执行文件进行签名

适用于GPG程序没有默认安装在默认位置的情况


欢迎关注拓行公众号,分享各种技术博客文章

拓行——奋勇进取,开拓未来,砥砺前行

最后修改:2025 年 05 月 13 日
如果您对各种技术博客文章感兴趣,欢迎关注拓行公众号,分享各种专业技术知识~