本文首发在 Freebuf。但好像排版没那么好看 233.。。。于是想博客也发一份对照看看,个人感觉还是我这里的排版好看点哈哈哈
前言
在做一些CTF的审计题或者复现漏洞时,会得到 Docker 环境。需要我们在 Docker环境中进行代码调试。在 PHP 代码审计中,没有好的调试环境(如 xdebug这类调试工具),单单靠 var_dump 和 debug_print_backtrace 来手撕代码,效率十分低下。而要配好一个调试环境有时也是挺多坑的。俗话说“搭建环境两小时,漏洞复现两分钟”。本文就来简单说说如何使用 PHPSTORM 调试 Docker项目。
在开始之前,需要简单了解下 Docker。至少要会最基本的用法。不过由于主要是讲 PHPSTORM 调试的。所以 Docker介绍不会讲太多。若想了解更多的话建议看手册。
Docker基本用法
首先要知道 Docker 最基本的两样东西:image 和 container
image 就是镜像,搭建一个 Docker 环境最基本的就是 image。可以简单理解为这就是创建虚拟机的那个镜像。
container 就是容器。即 依据 image,创建出来的虚拟环境。可以简单理解为就是运行着的虚拟机
Docker 的配置文件为 Dockerfile。该文件内容为构建 container 的命令。基本命令有:
FROM 设置 container 运行在哪个 image 上。必须写在 Dockerfile 的开头。可以在 Docker Hub 里找适合的 image。
RUN 在 image build 时执行命令。一般用于安装环境
CMD 在 container刚启动时执行命令。一般用于启动服务
COPY 将宿主机的文件拷贝到 container中
WORKDIR 设置工作目录。所有命令都会在这个目录的基础上进行工作
了解了上面的三个命令后,我们可以来简单写一个 Dockerfile了。
1 | 基础 image 为 php:7.3-apache |
写完后在 Dockerfile 的目录里运行
1 | docker build -t test/testmyphp . |
等命令跑完,输入以下命令即可看到创建好的 image
1 | docker image ls |
要想让 container 运行起来,使用如下命令。使用 -p
来指定映射端口,左边是宿舍机端口,右边是 container 端口
1 | docker run -p 81:80 test/testmyphp |
查看运行中的 container
1 | docker container ls |
进入 container 的 shell
1 | docker exec -it CONTAINER_ID值 bash |
至此,基本的 Docker 环境我们就建好了。接下来研究下如何让 PHPSTORM 调试 Docker 项目
调试方式一 - Docker挂载调试
这里做测试使用前文的 Dockerfile,需要按照实际情况进行配置添加。
1 | 基础 image 为 php:7.3-apache |
start.sh 内容
1 | 配置 Xdebug |
重建 image:
1 | 最好先删掉之前的再重建 |
重建完先不急着跑 container。先定位到我们的项目代码位置

使用 Docker 的 Bind mounts 技术。将宿主机的项目目录映射到 container 的网站目录下
1 | docker run -p 81:80 --mount type=bind,source=/home/xp/test_docker/test_program,target=/var/www/html test/testmyphp |
至此,调试环境所需要的服务就安装好了,接下来配置 PHPSTORM。
配置 PHPSTORM
直接使用 Open
打开项目
配置 目录映射
进入 File -> Settings -> Languages & Frameworks -> PHP -> Servers。配置目录映射
注意一定要把 Use path mappings 的勾勾上,才能配置目录映射
Absolute path on the server 是要手动打上服务器路径的

设置Xdebug端口
进入 File -> Settings -> Languages & Frameworks -> PHP -> Debug。设置 Xdebug Debug port 为 9003。和 php 配置一致。

配置一个 Run/Debug Configuration
点击 PHPSTORM 右上角的 *Add Configuration….*。进入配置面,新增一个 PHP Web Page。并进行如下简单的配置即可(改名字设url 路径)

启动 PHP Debug Listening
直接点击 PHPSTROM 右上角的 小电话 ,即可开启监听

验证
在 php文件上打上断点,点击 右上角 甲壳虫样式的 Debug按钮。即可成功断点

调试方式二 - ssh 隧道
这种方式不仅仅可以用在 Docker 环境 上,同样可以用在 远程服务器 上。缺点就是要在 container 里头安装许多额外服务。
配置Dockerfile
- 安装ssh服务。 PHPSTORM需要 ssh 来进行目录映射,不然无法成功 Debug
- 安装 xdebug。这是调试的基础扩展组件
- 设置 ssh 允许 root 登陆。毕竟只是个 docker 调试环境,就不弄那么麻烦了。当然如果是线上业务啥的当然要做好权限分配。
- 修改 root密码。毕竟不知道密码也无法连接
- 启动 ssh 服务
- 重启 apache 服务
这里做测试使用前文的 Dockerfile,需要按照实际情况进行配置添加。
修改前文的 Dockerfile。
1 | 基础 image 为 php:7.3-apache |
start.sh 内容
1 | !/bin/bash |
重建 image:
1 | 最好先删掉之前的再重建 |
启动 container:
1 | docker run -p 81:80 -p 2222:22 test/testmyphp |
至此,调试环境所需要的服务就安装好了,接下来配置 PHPSTORM。
配置 PHPSTORM
打开 PHPSTORM,新建一个 Project。选择 New Project from Existing Files

选择 Web server is on remote host。

一路 Next。走到配置 Remote Server

然后又是一路 Next 即可。
配置完成后,将能看到我们的项目。

此时是不能直接进行代码调试的。我们要做以下工作
- 配置 CLI Interpreter
- 配置目录映射
- 设置Xdebug端口
- 配置一个 Run/Debug Configuration
- 启动 PHP Debug Listening
ps:由于有些步骤是一样的,就直接拉上文的截图过来了。
配置 CLI Interpreter
进入 File -> Settings -> Languages & Frameworks -> PHP。设置 CLI Interpreter

新建一个 CLI Interpreter。选择 From Docker, Vagrant, VM, WSL,Remote….
这里我们可以填 SSH,也可以直接选择 Docker。我这里用的是 SSH

设置 PHP executable 路径。不知道可以进入 container 中使用 whereis php
进行搜索

配置 目录映射
进入 File -> Settings -> Languages & Frameworks -> PHP -> Servers。配置目录映射
注意一定要把 Use path mappings 的勾勾上,才能配置目录映射
Absolute path on the server 是要手动打上服务器路径的

设置Xdebug端口
进入 File -> Settings -> Languages & Frameworks -> PHP -> Debug。设置 Xdebug Debug port 为 9003。和 php 配置一致。

配置一个 Run/Debug Configuration
点击 PHPSTORM 右上角的 *Add Configuration….*。进入配置面,新增一个 PHP Web Page。并进行如下简单的配置即可(改名字设url 路径)

启动 PHP Debug Listening
直接点击 PHPSTROM 右上角的 小电话 ,即可开启监听

验证
在 php文件上打上断点,点击 右上角 甲壳虫样式的 Debug按钮。即可成功断点

扩展 - 远程服务器调试
这里可以扩展以一下,如果调试目标是远程服务器而不是本地 Docker,该如何配置?
其实和上面的步骤一样的,只是 Xdebug 监听的流量我们要做修改下。
简单说说 Xdebug 监听的原理:
当 php-xdebug 接收到带有 XDEBUG_SESSION_START 的请求时,将会把当前的 Debug信 息发送给 xdebug 中配置的 client_host 和 cilent_port。
在我们的 start.sh 启动文件中,设置如下:
1 | xdebug.client_host = host.docker.internal |
host.docker.internal
值在 Xdebug 中是自动将请求端的 ip 设置为 调试端,即自动将 Debug信息 发送给任何请求IP。
而 xdebug.client_port
的值是 Debug信息 被发送至的端口。
我们可以想下,在本地,xdebug端是能访问到到我们请求端的 ip 和 端口 的,因为都是同一局域网。
但若调试的是远程服务器,由于公网和内网的原因,公网服务器是无法发送 Debug信息 调试端的(除非调试端也在公网上。。)
所以我们需要将 xdebug.client_host
设置为 127.0.0.1
,让 xdebug 转发 Debug信息 到本地的 9003 端口中。
接着使用 ssh 隧道进行端口转发,把调试端监听的 9003 端口映射到 服务端的 9003 上。这样就能接收到 xdebug 转发的 Debug信息了。这一点使用 ssh 隧道是可以做到的。
我们来测试下,首先修改 php.ini。让 xdebug.client_host
值为 127.0.0.1

进行 ssh隧道端口转发
1 | ssh -N -R 远程IP:远程端口:127.0.0.1:9003 root@远程IP |
这样就能使用调试远程程序了。