Skip to main content

PHPアプリケーションを安全にDocker化する方法

著者
Headshot of James Olaogun

James Olaogun

feature docker secrets

2023年8月2日

0 分で読めます

編集者注:2023年8月10日

この記事は更新され、チュートリアルでより新しいバージョンのPHPを使用しています。

PHPアプリケーションを開発したものの、軽量で持ち運びやすく、安全性も保てる方法で、アプリケーションをサポートするインフラから切り離したいとします。ほかの開発者が環境全体を再構築せずに作業できるようにしたいでしょう。つまり、アプリケーションをコンテナ化し、依存関係とともにコンテナにパッケージ化して、さまざまな環境で簡単に共有できるようにします。

現在、最も広く使われているコンテナ化ソリューションはDockerです。2013年に初めて公開されたオープンソースプラットフォームです。以来、アプリケーションの分離、再利用可能なCI/CDコンポーネントの作成、迅速なデプロイを可能にするテクノロジーとして、ソフトウェア開発で広く採用されてきました。

このチュートリアルでは、PHPアプリケーションを安全にコンテナ化、つまり「Docker化」する方法を手順に沿って解説します。また、最新のアプリケーション向けセキュリティツールを提供する開発者向けセキュリティプラットフォーム、Snykを使って、脆弱性を検出し、自動で修正します。

前提条件と推奨事項

以下のチュートリアルを始める前に、次のツールが揃っていることを確認してください。

  • お使いのマシンにDockerがインストールされていること。PHPコンテナの実行と管理に使用します。

  • Snykの無料アカウント。Snykのセキュリティスキャンツールを使って、コンテナの脆弱性を検出し、修正します。

  • Snyk CLIのインストールと実行に必要なNPMまたはYarn。

  • 基本的なPHPアプリケーション。このチュートリアルでは、新規作成したLaravelプロジェクトを使用します。

これらの要件を満たしていることを確認したら、チュートリアルに進む前に、次のヒントも参考にしてください。PHPアプリケーションをスムーズかつ安全にDocker化するための推奨事項です。

特定のイメージバージョンをベースにビルドする

アプリケーションのビルドには、必ずDockerイメージの特定のバージョンを使用してください。たとえば、PHP-FPMを取得する代わりに、イメージのバージョンを指定してPHP:8.0-fpmを取得できます。使用するベースイメージは、アプリケーションのバージョン要件によって異なる点に注意してください

そうすることで、既知のテスト済みで安定したリリースをベースにアプリケーションを構築できます。また、異なる環境でもイメージが同じ基盤プラットフォームと依存関係を使用できるため、再現性と一貫性が向上します。

リモートの依存関係にADDを使わない

ADDコマンドは任意のURLからファイルをダウンロードできるため、Docker化したアプリケーションにセキュリティ上の脆弱性を持ち込むおそれがあります。そのため、リモートの依存関係を追加する目的でDockerfileに`ADD`コマンドを使うことは避けることをおすすめします。

代わりにCOPYコマンドを使って、ローカルファイルシステムからファイルをコピーしてください。COPYコマンドを使えば、アプリケーションに必要なファイルだけがDockerイメージに含まれます。これも脆弱性のリスク低減につながります。

docker-compose YAMLファイルを使う

Docker Composeを使うと、YAMLファイルでDockerアプリケーションを定義し、アプリケーションのビルド方法や実行方法を指定して、Dockerコンテナを管理・オーケストレーションできます。Docker Composeを使うメリットには、次のようなものがあります。

  • アプリケーションのコンポーネントを分離。Docker Composeを使うと、アプリケーションのコンポーネントを別々のコンテナに分離できます。各コンテナの攻撃対象領域を限定し、単一障害点のリスクを軽減できます。

  • 環境変数を使用。Docker Composeでは環境変数を使ってアプリケーションを設定できるため、データベースのパスワードなどの機密情報を安全に管理できます。

  • ネットワーク接続を管理。Docker Composeでは、コンテナ間のネットワーク接続を管理できます。これにより、コンテナ同士が安全なチャネル経由でのみ通信するようにできます。

Windows、Mac、またはLinux向けにDocker Desktopをインストールしていれば、Docker Composeもすでに利用できます。

mod_phpではなくPHP-FPMを使う

PHP-FPMとmod_phpは、どちらもWebサーバー上でPHPアプリケーションを実行する方法ですが、仕組みには違いがあります。mod_phpはApache WebサーバーがPHPファイルを処理できるようにするApacheモジュールです。一方、PHP-FPMは、NGINXやApacheなど、さまざまなWebサーバーと連携するPHPプロセスマネージャーです。

PHP-FPMは、特にトラフィックの多い環境において、PHPアプリケーションをより効率的かつスケーラブルに実行できる方法と一般に考えられています。

マルチステージビルドを使う

マルチステージビルドは、Dockerでより小さく安全なイメージを作成するための手法です。Dockerfile内にそれぞれ独自の手順を持つ複数のステージを作成すれば、変更のたびにイメージ全体を最初から再構築する必要がありません。その結果、Dockerのビルドプロセスの効率と速度が向上します。

Docker化したアプリケーションのセキュリティを必ずテストする

アプリケーションの安全性を確保し、向上させるには、定期的なセキュリティテストが欠かせません。Dockerはアプリケーションを実行するための隔離環境を提供しますが、それだけでアプリケーションの安全性が保証されるわけではありません。攻撃者に悪用される前に潜在的な脆弱性を特定して対処するため、定期的にセキュリティチェックを実施してください。

PHPアプリケーションを安全にDocker化する5つのステップ

それでは実践編に進みましょう。ここからは、先ほどのヒントも取り入れながら、Laravel PHPアプリケーションを安全にDocker化する5つのステップを解説します。

1. Apacheを使ってDockerfileを設定する

まだプロジェクトがない場合は新しいLaravelプロジェクトを作成し、cdコマンドで新しいLaravelプロジェクトのディレクトリに移動します。たとえば、次のコマンドでは{{laravel-application-directory}}をLaravelアプリケーションのディレクトリ名に置き換えてください。

cd {{laravel-application-directory}} 

Laravelアプリケーションのルートディレクトリに、Dockerfileという名前の新しいファイルを作成します。

touch Dockerfile

作成したDockerfileに、次のコードをコピーして貼り付けます。

FROM composer:latest as composer
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --prefer-dist --no-scripts --no-dev --no-autoloader && \
    composer clear-cache
COPY . .
RUN composer dump-autoload --no-scripts --no-dev --optimize

# Depending on the composer you use, you may be required to use a different php version.
FROM php:8.2-apache
ENV APACHE_DOCUMENT_ROOT=/var/www/html/public

# 1. Install production packages and clean up apt cache.
RUN apt-get update && apt-get install -y \
    libbz2-dev \
    libfreetype6-dev \
    libicu-dev \
    libjpeg-dev \
    libmcrypt-dev \
    libpng-dev \
    libonig-dev \
    libzip-dev \
    libreadline-dev \
    sudo \
    zip \
 && rm -rf /var/lib/apt/lists/*

# 2. Apache configs + document root.
RUN echo "ServerName laravel-app.local" >> /etc/apache2/apache2.conf
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf
RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf

# 3. mod_rewrite for URL rewrite and mod_headers for .htaccess extra headers like Access-Control-Allow-Origin-
RUN a2enmod rewrite headers

# 4. Start with base PHP config, then add extensions.
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

RUN docker-php-ext-install \
    bcmath \
    bz2 \
    calendar \
    iconv \
    intl \
    mbstring \
    opcache \
    pdo_mysql \
    zip

# 5. Copy the application code from the composer stage.
COPY --from=composer /app /var/www/html

# 6. Create a non-root user to run the application.
ARG uid
RUN useradd -G www-data,root -u $uid -d /home/devuser devuser
RUN mkdir -p /home/devuser/.composer && \
    chown -R devuser:devuser /home/devuser

USER devuser
RUN echo "export PATH=$PATH:/var/www/html/vendor/bin" >> /home/devuser/.bashrc

前述のとおり、スケーラブルなソリューションを構築するにはPHP-FPMのほうが適していますが、このチュートリアルではアプリケーションが小規模なためmod_phpを使用します。

また、上記のDockerfileではマルチステージビルドを使用しています。最初のステージでは公式Composerイメージを使って、アプリケーションの依存関係をインストールし、オートローダーをビルドします。2番目のステージでは公式PHP/Apacheイメージを使ってアプリケーションを実行します。Composerのビルドファイルをローカルファイルシステムからコピーするには、COPYコマンドを使い、ADDコマンドを使わない点に注意してください。

Laravelアプリケーションのルートディレクトリに、docker-compose.ymlという名前の新しいファイルを作成します。

Touch docker-compose.yml

作成したdocker-compose.ymlに、次のコードをコピーして貼り付けます。

version: '3'

services:
  snykdemoapp:
    build:
      context: '.'
      args:
        uid: 1000
    container_name: snykdemoapp
    image: snykdemoapp
    ports:
      - 8000:80
    environment:
      - APACHE_RUN_USER=#1000
      - APACHE_RUN_GROUP=#1000
    volumes:
      - .:/var/www/html
    networks:
      app-network:
        aliases:
          - snykdemoapp

networks:
  app-network:
    name: app-network

前述のコードでは、snykdemoappサービスがPHPアプリケーション用のコンテナです。また、networksセクションでは、Docker Composeが通信に使用するapp-networkを定義しています。

2. docker-compose.ymlにMySQLデータベースコンテナを設定する

docker-compose.ymlファイルにMySQLコンテナを追加しましょう。このコンテナがLaravelアプリケーションのデータベースをホストします。

ステップ1で作成したdocker-compose.ymlファイルに、次のコードを新しいサービスとして追加し、MySQLコンテナ用のサービスを作成します。

app_db:
    image: mysql:5.7
    container_name: app_db
    volumes:
      - ./run/var:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=securerootpassword
      - MYSQL_DATABASE={{database_name}} 
      - MYSQL_USER={{database_user}}
      - MYSQL_PASSWORD={{dbuser_password}}
    networks:
      app-network:
        aliases:
          - app_db

前述のコードでは、次のようになっています。

  • app_dbは、MySQLバージョン5.7のイメージを使って構築されるデータベースサービスの名前です。名前は自由に変更できますが、関連付けられている他のサービスでも変更してください。

  • {{database_name}}は、任意のデータベース名です。

  • {{database_user}}は、任意のデータベースユーザー名です。

  • {{dbuser_password}}は、データベースユーザーに設定する任意のパスワードです。

これで、Docker環境にMySQLコンテナを追加できました。

3. docker-compose.ymlにPHPMyAdminコンテナを設定する

次に、LaravelコンテナとMySQLコンテナに加えて、ローカル開発やテスト用のPHPMyAdminコンテナを追加しましょう。PHPMyAdminは、MySQLとMariaDBを管理するための無料のオープンソースツールです。データベースを管理するためのWebインターフェースを提供します。PHPMyAdminは開発環境のみで使用し、本番環境では使用しないことをおすすめします。

以下のコードをdocker-compose.ymlに新しいサービスとして追加し、PHPMyAdminコンテナ用のサービスを作成します。

phpmyadmin:
    image: phpmyadmin/phpmyadmin
    links:
      - app_db
    environment:
      PMA_HOST: app_db
      PMA_PORT: 3306
    ports:
      - '8080:80'
    networks:
      app-network:
        aliases:
          - phpmyadmin

前述のコードでは、phpmyadminはphpmyadmin/phpmyadminイメージを使って構築されるPHPMyAdminサービスの名前です。environmentセクションでは、PMA_HOST変数をapp_dbに設定しています。これは、同じdocker-compose.ymlファイル内のMySQLサービスの名前です。

これで、LaravelコンテナおよびMySQLコンテナと連携するPHPMyAdminコンテナを設定できました。PHPMyAdminイメージは開発目的でのみ使用してください。本番環境で使用すると、アプリケーションのセキュリティリスクになります。

4. DockerでLaravel PHPアプリケーションをビルドして実行する

Dockerfileとdocker-compose.ymlの設定ができたら、Dockerサービスを起動し、実行中であることを確認します。その後、次のコマンドでLaravel PHPアプリケーションをビルドして実行できます。

  • docker-compose build:Dockerイメージをビルドします。

  • docker-compose up -d:コンテナを起動します。

  • docker ps:コンテナのステータスを確認します。

上記のコマンドがすべて正常に実行されたら、Webブラウザーを開いてhttp://localhost:8000/にアクセスし、Laravelアプリケーションのトップページを表示します。

Documentation、Laracasts、Laravel Newsへのリンクと、活気あふれるエコシステムが表示されたLaravelのウェルカムページ。

http://localhost:8080/にアクセスすると、PHPAdminインターフェースも表示できます。docker-compose.ymlファイルで設定したMYSQL_USERとMYSQL_PASSWORDを使ってログインしてください。

英語が選択されたphpMyAdminのウェルカム画面。ログイン用のユーザー名とパスワードの入力欄が表示されています

5. SnykでPHPアプリケーションをテストして保護する

Dockerアプリケーションのビルドが完了し、ブラウザーからアプリケーションとPHPMyAdminの両方にアクセスできたら、次はDocker化したアプリケーションのセキュリティをテストします。このチュートリアルでは、Snykを使ってDocker化したアプリケーションをテストし、安全性を確保します。

ページに表示されている任意のSSOを使ってログインするか、新しいSnykアカウントを作成します。記事の「前提条件」セクションで説明したとおり、Snyk CLIをインストールし、お使いのマシンを認証してください。

脆弱性のスキャンと修正について説明するパネルの横に、GitHubとGoogleの選択肢が表示されたSnykの登録画面。

Snykの設定が完了したら、Snyk CLIを使ってDocker化したアプリケーションのセキュリティを評価します。Laravelアプリケーションのルートディレクトリで次のコマンドを実行してください。

snyk container test {{your-image}} –file=Dockerfile

前述のコマンドの{{your-image}}は、テストするイメージの名前です。`docker ps`コマンドを使うと、アプリケーション内のDockerイメージ一覧を取得できます。

このチュートリアルのsnykdemoappという名前のベースPHP 8.2イメージに対して前述のコマンドを実行すると、次の結果が得られます。

Snyk Containerのテストによる、PHP 8.2コンテナイメージのセキュリティ脆弱性の検出結果。

PHP 8.2は最新の長期サポート(LTS)バージョンです。このコンテナのベースイメージで69件のセキュリティ問題が見つかりましたが、すべて深刻度は低いものでした。

PHP 7.4など古いバージョンを本番アプリケーションで使用している場合、対処が必要な、より重大で緊急性の高いセキュリティ脆弱性が見つかるでしょう。そのような場合、脆弱性の件数が少ない代替イメージを提示するSnykが、さらに役立ちます。

snyk container testコマンドで確認できるのは、その時点での結果のみです。より効果的な方法は、container monitorコマンドを実行してコンテナをスキャンすることです。

snyk container monitor {{your-image}}

container monitorコマンドを実行すると、コンテナイメージのテスト情報がSnykに送信されます。Snykはその情報をダッシュボードに表示し、今後イメージで発生する可能性のある脆弱性を通知します。

container monitorを、このチュートリアルのsnykdemoappという名前のベースPHP 7.4イメージに対して実行します。

Dockerイメージのcomposer.lockマニフェストと113個の依存関係の一覧を表示したSnykのプロジェクトダッシュボード

PHPアプリケーションで検出された脆弱性を修正する方法

PHPコンテナのテストが完了したら、検出された脆弱性を修正する際に役立つポイントをいくつかご紹介します。

  • イメージを再ビルドして、上流のDockerベースイメージから最新のセキュリティ更新を取得しましょう。 これにより、セキュリティ更新がアプリケーションに確実に反映されます。再ビルド後、もう一度セキュリティスキャンを実行し、ベースイメージの最新のセキュリティ更新に伴って発生する可能性のある脆弱性を特定して修正します。

  • Snykが提供する脆弱性評価を確認してください。 このリストは、アプリケーションの依存関係に潜むセキュリティ上の問題を把握するうえで役立ちます。

  • 脆弱性はさまざまな要因から生じる可能性があることを念頭に置きましょう。 PHPランタイムや、ベースイメージにインストールされているツールやライブラリもその要因に含まれます。アプリケーションスタックのすべてのコンポーネントを定期的に更新・監視し、セキュリティの脆弱性を速やかに特定して対処することが重要です。

まとめ

いくつかのベストプラクティスに従うことで、脆弱性の少ないアプリケーションを開発できます。手順に沿ったチュートリアルを最後まで進めた方は、Snykセキュリティプラットフォームを使って脆弱性を検出・修正しながら、安全なDocker化PHPアプリケーションを開発できるはずです。

このチュートリアルで使用したDocker化Laravelアプリケーションのコードは、GitHubでご覧いただけます。

開発者ファーストのコンテナセキュリティ

Snykは、コンテナイメージとKubernetesワークロードの脆弱性を検出し、自動的に修正します。

続きを読む

feature customer snowflake
Article

開発初期からセキュリティを確保:Snowflake Cortex Code向けSnyk Studioインテグレーションを発表

Snyk StudioがSnowflake Cortex Codeと連携し、開発中にAI生成コード、依存関係、コンテナの脆弱性をスキャンします。

blog feature pypi spoof
Article

オープンソースのメンテナーに、Snyk AI Security Platformをすべて無料で提供

オープンソースのメンテナーは、実際の脆弱性レポートに追われ、優先順位付けや修正、修正版のリリースを迅速に進めるための支援を必要としています。SnykのSecure Developer Programでは、条件を満たすプロジェクトにSnyk AI Security Platformを無料で提供します。

feature insights announcement
Blog

Node-gypサプライチェーン侵害:binding.gypに潜む自己増殖型npmワーム

新たなnpmワームがbinding.gypを悪用し、インストール時にnode-gypを起動。ライフサイクルスクリプトを使わずに悪意あるパッケージからコードを実行します。認証情報を窃取し、GitHub上に潜伏し、メンテナー間で自己増殖します。