diff --git a/.github/workflows/legacy-v5.yml b/.github/workflows/legacy-v5.yml index 66a3476..17470df 100644 --- a/.github/workflows/legacy-v5.yml +++ b/.github/workflows/legacy-v5.yml @@ -100,6 +100,10 @@ jobs: docker pull --quiet ghcr.io/${{ github.repository_owner }}/php:rc${GITHUB_RUN_ID}-${{ matrix.php_version }}-v5-slim-${{ matrix.variant }}-amd64 # Retrieve minor PHP_PATCH_MINOR=`docker run --rm ghcr.io/${{ github.repository_owner }}/php:rc${GITHUB_RUN_ID}-${{ matrix.php_version }}-v5-slim-${{ matrix.variant }}-amd64 php -v | head -n1 | grep -P '\d+\.\d+\.\d+' -o | head -n1` + # The php2shell backport keeps the version number of 7.4, 8.0 and 8.1: tag it apart from the existing X.Y.Z tags + if [[ " 7.4 8.0 8.1 " == *" ${{ matrix.php_version }} "* ]]; then + PHP_PATCH_MINOR="${PHP_PATCH_MINOR}-php2shell" + fi echo "PHP_PATCH_MINOR=${PHP_PATCH_MINOR}" echo "PHP_PATCH_MINOR=${PHP_PATCH_MINOR}" >> $GITHUB_ENV - name: Get list of images for this variant @@ -121,7 +125,7 @@ jobs: for TARGET in $TARGETS; do TAG_ORI=$(REPO="ghcr.io/${{ github.repository_owner }}/php" TAG_PREFIX="rc${GITHUB_RUN_ID}-" docker buildx bake ${TARGET} --progress=quiet --print | jq -r ".target | to_entries[] | select(.key == \"${TARGET}\") | .value.tags[0]") - TAG_PATH=$(REPO="php" PHP_PATCH_MINOR="${{ env.PHP_PATCH_MINOR }}" docker buildx bake ${TARGET} --progress=quiet --print | jq -r ".target | to_entries[] | select(.key == \"${TARGET}\") | .value.tags[0]") + TAG_PATH=$(REPO="php" IS_RELEASE="1" PHP_PATCH_MINOR="${{ env.PHP_PATCH_MINOR }}" docker buildx bake ${TARGET} --progress=quiet --print | jq -r ".target | to_entries[] | select(.key == \"${TARGET}\") | .value.tags[1]") TAG_MINOR=$(REPO="php" docker buildx bake ${TARGET} --progress=quiet --print | jq -r ".target | to_entries[] | select(.key == \"${TARGET}\") | .value.tags[0]") # Create manifest for rc tag (always) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index f902007..31ac139 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -134,7 +134,7 @@ jobs: for TARGET in $TARGETS; do TAG_ORI=$(REPO="ghcr.io/${{ github.repository_owner }}/php" TAG_PREFIX="rc${GITHUB_RUN_ID}-" docker buildx bake ${TARGET} --progress=quiet --print | jq -r ".target | to_entries[] | select(.key == \"${TARGET}\") | .value.tags[0]") - TAG_PATH=$(REPO="php" PHP_PATCH_MINOR="${{ env.PHP_PATCH_MINOR }}" docker buildx bake ${TARGET} --progress=quiet --print | jq -r ".target | to_entries[] | select(.key == \"${TARGET}\") | .value.tags[0]") + TAG_PATH=$(REPO="php" IS_RELEASE="1" PHP_PATCH_MINOR="${{ env.PHP_PATCH_MINOR }}" docker buildx bake ${TARGET} --progress=quiet --print | jq -r ".target | to_entries[] | select(.key == \"${TARGET}\") | .value.tags[1]") TAG_MINOR=$(REPO="php" docker buildx bake ${TARGET} --progress=quiet --print | jq -r ".target | to_entries[] | select(.key == \"${TARGET}\") | .value.tags[0]") # Create manifest for rc tag (always) diff --git a/CHANGELOG.md b/CHANGELOG.md index 298ea32..9b5d37f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ ### Minor changes +* **2026-10-23** + * Security: php2shell is fixed in the arm64 images too, now that the fixed PHP packages are published for arm64. The php2shell test fails again for a vulnerable image on every architecture + * The tags of the PHP patch versions (e.g. `8.5.12-v5-cli`) are published again: they were no longer published since 2025-12-10. The `X.Y-v5-*` tags are not affected + * PHP 7.4, 8.0 and 8.1: the images fixed against php2shell are tagged `7.4.33-php2shell-v5-*`, `8.0.30-php2shell-v5-*` and `8.1.34-php2shell-v5-*`, the existing `7.4.33-v5-*`, `8.0.30-v5-*` tags being kept unchanged + * **2026-10-21** * Security: fix of php2shell (CVE-2026-109618 and CVE-2026-109619, CVSS 9.2) in all the v5 amd64 images. PHP 7.4, 8.0 and 8.1 images are rebuilt with the backport of the fix (same PHP version: 7.4.33, 8.0.30, 8.1.34). The arm64 images will be fixed when the fixed PHP packages are published for arm64. See [php2shell](docs/security-php2shell.md) * NodeJS 18 and 20 variants are built again for all the PHP versions, by the legacy workflow diff --git a/README.md b/README.md index 794ce11..a6c810c 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,10 @@ # General purpose PHP images for Docker -> **Security: php2shell (CVE-2026-109618 and CVE-2026-109619, CVSS 9.2) is fixed in all the v5 amd64 images** +> **Security: php2shell (CVE-2026-109618 and CVE-2026-109619, CVSS 9.2) is fixed in all the v5 images** > -> Pull the images again, recreate your containers and rebuild the images built from ours. The **arm64** images -> (Apple Silicon, AWS Graviton...) are **not fixed yet**: the fixed PHP packages for arm64 are still being built -> upstream, and the images will be updated as soon as they are published. The v4 images are **not** fixed: migrate to +> Pull the images again, recreate your containers and rebuild the images built from ours. **Update:** the arm64 images, +> first published without the fix, are now fixed too: pull them again. The v4 images are **not** fixed: migrate to > v5. See [what to do](docs/security-php2shell.md). This repository contains a set of developer-friendly, general purpose PHP images for Docker. diff --git a/docs/security-php2shell.md b/docs/security-php2shell.md index e5aafce..8a00f5b 100644 --- a/docs/security-php2shell.md +++ b/docs/security-php2shell.md @@ -3,8 +3,11 @@ Two critical vulnerabilities of PHP (CVSS 9.2), disclosed on 2026-10-21, allow remote code execution without authentication. Exploits are public: update now. -All the v5 images are fixed on amd64, from PHP 7.4 to 8.5, with or without NodeJS (`-node18` to `-node26`). The -arm64 images are not fixed yet: see [arm64 images](#arm64-images). +All the v5 images are fixed, from PHP 7.4 to 8.5, on amd64 and arm64, with or without NodeJS (`-node18` to `-node26`). + +> **Update: the arm64 images are now fixed too.** They were first published without the fix, the fixed PHP packages +> being published later for arm64. On arm64 (Apple Silicon, AWS Graviton...), pull the images again and recreate your +> containers, even if you already did it after 2026-10-21. ## What to do @@ -22,17 +25,14 @@ arm64 images are not fixed yet: see [arm64 images](#arm64-images). In your CI, check that the base image is pulled again and not taken from a cache. -3. Use the `X.Y-v5-*` tags (e.g. `8.4-v5-apache`). The tags of older PHP patch versions (e.g. `8.4.26-v5-apache`) are - not fixed. +3. Use the `X.Y-v5-*` tags (e.g. `8.4-v5-apache`), or the tags of the fixed PHP patch versions (e.g. + `8.4.27-v5-apache`). The tags of older PHP patch versions (e.g. `8.4.26-v5-apache`) are not fixed. PHP 7.4, 8.0 and 8.1 images still show 7.4.33, 8.0.30 and 8.1.34 in `php -v`: the fix does not change the version. +Their existing patch version tags (e.g. `7.4.33-v5-apache`) are kept unchanged and are **not** fixed: the fixed images +are tagged `7.4.33-php2shell-v5-*`, `8.0.30-php2shell-v5-*` and `8.1.34-php2shell-v5-*`. -## arm64 images - -The images install PHP from the packages of [packages.sury.org](https://packages.sury.org/php/), which publishes the -fixed packages for amd64 first: the arm64 packages are still being built. Until they are published, all the arm64 -images stay vulnerable, including those of the tags of the fixed PHP patch versions (on arm64, `8.4.27-v5-apache` -still contains PHP 8.4.26). The arm64 images will be rebuilt as soon as the packages are available. +## Check an image Check the PHP version of your image: 8.2.35, 8.3.36, 8.4.27, 8.5.12 or newer are fixed. PHP 7.4, 8.0 and 8.1 keep their version number, and are fixed when the PHP binary contains the fix: @@ -43,9 +43,6 @@ $ docker run --rm thecodingmachine/php:8.1-v5-cli bash -c 'grep -c zend_mm_safe_ `0` means that the image is vulnerable. -In the meantime, an arm64 host can run the amd64 image through emulation (`--platform linux/amd64`, or -`platform: linux/amd64` in a Compose file), which is much slower. - ## NodeJS 16 and older The v5 images are not built with NodeJS 16 and older. Use a maintained NodeJS version (22, 24 or 26) when possible. diff --git a/tests-suite/php-security-patches.sh b/tests-suite/php-security-patches.sh index 8c38fdc..adbf838 100755 --- a/tests-suite/php-security-patches.sh +++ b/tests-suite/php-security-patches.sh @@ -8,20 +8,14 @@ # The end-of-life branches (7.4, 8.0, 8.1) only got a source backport that keeps the # version number (7.4.33, 8.0.30, 8.1.34): it is detected with the zend_mm_safe_error_ex # symbol it adds to the Zend engine, in every PHP SAPI shipped in the image. -# The fixed packages are not yet published for arm64: a vulnerable arm64 image is only a warning. test_php2shellPatched() { - IMAGE="${REPO}:${TAG_PREFIX}${PHP_VERSION}-${BRANCH}-${BRANCH_VARIANT}${ARCH_SUFFIX}" if [[ "$(printf '%s\n' "$PHP_VERSION" "8.2" | sort -V | head -n 1)" != "8.2" ]]; then - RESULT="$(docker run ${RUN_OPTIONS} --rm "${IMAGE}" \ - bash -c 'dpkg --print-architecture - for BIN in /usr/bin/php${PHP_VERSION} /usr/sbin/php-fpm${PHP_VERSION} /usr/lib/apache2/modules/libphp${PHP_VERSION}.so; do + RESULT="$(docker run ${RUN_OPTIONS} --rm \ + "${REPO}:${TAG_PREFIX}${PHP_VERSION}-${BRANCH}-${BRANCH_VARIANT}${ARCH_SUFFIX}" \ + bash -c 'for BIN in /usr/bin/php${PHP_VERSION} /usr/sbin/php-fpm${PHP_VERSION} /usr/lib/apache2/modules/libphp${PHP_VERSION}.so; do [ -e "${BIN}" ] || continue grep -q zend_mm_safe_error_ex "${BIN}" && echo "patched ${BIN}" || echo "vulnerable ${BIN}" done' 2>/dev/null)" - if [[ "$RESULT" == arm64* ]] && [[ "$RESULT" == *vulnerable* ]]; then - echo "::warning::${IMAGE} (arm64) is vulnerable to CVE-2026-109618: PHP SAPI without the backport" - return - fi assert_matches "patched /usr/bin/php${PHP_VERSION}" "$RESULT" "PHP CLI not found or not patched" assert_not_matches "vulnerable" "$RESULT" "PHP SAPI without the CVE-2026-109618 backport" else @@ -31,15 +25,10 @@ test_php2shellPatched() { 8.4) FIXED_VERSION="8.4.27" ;; *) FIXED_VERSION="8.5.12" ;; esac - RESULT="$(docker run ${RUN_OPTIONS} --rm "${IMAGE}" \ - bash -c 'dpkg --print-architecture; php -r "echo PHP_VERSION;"' 2>/dev/null)" - ARCH="$(head -n 1 <<< "$RESULT")" - VERSION="$(tail -n 1 <<< "$RESULT")" - if [[ "$ARCH" == "arm64" ]] && [[ "$(printf '%s\n' "$VERSION" "${FIXED_VERSION}" | sort -V | head -n 1)" != "${FIXED_VERSION}" ]]; then - echo "::warning::${IMAGE} (arm64): PHP ${VERSION} is vulnerable to CVE-2026-109618 (fixed in ${FIXED_VERSION})" - return - fi - assert_equals "${FIXED_VERSION}" "$(printf '%s\n' "$VERSION" "${FIXED_VERSION}" | sort -V | head -n 1)" \ - "PHP ${VERSION} is vulnerable to CVE-2026-109618 (fixed in ${FIXED_VERSION})" + RESULT="$(docker run ${RUN_OPTIONS} --rm \ + "${REPO}:${TAG_PREFIX}${PHP_VERSION}-${BRANCH}-${BRANCH_VARIANT}${ARCH_SUFFIX}" \ + php -r 'echo PHP_VERSION;' 2>/dev/null)" + assert_equals "${FIXED_VERSION}" "$(printf '%s\n' "$RESULT" "${FIXED_VERSION}" | sort -V | head -n 1)" \ + "PHP ${RESULT} is vulnerable to CVE-2026-109618 (fixed in ${FIXED_VERSION})" fi } diff --git a/utils/README.blueprint.md b/utils/README.blueprint.md index e9e175f..4f8cab0 100644 --- a/utils/README.blueprint.md +++ b/utils/README.blueprint.md @@ -2,11 +2,10 @@ # General purpose PHP images for Docker -> **Security: php2shell (CVE-2026-109618 and CVE-2026-109619, CVSS 9.2) is fixed in all the v5 amd64 images** +> **Security: php2shell (CVE-2026-109618 and CVE-2026-109619, CVSS 9.2) is fixed in all the v5 images** > -> Pull the images again, recreate your containers and rebuild the images built from ours. The **arm64** images -> (Apple Silicon, AWS Graviton...) are **not fixed yet**: the fixed PHP packages for arm64 are still being built -> upstream, and the images will be updated as soon as they are published. The v4 images are **not** fixed: migrate to +> Pull the images again, recreate your containers and rebuild the images built from ours. **Update:** the arm64 images, +> first published without the fix, are now fixed too: pull them again. The v4 images are **not** fixed: migrate to > v5. See [what to do](docs/security-php2shell.md). This repository contains a set of developer-friendly, general purpose PHP images for Docker.