You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP API如何验证移动端APP来源并防范虚假获奖参赛提交?

解决方案建议

1. 基于登录态的服务端验证令牌

既然用户已在APP登录,APP应持有服务端颁发的会话令牌(如JWT或Session ID)。你的PHP API要求请求时携带该令牌,验证通过后从令牌解析出真实的account ID,绝不直接信任请求体中的account ID。同时,服务端需维护「用户获奖记录池」,每次请求先校验该account ID是否确实获得对应prize ID——这一步必须由服务端完成,完全不依赖客户端判断。

PHP示例(JWT验证):

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

// 从请求头获取令牌
$token = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if (empty($token)) {
    http_response_code(401);
    exit('Unauthorized');
}

try {
    // 验证并解析JWT,提取真实account ID
    $decoded = JWT::decode($token, new Key('your-secret-key', 'HS256'));
    $realAccountId = $decoded->account_id;
    
    // 对比请求体account ID,不一致直接拒绝
    if ($_POST['account_id'] !== $realAccountId) {
        http_response_code(403);
        exit('Invalid account ID');
    }
    
    // 服务端校验用户是否获得对应奖品
    $isValidPrize = checkUserPrize($realAccountId, $_POST['prize_id']);
    if (!$isValidPrize) {
        http_response_code(400);
        exit('Invalid prize claim');
    }
    
    // 后续处理地址、协议同意等信息
} catch (Exception $e) {
    http_response_code(401);
    exit('Invalid token');
}

// 模拟服务端获奖校验函数
function checkUserPrize($accountId, $prizeId) {
    $pdo = new PDO('mysql:host=localhost;dbname=your_db', 'user', 'pass');
    $stmt = $pdo->prepare('SELECT COUNT(*) FROM user_prizes WHERE account_id = ? AND prize_id = ? AND is_claimed = 0');
    $stmt->execute([$accountId, $prizeId]);
    return $stmt->fetchColumn() > 0;
}

2. 一次性请求签名机制

和APP开发方约定一个共享密钥,让APP发起请求前,用HMAC算法对account ID + prize ID + 当前时间戳生成签名,将这些参数连同签名一起提交。PHP端用相同规则生成签名并对比,同时校验时间戳有效期(比如5分钟内),防止重放攻击。

PHP端校验逻辑:

$secretKey = 'your-shared-secret'; // 和APP端一致,密钥需保密
$accountId = $_POST['account_id'];
$prizeId = $_POST['prize_id'];
$timestamp = $_POST['timestamp'];
$clientSignature = $_POST['signature'];

// 校验请求是否过期
if (time() - $timestamp > 300) { // 5分钟有效期
    http_response_code(400);
    exit('Request expired');
}

// 服务端生成签名并对比
$serverSignature = hash_hmac('sha256', $accountId . $prizeId . $timestamp, $secretKey);
if ($serverSignature !== $clientSignature) {
    http_response_code(403);
    exit('Invalid signature');
}

// 再执行服务端获奖有效性校验(同方案1的checkUserPrize函数)

注:该方案依赖APP安全存储密钥,若密钥泄露及时更换即可,比无验证的安全性高很多。

3. 预生成获奖凭证

如果获奖判定由服务端完成,用户获奖时服务端直接生成唯一的获奖凭证(如UUID),关联该用户和prize ID。APP只需携带此凭证提交地址等信息,API仅需校验凭证的有效性(是否存在、未被使用、关联用户匹配)。

PHP示例:

$claimToken = $_POST['claim_token'];

// 校验获奖凭证
$pdo = new PDO('mysql:host=localhost;dbname=your_db', 'user', 'pass');
$stmt = $pdo->prepare('SELECT account_id, prize_id FROM prize_claims WHERE token = ? AND is_used = 0');
$stmt->execute([$claimToken]);
$claim = $stmt->fetch(PDO::FETCH_ASSOC);

if (!$claim) {
    http_response_code(400);
    exit('Invalid claim token');
}

// 可选:验证请求的account ID与凭证关联用户是否一致
if ($_POST['account_id'] !== $claim['account_id']) {
    http_response_code(403);
    exit('Mismatched account');
}

// 处理地址等信息,标记凭证为已使用
$stmt = $pdo->prepare('UPDATE prize_claims SET is_used = 1, address = ?, agree_terms = ?, claim_date = ? WHERE token = ?');
$stmt->execute([$_POST['address'], $_POST['agree_terms'], $_POST['date'], $claimToken]);

这个方案安全性最高,伪造凭证几乎不可能,完全依赖服务端预生成逻辑,无需APP存储密钥,从根源避免了逆向获取prize ID后伪造请求的问题。

核心注意事项

  • 绝对禁止依赖客户端的获奖判定:无论采用哪种方案,最终获奖有效性必须由服务端校验,客户端仅负责展示和提交。
  • 不要信任请求体中的任何参数:account ID必须从登录令牌或预生成凭证中获取,而非直接使用请求参数。
  • 防范重放攻击:签名方案加入时间戳,预生成凭证标记为一次性使用,避免同一请求被多次提交。

内容的提问来源于stack exchange,提问作者Adam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 13:57:34