「なぜか攻撃される」を防ぐ!Dockerマルチステージビルドで守る「泥棒の入る隙間がない家」の作り方
こんにちは!セキュリティの世界で「盾」を磨き続けているエンジニアです。
今日は、開発現場でよく聞く「マルチステージビルド」についてお話しします。「難しそうだな…」と身構える必要はありません。実はこれ、皆さんの身近にある「家の防犯」と同じくらいシンプルな考え方なんです。
この記事を読み終える頃には、なぜDockerイメージを小さく保つことが、最強のセキュリティ対策になるのか、その理由が腹落ちしているはずですよ。
—
「道具箱を置いて帰る」のは泥棒を招くのと同じ
まず、想像してみてください。あなたは家を建てました。でも、「家の中に、頑丈な金庫を開けるための電動ドリルや、窓ガラスを割るためのハンマーを置きっぱなし」にして、玄関の鍵をかけて出かけたとします。
泥棒がもし玄関をこじ開けたらどうなるでしょう? そう、家の中にある「道具」を使って、さらに奥深く、もっと大事なもの(金庫など)を壊しにくるはずです。
Dockerの世界でも同じことが起きています。
開発者がアプリケーションを作るために必要な「コンパイラ(プログラムを作る道具)」や「パッケージ管理ツール」、「デバッグ用のツール」を、動いている本番環境のイメージにそのまま残していませんか?
攻撃者は、あなたのサービスに侵入した瞬間、その「置きっぱなしの道具」を見つけて、それを使ってさらに攻撃をエスカレートさせるのです。これが「攻撃対象領域(アタックサーフェス)」が大きい状態です。
—
マルチステージビルド=「必要なものだけを持ち込む」引っ越し術
マルチステージビルドとは、簡単に言えば「作る場所(作業場)」と「住む場所(本番環境)」を明確に分ける手法のことです。
1. ビルドステージ(作業場): ここでプログラムをコンパイルしたり、ライブラリをダウンロードしたりします。作業が終わったら、ここにある「道具」はすべてゴミ箱行きです。
2. 実行ステージ(住む場所): ここには、プログラムが動くための「最小限の部品」だけをコピーしてきます。不要なツールは一切持ち込みません。
こうすれば、万が一泥棒(攻撃者)が入ってきても、家の中には「何も道具がない」状態なので、彼らは何もできずに立ち去るしかありませんよね。
—
実践!Dockerfileを書き換えてみよう
では、実際にどう書くのか見てみましょう。下の例は、Go言語のアプリケーションを作る場合の構成です。
— ステージ1: ビルド環境(ここを作業場と呼びます) —
FROM golang:1.21 AS builder
必要なライブラリをインストール
WORKDIR /app
COPY . .
プログラムをコンパイル(ここでコンパイラを使用!)
RUN go build -o my-app main.go
— ステージ2: 本番環境(ここを住む場所にします) —
非常に軽量なベースイメージを採用
FROM alpine:latest
WORKDIR /root/
ステージ1で作った「実行ファイルだけ」をコピー
COPY –from=builder /app/my-app .
不要なコンパイラやソースコードは一切含まれていません!
CMD [“./my-app”]
このコードのポイント
- `AS builder`: 作業場に名前をつけます。
- `COPY –from=builder`: ここが魔法の鍵です。作業場から「完成品(my-app)」だけを抜き取って、空っぽの綺麗な部屋(alpine)に運んでいます。
これだけで、完成したイメージの中に「誰でも使えるコンパイラ」や「開発用ライブラリ」が混入するのを防ぐことができます。
—
セキュリティは「面倒くさい」を「当たり前」に
「いちいち分けるのは面倒だな」と思うかもしれません。でも、一度この書き方に慣れてしまえば、それが「当たり前」になります。
セキュリティ対策は、特別な魔法を使うことではありません。「自分たちの家(サーバー)に、不用品を置かない」という、整理整頓の延長線上にあるものなのです。
今すぐできる一歩
まずは、今動かしているDockerイメージを一度確認してみてください。
- `docker run -it <イメージ名> sh` で中に入り、`ls` を打ってみてください。
- もしそこに `go` や `gcc`、`apt` といったコマンドがあったら、それが「攻撃者に貸し与えている武器」かもしれません。
一歩ずつ、一つずつ。こうして「隙のない環境」を作っていくことが、結果として最強のセキュリティエンジニアへの第一歩になります。
また次回の記事で、さらに深いセキュリティの盲点についてお話ししましょう。皆さんの開発ライフが、安全で快適なものになりますように!

コメント