本文目录导读:

在 PHP 中实现 Seccomp(安全计算模式)主要有以下几种方法:
使用 PHP 扩展
libseccomp 扩展
<?php
// 检查扩展是否可用
if (!extension_loaded('seccomp')) {
echo "seccomp 扩展未加载\n";
exit(1);
}
// 创建 seccomp 过滤器
$filter = seccomp_init(SCMP_ACT_ALLOW); // 默认允许所有
// 禁止特定系统调用
seccomp_rule_add($filter, SCMP_ACT_KILL, SCMP_SYS(execve), 0);
seccomp_rule_add($filter, SCMP_ACT_ERRNO, SCMP_SYS(open), 0, EACCES);
// 加载过滤器
seccomp_load($filter);
// 清理
seccomp_release($filter);
echo "Seccomp 过滤器已应用\n";
通过 FFI(Foreign Function Interface)
PHP 8.0+ 可以使用 FFI 直接调用 C 库:
<?php
// 使用 FFI 调用 libseccomp
$ffi = FFI::cdef("
typedef struct scmp_filter_ctx *scmp_filter_ctx;
scmp_filter_ctx seccomp_init(uint32_t def_action);
int seccomp_rule_add(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, ...);
int seccomp_load(scmp_filter_ctx ctx);
void seccomp_release(scmp_filter_ctx ctx);
#define SCMP_ACT_ALLOW 0x7fff0000U
#define SCMP_ACT_KILL 0x00000000U
", "libseccomp.so");
// 初始化过滤器
$ctx = $ffi->seccomp_init($ffi->SCMP_ACT_ALLOW);
// 禁止 execve 系统调用
$SYS_execve = 59; // x86_64 架构
$ffi->seccomp_rule_add($ctx, $ffi->SCMP_ACT_KILL, $SYS_execve, 0);
// 加载过滤器
$ffi->seccomp_load($ctx);
// 清理
$ffi->seccomp_release($ctx);
echo "Seccomp 已通过 FFI 应用\n";
调用外部工具
使用执行脚本方式
<?php
// 创建 seccomp 配置脚本
function apply_seccomp_policy(array $allowed_syscalls = []) {
$script = "#!/bin/bash\n";
$script .= "SECCCOMP_TOOL=$(which seccomp-tool 2>/dev/null)\n";
if (empty($allowed_syscalls)) {
// 默认策略:禁止所有,然后允许白名单
$script .= "$SECCCOMP_TOOL --kill-on-error --blacklist=";
$script .= implode(',', get_default_blocked_syscalls());
} else {
// 白名单模式
$script .= "$SECCCOMP_TOOL --whitelist=";
$script .= implode(',', $allowed_syscalls);
}
exec($script, $output, $return_code);
if ($return_code !== 0) {
throw new RuntimeException("Seccomp 应用失败: " . implode("\n", $output));
}
}
// 使用示例
apply_seccomp_policy(['read', 'write', 'exit', 'exit_group']);
echo "Seccomp 策略已应用\n";
使用 Docker 的方式(适用于容器环境)
<?php
// 检查是否在 Docker 中
function get_docker_seccomp_profile() {
$profile = [
"defaultAction" => "SCMP_ACT_ERRNO",
"defaultErrnoRet" => 1,
"archMap" => [
["architecture" => "SCMP_ARCH_X86_64", "subArchitectures" => []]
],
"syscalls" => [
[
"names" => [
"read", "write", "exit", "exit_group",
"fstat", "brk", "mmap", "munmap", "access"
],
"action" => "SCMP_ACT_ALLOW",
"args" => []
]
]
];
return json_encode($profile);
}
// 应用 Docker seccomp profile
function apply_docker_seccomp($container_id) {
$profile = get_docker_seccomp_profile();
$profile_file = '/tmp/seccomp-' . uniqid() . '.json';
file_put_contents($profile_file, $profile);
// 通过 Docker 命令应用
$cmd = sprintf(
'docker update --security-opt seccomp=%s %s',
escapeshellarg($profile_file),
escapeshellarg($container_id)
);
exec($cmd, $output, $return_code);
unlink($profile_file);
return $return_code === 0;
}
完整的 PHP 封装类
<?php
class SeccompManager {
private $ffi = null;
private $ctx = null;
public function __construct() {
$this->initFFI();
}
private function initFFI() {
try {
$this->ffi = FFI::cdef("
typedef struct scmp_filter_ctx *scmp_filter_ctx;
scmp_filter_ctx seccomp_init(uint32_t def_action);
int seccomp_rule_add(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, ...);
int seccomp_load(scmp_filter_ctx ctx);
void seccomp_release(scmp_filter_ctx ctx);
#define SCMP_ACT_ALLOW 0x7fff0000U
#define SCMP_ACT_KILL 0x00000000U
#define SCMP_ACT_ERRNO 0x00050000U
", "libseccomp.so");
} catch (FFI\Exception $e) {
throw new RuntimeException("无法初始化 FFI: " . $e->getMessage());
}
}
public function initialize($default_action = null) {
$action = $default_action ?: 0x7fff0000; // SCMP_ACT_ALLOW
$this->ctx = $this->ffi->seccomp_init($action);
if ($this->ctx === null) {
throw new RuntimeException("Seccomp 初始化失败");
}
return true;
}
public function blockSyscall($syscall_num, $action = 0) { // SCMP_ACT_KILL
if ($this->ctx === null) {
throw new RuntimeException("请先初始化");
}
$result = $this->ffi->seccomp_rule_add(
$this->ctx,
$action,
$syscall_num,
0
);
return $result === 0;
}
public function apply() {
if ($this->ctx === null) {
throw new RuntimeException("请先初始化");
}
$result = $this->ffi->seccomp_load($this->ctx);
return $result === 0;
}
public function cleanup() {
if ($this->ctx !== null) {
$this->ffi->seccomp_release($this->ctx);
$this->ctx = null;
}
}
public function __destruct() {
$this->cleanup();
}
}
// 使用示例
try {
$seccomp = new SeccompManager();
$seccomp->initialize();
// 禁止危险系统调用
$blocked_syscalls = [
59, // execve (x86_64)
57, // fork (x86_64)
107, // socket (x86_64)
41, // socketcall (x86_64)
];
foreach ($blocked_syscalls as $syscall) {
$seccomp->blockSyscall($syscall);
}
$seccomp->apply();
$seccomp->cleanup();
echo "Seccomp 配置完成\n";
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
注意事项
- 权限要求:Seccomp 需要 root 权限或适当的 capabilities
- 系统架构:系统调用号因架构而异(x86_64 vs ARM)
- 不可逆性:Seccomp 一旦应用就无法撤销
- 性能影响:过多的系统调用检查会影响性能
- 兼容性:确保 PHP 版本支持 FFI(8.0+)
选择哪种方式取决于你的具体需求、PHP 版本和运行环境。