PHP 怎么Seccomp

wen PHP项目 2

本文目录导读:

PHP 怎么Seccomp

  1. 使用 PHP 扩展
  2. 通过 FFI(Foreign Function Interface)
  3. 调用外部工具
  4. 使用 Docker 的方式(适用于容器环境)
  5. 完整的 PHP 封装类
  6. 注意事项

在 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";
}

注意事项

  1. 权限要求:Seccomp 需要 root 权限或适当的 capabilities
  2. 系统架构:系统调用号因架构而异(x86_64 vs ARM)
  3. 不可逆性:Seccomp 一旦应用就无法撤销
  4. 性能影响:过多的系统调用检查会影响性能
  5. 兼容性:确保 PHP 版本支持 FFI(8.0+)

选择哪种方式取决于你的具体需求、PHP 版本和运行环境。

抱歉,评论功能暂时关闭!