在 Nginx 与 PHP 共同运行的 Web 环境中,让 PHP 把业务数据以 JSON 格式持久化到文件,是很多接口和小型后台常用的做法。但这种看起来简单的写文件动作,如果忽略权限、路径校验和并发处理,就会变成服务器被篡改的入口。本文从实际部署角度,说明如何既完成写入又避免常见安全问题。

一、明确 Nginx 与 PHP 的权限边界
大多数 Nginx 搭配 PHP-FPM 的方案里,Nginx 进程和 PHP-FPM 进程往往使用不同系统用户。Nginx 一般用 www-data 或 nginx 用户运行,而 PHP-FPM 的子进程池也可以配置成同样的用户,或者单独的用户。写入 JSON 文件的权限,本质上取决于 PHP-FPM 进程用户对目标目录是否有写权限,而不是 Nginx 是否有写权限。
如果目录归属混乱,比如把存储目录设成 777 权限,任何本地用户或异常进程都能修改其中的 JSON 文件。更合理的做法是新建一个专用目录,例如 /var/www/storage/json,将其拥有者设为 PHP-FPM 的运行用户,权限设为 750 或 700。同时,在 php.ini 中通过 open_basedir 限制 PHP 只能访问指定前缀路径,防止脚本被诱导写出到系统关键位置。
二、避免路径拼接导致目录遍历
很多漏洞来源于直接用用户输入拼文件路径。例如用用户提交的 filename 参数拼接成写入地址,攻击者可传入 ../../etc/cron.d/evil 这类字符串,绕过预期目录。安全写入的第一步,是永远使用固定基目录,并对用户侧传入的文件标识做白名单过滤。
下面示例展示如何安全地构造目标路径:
<?php
$baseDir = '/var/www/storage/json/';
// 仅允许字母数字下划线作为文件标识
if (!preg_match('/^[a-zA-Z0-9_]+$/', $_POST['name'] ?? '')) {
die('invalid name');
}
$fileName = $baseDir . $_POST['name'] . '.json';
// 确认最终路径仍在基目录内
$realBase = realpath($baseDir);
if (strpos(realpath(dirname($fileName)), $realBase) !== 0) {
die('path traversal');
}
?>
上述代码先校验名称格式,再用 realpath 比对实际目录前缀,双重保险阻止遍历。即便后续逻辑变化,也不会把文件写到基目录之外。
三、数据校验与 JSON 编码
写入前必须对数据结构做清洗。若直接把 $_POST 数组交给 json_encode,可能包含意料之外的字段,或被插入构造好的嵌套结构影响前端解析。推荐先定义允许字段,再组装干净数组。
同时应注意 json_encode 的选项错误。默认情况下中文会被编码成 unicode,若希望可读可加 JSON_UNESCAPED_UNICODE。还要检查返回值是否为 false,避免把 false 当成字符串写入。
<?php
$allowed = ['uid' => 1, 'score' => 1];
$data = [];
foreach ($allowed as $k => $type) {
if (isset($_POST[$k])) {
$data[$k] = $type === 1 ? (int)$_POST[$k] : $_POST[$k];
}
}
$json = json_encode($data, JSON_UNESCAPED_UNICODE);
if ($json === false) {
die('encode error');
}
?>
经过类型强制转换和字段限制,生成的 JSON 内容可控,也不会因特殊字符产生解析异常。这一步是安全写入的内容层防护。
四、原子写入与并发控制
直接用 file_put_contents 写目标文件,在并发请求下可能读到写了一半的内容。更稳妥的方式是先写临时文件,再 rename 到正式路径。rename 在同类文件系统上是原子操作,外部读到的要么旧版要么新版。
配合 flock 还能避免多个 PHP 进程同时写同一文件。下面给出完整安全写入函数示例:
<?php
function safe_write_json($file, $json) {
$tmp = $file . '.' . uniqid() . '.tmp';
$fp = fopen($tmp, 'w');
if (!$fp) return false;
flock($fp, LOCK_EX);
fwrite($fp, $json);
fflush($fp);
flock($fp, LOCK_UN);
fclose($fp);
return rename($tmp, $file);
}
safe_write_json($fileName, $json);
?>
该函数先落盘临时文件并加独占锁,写完释放锁再原子替换。即使中途崩溃,也只会留下临时文件,不会影响原 JSON 可用性。定期清理残留 tmp 即可。
五、Nginx 层屏蔽外部访问
JSON 存储目录不应被浏览器直接下载,否则可能泄露业务数据。在 Nginx 配置中,对该 location 拒绝访问:
location /storage/json/ {
deny all;
return 403;
}
这样即使有人猜到文件路径,Nginx 也会直接返回 403。结合 PHP 侧基目录限制,形成双层的存储隔离。注意该配置要放在 server 块内并重新加载生效。
六、综合建议
总结来看,在 Nginx 服务器上用 PHP 安全写 JSON 文件,核心在于:固定且受限的目录、严格的输入与路径校验、干净的数据编码、原子且加锁的写入方式,以及 Nginx 对存储路径的拒绝访问。把这些环节串起来,就能在普通业务场景下规避绝大部分写文件相关的安全风险。
实际项目中还可把 JSON 写入封装成类,统一处理异常和日志。当业务增长后,若文件并发量变大,再考虑迁移到 SQLite 或 Redis,但上述安全原则依然适用。
NginxPHPJSON_file_write修改时间:2026-08-02 15:24:28