Security Risk: in CI jobs, the CI_JOB_TOKEN is stored in .git/config, shouldn't be
When a CI job clones a Git repository, the CI_JOB_TOKEN token for the job is stored within the project subdirectory, in .git/config.
This is because the job token is included inline in the remote URL, passed through in https://username:password@ format.
This can be seen with commands run from CI, eg from https://gitlab.com/andrewn/git-remote-show/-/blob/main/.gitlab-ci.yml
$ # show the remotes
$ git remote -v
origin https://gitlab-ci-token:SECRET_VALUE@gitlab.com/andrewn/git-remote-show.git (fetch)
origin https://gitlab-ci-token:SECRET_VALUE@gitlab.com/andrewn/git-remote-show.git (push)
$ # check for the git configuration
$ cat .git/config
...
[remote "origin"]
url = https://gitlab-ci-token:SECRET_VALUE@gitlab.com/andrewn/git-remote-show.git
fetch = +refs/heads/*:refs/remotes/origin/*This token is only temporary, so the risk of exfiltration of this token is low. However, the risk is non-zero and an attacker could setup a polling job to wait for fresh exfiltrations.
For example:
- An attacker searches for all projects with Dockerfiles containing
COPY . ...commands in them. Example search: https://gitlab.com/search?group_id=6543&scope=blobs&search=file:Dockerfile+COPY+.+. This command will copy the current project directory into a Container image. The image will include the.git/configfile, containing the temporaryCI_JOB_TOKENtoken. - The attacker looks for jobs which publish a docker image and continue running afterwards. For example, if the job performs some QA tests using the published images, it may run after the docker image is published.
- The attacker sets up a polling job waiting for new images to be published from the CI job.
When one is published, the image is fetched and the the temporary, but still working
CI_JOB_TOKENtoken is exploited.
Simple Workaround
Projects should add .git to their .dockerignore configurations.
However, this needs to be done on a per-project basis, and can easily be missed for a single project.
Additionally, other means of accidentally exfiltrating the .git directory would not be covered. This could include tarball creation, pages site publishing etc etc. All of these methods may accidentally exfiltrate the .git/config directory, containing the CI_JOB_TOKEN.
GitLab Global Workaround
One option (there may be better ones) would be to not write the token into the project directory.
For example, instead of embedding the CI_JOB_TOKEN directly in the remote URL, a git-credential helper could be used to reference the $CI_JOB_TOKEN environment variable.
Example adapted from https://git-scm.com/docs/gitcredentials
$ cat .git/config
...
; Git credentials are not stored directly in the file any longer:
[remote "origin"]
url = https://gitlab.com/andrewn/git-remote-show.git
fetch = +refs/heads/*:refs/remotes/origin/*
[credential "https://gitlab.com"]
username = gitlab-ci-token
helper = "!f() { test \"$1\" = get && echo \"password=$CI_JOB_TOKEN\"; }; f"