技术栈:FreeBSD + Apache + PHP + MySQL + 系统 FTP (vsftpd/ftpd)。核心流程:用户网页注册 → PHP 写入 MySQL → 调用系统命令创建系统用户(即 FTP 账号)+ 创建家目录 + 设置权限 → 用户通过 FTP 客户端上传作品。
FreeBSD 无 useradd,使用 pw 命令:
# 创建用户,指定组、shell、无密码交互 (-h 0 表示从标准输入读取密码)
echo "userpass" | pw useradd username -g ftpuser -s /bin/ftponly -h 0
-g ftpuser:指定主组为预创建的 ftpuser 组。-s /bin/ftponly:指定受限 shell,禁止 SSH/Telnet 登录。-h 0:从标准输入读取密码(适合脚本自动化)。#!/bin/csh
# 文件:/bin/ftponly
/bin/cat << 'XX'
You can only use this username to login ftp server!
And you can not use it to telnet to this system!
XX
sleep 10
exit 1
赋予执行权限:chmod +x /bin/ftponly,并添加到 /etc/shells。
popen() 以管道方式执行命令,返回文件指针,支持读/写单向操作。结合 su root -c '...' 提权执行 pw、mkdir、chown。
<?php
// 简化版,生产环境需加输入验证、预处理、异常处理、日志记录
if (!empty($_POST['username']) && !empty($_POST['userpasswd'])) {
$username = escapeshellarg($_POST['username']); // 防命令注入
$userpasswd = escapeshellarg($_POST['userpasswd']);
$rootpasswd = 'YourRootPasswordHere'; // 建议从环境变量/配置文件读取
// 1. 创建系统用户 (FTP 账号)
$cmd = "su root -c 'echo {$userpasswd} | pw useradd {$username} -g ftpuser -s /bin/ftponly -h 0'";
$fp = popen($cmd, 'w');
fwrite($fp, $rootpasswd . "\n"); // su 需要输入 root 密码
pclose($fp);
// 2. 创建家目录
foreach (['', '/public_html'] as $sub) {
$cmd = "su root -c 'mkdir -p /home/{$username}{$sub}'";
$fp = popen($cmd, 'w');
fwrite($fp, $rootpasswd . "\n");
pclose($fp);
}
// 3. 修改目录所有者为该用户 (初始为 www)
foreach (['', '/public_html'] as $sub) {
$cmd = "su root -c 'chown {$username} /home/{$username}{$sub}'";
$fp = popen($cmd, 'w');
fwrite($fp, $rootpasswd . "\n");
pclose($fp);
}
echo "恭喜 {$username},您的 FTP 账号已创建成功!请使用 FTP 客户端登录,注意:无 Telnet 权限。";
} else {
?>
<?php } ?>
| 风险点 | 对策 |
|---|---|
| 命令注入 | 所有用户输入进 shell 命令必须用 escapeshellarg() 或 escapeshellcmd() 转义。 |
| Root 密码硬编码 | 从环境变量 getenv('ROOT_PASS')、专用配置文件(chmod 600)、密钥管理系统读取。 |
| Apache 权限不足 | Apache 运行用户需在 wheel 组才能 su root。创建用户 www 加入 wheel,修改 httpd.conf User www Group wheel,重启 Apache。 |
| 并发竞争/残留进程 | pclose() 必须调用;高并发改用任务队列 (Gearman/Redis+Worker) 异步处理。 |
| FTP 被动模式端口 | 防火墙开放 pasv_min_port-pasv_max_port,vsftpd 配置 pasv_address 为公网 IP。 |
| 磁盘配额 | 启用文件系统 quota 或 pw usermod -L 限制登录类资源。 |
pam_userdb 或 MySQL 认证,无需创建系统用户,PHP 只操作数据库,安全性高、性能好、无提权风险。本方案展示了 PHP 通过 popen() + su + pw 实现系统级 FTP 账号自动化管理的经典模式,适用于 FreeBSD 等类 Unix 系统。生产环境务必强化输入过滤、密钥管理、权限最小化,并优先考虑虚拟用户或对象存储等现代架构替代。