php案例 用实现一套安全授权机制 + 核心逻辑逆向防护=✅ 授权有效!

php案例 用实现一套安全授权机制 + 核心逻辑逆向防护=✅ 授权有效!

需要运行的命令

***poser require web-token/jwt-framework
***poser global require phalcon/zephir
***poser global config bin-dir --absolute

wsl2中:

webman@WIN-3RSG1B1SCQ2:/mnt/d/phpstudy_pro/WWW/static-php-cli-main$ export PATH="$PATH:/home/webman/.config/***poser/vendor/bin"
webman@WIN-3RSG1B1SCQ2:/mnt/d/phpstudy_pro/WWW/static-php-cli-main$ zephir --version


 _____              __    _
/__  /  ___  ____  / /_  (_)____
  / /  / _ \/ __ \/ __ \/ / ___/
 / /__/  __/ /_/ / / / / / /
/____/\___/ .___/_/ /_/_/_/
         /_/

Zephir 0.19.0 by the Phalcon Team
Usage:
  ***mand [options] [arguments]

Options:
      --dumpversion  Print the version of the ***piler and don't do anything else (also works with a single hyphen)
  -h, --help         Print this help message
      --no-ansi      Disable ANSI output
  -v, --verbose      Displays more detail in error messages from exceptions generated by ***mands (can also disable with -V)
      --vernum       Print the version of the ***piler as integer
      --version      Print ***piler version information and quit

Available ***mands:
  api        Generates a HTML API based on the classes exposed in the extension
  build      Generates/***piles/Installs a Zephir extension
  clean      Cleans any object files created by the extension
  ***pile    ***pile a Zephir extension
  fullclean  Cleans any object files created by the extension (including files generated by phpize)
  generate   Generates C code from the Zephir code without ***piling it
  help       Display help for a ***mand
  init       Initializes a Zephir extension
  install    Installs the extension in the extension directory (may require root password)
  stubs      Generates stubs that can be used in a PHP IDE

webman@WIN-3RSG1B1SCQ2:/mnt/d/phpstudy_pro/WWW/static-php-cli-main$ zephir build
Extension namespace cannot be loaded
webman@WIN-3RSG1B1SCQ2:/mnt/d/phpstudy_pro/WWW/static-php-cli-main$ sudo apt install golang-go
webman@WIN-3RSG1B1SCQ2:/mnt/d/phpstudy_pro/WWW/static-php-cli-main$ GOOS=windows GOARCH=amd64 go build -o license_verify.exe verify.go

powershell中:

payload.json
{"exp":1992012800,"domain":"localhost:85"}
$env:CLIENT_DOMAIN = "localhost:85"
PS D:\phpstudy_pro\WWW\static-php-cli-main> license_verify.exe .\payload.json
true

guard.zep

namespace LicenseGuard;

class Guard {
    /**
     * 核心验证:域名+过期时间
     * @param array payload 授权数据
     * @return bool 是否有效
     */
    public static function verify(array payload) -> bool {
        // 验证过期时间
        if payload["exp"] < time() {
            return false;
        }
        // 验证域名(获取当前客户端域名)
        string currentDomain = _SERVER["HTTP_HOST"];
        if payload["domain"] != currentDomain {
            return false;
        }
        return true;
    }
}

步骤 1:生成非对称密钥对(RSA)

用web-token/jwt-framework生成私钥(加签用)和公钥(验签用)

1.php

<?php
require __DIR__ . '/vendor/autoload.php';
use Jose\***ponent\KeyManagement\JWKFactory;

// -------------------------- 配置(替换成你的实际路径) --------------------------
$openssl***f = "D:/EServer-data/childApp/php/php-8.4/extras/ssl/openssl.***f";
// --------------------------------------------------------------------------------

try {
    // 1. 原生OpenSSL生成RSA密钥(显式传递config参数)
    $config = [
        'private_key_bits' => 2048,
        'private_key_type' => OPENSSL_KEYTYPE_RSA,
        'config' => $openssl***f  // 关键:手动指定config文件
    ];
    $privateKeyResource = openssl_pkey_new($config);
    if (!$privateKeyResource) {
        throw new \RuntimeException("原生密钥生成失败:" . openssl_error_string());
    }

    // 2. 导出私钥PEM
    openssl_pkey_export($privateKeyResource, $privateKeyPem, null, $config);
    if (!$privateKeyPem) {
        throw new \RuntimeException("私钥导出失败:" . openssl_error_string());
    }

    // 3. 导出公钥PEM
    $publicKeyDetails = openssl_pkey_get_details($privateKeyResource);
    if (!$publicKeyDetails) {
        throw new \RuntimeException("公钥获取失败:" . openssl_error_string());
    }
    $publicKeyPem = $publicKeyDetails['key'];

    // 4. 转换成框架的JWK对象(保留alg和use属性)
    $privateJwk = JWKFactory::createFromKey($privateKeyPem, null, [
        'alg' => 'RS256',
        'use' => 'sig'
    ]);
    $publicJwk = $privateJwk->toPublic();

    // 5. 保存密钥文件
    $privatePemPath = __DIR__ . "/private_key.pem";
    $publicPemPath = __DIR__ . "/public_key.pem";
    $privateJwkPath = __DIR__ . "/private_key.jwk";
    $publicJwkPath = __DIR__ . "/public_key.jwk";

    file_put_contents($privatePemPath, $privateKeyPem);
    file_put_contents($publicPemPath, $publicKeyPem);
    file_put_contents($privateJwkPath, json_encode($privateJwk));
    file_put_contents($publicJwkPath, json_encode($publicJwk));

    echo "✅ 密钥生成成功!\n";
    echo "私钥(PEM):{$privatePemPath}\n";
    echo "公钥(PEM):{$publicPemPath}\n";
    echo "私钥(JWK):{$privateJwkPath}\n";
    echo "公钥(JWK):{$publicJwkPath}\n";

} catch (\Exception $e) {
    echo "❌ 错误:{$e->getMessage()}\n";
    while ($err = openssl_error_string()) {
        echo "🔍 {$err}\n";
    }
}


步骤 2:私钥加签授权文件

用私钥对授权数据(含过期时间、域名等)签名,生成不可篡改的 JWS:

2.php

<?php
require_once "vendor/autoload.php";
use Jose\***ponent\Core\JWK;
use Jose\***ponent\Signature\JWSBuilder;
use Jose\***ponent\Signature\Serializer\***pactSerializer;
use Jose\***ponent\Core\AlgorithmManager;
use Jose\***ponent\Signature\Algorithm\RS256;

// 授权数据结构
$payload = json_encode([
    'exp' => time() + 3600*24*30,
    'domain' => 'localhost:85',
    'license_id' => 'LIC-2024-001'
]);

$privateKeyPath = 'private_key.jwk';

if (!file_exists($privateKeyPath)) {
    die("错误:私钥文件不存在,请先运行 generate_rsa_keys.php!");
}

// 构建JWS签名
$algorithmManager = new AlgorithmManager([new RS256()]);
$jwsBuilder = new JWSBuilder($algorithmManager);

$privateKeyJson = file_get_contents($privateKeyPath);
$privateKey = JWK::createFromJson($privateKeyJson);

$jws = $jwsBuilder->create()
    ->withPayload($payload)
    ->addSignature($privateKey, ['alg' => 'RS256'])
    ->build();

// 序列化JWS
$serializer = new ***pactSerializer();
$signedLicense = $serializer->serialize($jws, 0);

// 保存授权文件
file_put_contents('license.jws', $signedLicense);

echo "授权文件生成成功!\n";
echo "文件:license.jws\n";
?>

步骤 3:客户端验签(核心逻辑逆向保护)

客户端需验证签名有效性 + 授权项(过期、域名),核心逻辑用不易逆向的语言实现:

3.php

PHP 层:调用编译后的核心逻辑

PHP 仅负责调用二进制 / 扩展,不直接处理核心验证:

<?php
require_once "vendor/autoload.php";

// 补充缺失的类引入
use Jose\***ponent\Core\AlgorithmManager;
use Jose\***ponent\Signature\Algorithm\RS256;
use Jose\***ponent\Signature\JWSVerifier;
use Jose\***ponent\Signature\Serializer\***pactSerializer;
use Jose\***ponent\KeyManagement\JWKFactory;

try {
    // 1. 读取PEM格式公钥(修正:用createFromKey处理PEM)
    $publicKeyPem = file_get_contents('public_key.pem');
    if (!$publicKeyPem) throw new \RuntimeException("公钥文件不存在或无法读取");
    $publicKey = JWKFactory::createFromKey($publicKeyPem); // 正确方法

    // 2. 读取授权文件
    $signedLicense = file_get_contents('license.jws');
    if (!$signedLicense) throw new \RuntimeException("授权文件不存在");

    // 3. 反序列化JWS
    $serializer = new ***pactSerializer();
    $jws = $serializer->unserialize($signedLicense);

    // 4. 验证签名(补充AlgorithmManager和RS256的引入)
    $algorithmManager = new AlgorithmManager([new RS256()]);
    $verifier = new JWSVerifier($algorithmManager);
    $isSignatureValid = $verifier->verifyWithKey($jws, $publicKey, 0);

    if (!$isSignatureValid) die("❌ 签名无效!");

    // 5. 后续授权验证(保持你的业务逻辑)
    $payload = json_decode($jws->getPayload(), true);
    $currentDomain = $_SERVER["HTTP_HOST"] ?? '';
    
    // 示例:替换为你的核心验证逻辑
    $isAuthorized = (isset($payload['domain']) && $payload['domain'] === $currentDomain);
    
    if ($isAuthorized) echo "✅ 授权有效!";
    else die("❌ 授权无效!");

} catch (\Exception $e) {
    die("❌ 错误:" . $e->getMessage());
}

3.2 核心逻辑:用不易逆向的语言实现

将域名验证、过期时间检查等核心逻辑用C 扩展(Zephir)或Go实现:

示例:Zephir 写 C 扩展(最贴合 PHP)

Zephir 是高级语言,编译为 C 扩展,性能高且不易逆向:

verify.go

package main

import (
    "encoding/json"
    "fmt"
    "os"
    "time"
)

type Payload struct {
    Exp    int64  `json:"exp"`
    Domain string `json:"domain"`
}

func main() {
    // Step A: Check if a file path is provided (required!)
    if len(os.Args) < 2 {
        fmt.Fprintln(os.Stderr, "Usage: license_verify.exe <payload.json>")
        fmt.Println("false")
        return
    }
    filePath := os.Args[1]

    // Step B: Read the JSON file
    fileContent, err := os.ReadFile(filePath)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error reading file: %v\n", err)
        fmt.Println("false")
        return
    }

    // Step C: Parse JSON from file content
    var payload Payload
    err = json.Unmarshal(fileContent, &payload)
    if err != nil {
        fmt.Fprintf(os.Stderr, "JSON Parse Error: %v\n", err)
        fmt.Println("false")
        return
    }

    // Step D: Validate expiration and domain
    now := time.Now().Unix()
    if payload.Exp < now {
        fmt.Fprintln(os.Stderr, "Error: Payload expired")
        fmt.Println("false")
        return
    }
    clientDomain := os.Getenv("CLIENT_DOMAIN")
    if clientDomain == "" {
        fmt.Fprintln(os.Stderr, "Error: CLIENT_DOMAIN env var not set")
        fmt.Println("false")
        return
    }
    if payload.Domain != clientDomain {
        fmt.Fprintf(os.Stderr, "Domain mismatch: %s vs %s\n", payload.Domain, clientDomain)
        fmt.Println("false")
        return
    }

    // Step E: All checks passed
    fmt.Println("true")
}

效果
✅ 授权有效!

PS D:\EServer-data\www\static-php-cli-main> license_verify.exe .\payload.json
true
转载请说明出处内容投诉
CSS教程网 » php案例 用实现一套安全授权机制 + 核心逻辑逆向防护=✅ 授权有效!

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买