首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 透過 exec 取得硬體唯一識別碼:CPU 序號、MAC 位址跨平台實作

     編輯:PHP 綜合知識

授權綁定、資產管理、節點識別等場景常需取得伺服器/客戶端硬體唯一識別碼。PHP 在 Windows/Linux 下可透過 exec()/shell_exec() 呼叫系統指令獲取 CPU ID、主機板序號、MAC 位址等資訊。本文提供跨平台實作、安全考量與替代方案。

一、Windows 環境:WMI 查詢

1. 取得 CPU 處理器 ID

function getWindowsCpuId(): ?string {
    $cmd = 'wmic cpu get ProcessorId /value';
    $output = @shell_exec($cmd);
    if (!$output) return null;
    
    // 輸出格式:ProcessorId=BFEBFBFF000906E9
    if (preg_match('/ProcessorId\s*=\s*(\S+)/i', $output, $m)) {
        return strtoupper(trim($m[1]));
    }
    return null;
}

2. 取得主機板序號

function getWindowsBoardSerial(): ?string {
    $cmd = 'wmic baseboard get SerialNumber /value';
    $output = @shell_exec($cmd);
    if (!$output) return null;
    if (preg_match('/SerialNumber\s*=\s*(\S+)/i', $output, $m)) {
        return trim($m[1]);
    }
    return null;
}

3. 取得網卡 MAC 位址(排除虛擬/隧道介面)

function getWindowsMacAddresses(): array {
    $cmd = 'getmac /fo csv /nh';
    $output = @shell_exec($cmd);
    if (!$output) return [];
    
    $macs = [];
    $lines = explode("\n", $output);
    foreach ($lines as $line) {
        $line = trim($line);
        if ($line === '') continue;
        // CSV 格式:"Realtek PCIe GBE Family Controller","\Device\Tcpip_{...}","00-11-22-33-44-55"
        if (str_getcsv($line, ',', '"') === false) continue;
        $cols = str_getcsv($line, ',', '"');
        if (count($cols) >= 3) {
            $mac = strtoupper(str_replace('-', ':', $cols[2]));
            // 過濾 00:00:00:00:00:00、虛擬介面
            if ($mac !== '00:00:00:00:00:00' && !preg_match('/^(00:50:56|00:0C:29|00:1C:14)/', $mac)) {
                $macs[] = $mac;
            }
        }
    }
    return $macs;
}

二、Linux 環境:系統檔案與指令

1. CPU 序號(/proc/cpuinfo)

function getLinuxCpuId(): ?string {
    $content = @file_get_contents('/proc/cpuinfo');
    if (!$content) return null;
    
    // 取第一顆 CPU 的 serial(ARM)或 processor ID(x86 需 dmidecode)
    if (preg_match('/^Serial\s+:\s+(\S+)/m', $content, $m)) {
        return strtoupper($m[1]); // ARM Raspberry Pi 等
    }
    // x86 通常無 serial,改用 dmidecode(需 root)
    return null;
}

2. 主機板序號(dmidecode 需 root 權限)

function getLinuxBoardSerial(): ?string {
    $cmd = 'dmidecode -s baseboard-serial-number 2>/dev/null';
    $output = @shell_exec($cmd);
    return $output ? trim($output) : null;
}

3. MAC 位址(/sys/class/net/ 或 ip 指令)

function getLinuxMacAddresses(): array {
    $macs = [];
    $netDir = '/sys/class/net/';
    if (is_dir($netDir)) {
        foreach (scandir($netDir) as $iface) {
            if ($iface === '.' || $iface === '..' || $iface === 'lo') continue;
            $path = $netDir . $iface . '/address';
            if (is_file($path)) {
                $mac = strtoupper(trim(file_get_contents($path)));
                if ($mac !== '00:00:00:00:00:00') $macs[] = $mac;
            }
        }
    }
    return $macs;
}

三、跨平台統一介面

class HardwareId {
    public static function getMachineFingerprint(): string {
        $parts = [];
        
        // CPU
        $cpu = PHP_OS_FAMILY === 'Windows' 
            ? self::getWindowsCpuId() 
            : self::getLinuxCpuId();
        if ($cpu) $parts[] = 'cpu:' . $cpu;
        
        // 主機板
        $board = PHP_OS_FAMILY === 'Windows'
            ? self::getWindowsBoardSerial()
            : self::getLinuxBoardSerial();
        if ($board) $parts[] = 'board:' . $board;
        
        // MAC 位址(取第一張實體網卡)
        $macs = PHP_OS_FAMILY === 'Windows'
            ? self::getWindowsMacAddresses()
            : self::getLinuxMacAddresses();
        if ($macs) $parts[] = 'mac:' . $macs[0];
        
        // 組合雜湊作為指紋
        return hash('sha256', implode('|', $parts));
    }
    
    // ... 以上私有靜態方法 ...
}

四、安全與權限考量

風險說明緩解措施
指令注入參數未過濾導致任意命令執行不使用使用者輸入拼接指令;固定指令字串
權限不足dmidecode、wmic 需管理員/root服務帳號授予最小權限;或改讀 /sys、/proc
資訊洩露硬體 ID 屬敏感資產資訊僅在授權邏輯使用,不記錄日誌、不回傳前端
虛擬化/容器環境MAC/CPU ID 可能重複或為虛擬值結合多項指標、使用雲端實例 ID(AWS instance-id、Azure VM ID)
函數被禁用disable_functions=exec,shell_exec,...改用 com_dotnet (Windows WMI)、php_sysinfo 擴充、純 PHP 讀 /proc//sys

五、純 PHP 替代方案(無需 exec)

1. com_dotnet 擴充(Windows WMI)

if (class_exists('COM')) {
    $wmi = new COM('winmgmts://./root/cimv2');
    $cpus = $wmi->ExecQuery('SELECT ProcessorId FROM Win32_Processor');
    foreach ($cpus as $cpu) {
        echo $cpu->ProcessorId;
    }
}

2. 讀取 /etc/machine-id(Linux systemd)

$machineId = @file_get_contents('/etc/machine-id');
if ($machineId) echo trim($machineId); // 32 字元 hex

3. php_sysinfo / php_hardware PECL 擴充

提供結構化硬體資訊 API,效能優於指令呼叫,但需編譯安裝。

六、授權綁定最佳實踐

  1. 多因子指紋:CPU + 主機板 + MAC + 磁碟序號,單一變更不失效。
  2. 容差機制:允許 N 個指標變更(如換網卡),超過則需重新授權。
  3. 離線授權:客戶端產生指紋 → 伺服器簽署 License → 客戶端驗證簽名。
  4. 定期心跳:線上授權定期上報指紋,偵測遷移/複製。

結語:跨平台取得硬體 ID 需處理指令差異、權限限制、虛擬化環境等複雜度。建議封裝為 HardwareId 類別,提供統一介面與降級策略。授權系統應採多因子指紋+容差機制,避免單點故障導致合法用戶無法使用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved