本文目录导读:

在PHP项目中实现敏感操作二次验证,通常用于修改密码、删除数据、转账等关键操作的二次确认,下面我将为你介绍几种常见的实现方案。
验证码二次验证
实现方式
使用图片验证码或短信验证码进行二次确认。
<?php
// 生成验证码并存入session
session_start();
$code = generateRandomCode(6);
$_SESSION['verify_code'] = $code;
$_SESSION['verify_expire'] = time() + 300; // 5分钟过期
?>
<!-- 前端HTML -->
<form method="POST" action="process.php">
<label>请输入验证码验证身份</label>
<input type="text" name="verify_code" required>
<input type="hidden" name="action" value="delete_account">
<button type="submit">确认删除账号</button>
</form>
<?php
// 后端验证
if ($_POST['action'] === 'delete_account') {
// 验证是否存在验证码和重要操作标识
session_start();
if (!isset($_SESSION['verify_code']) ||
$_SESSION['verify_expire'] < time()) {
die('验证已过期,请重新验证');
}
if ($_POST['verify_code'] !== $_SESSION['verify_code']) {
die('验证码错误');
}
// 清除已使用的验证码
unset($_SESSION['verify_code']);
unset($_SESSION['verify_expire']);
// 执行敏感操作
deleteAccount($userId);
}
?>
密码二次验证
要求用户再次输入密码来确认身份。
<?php
class SecurityVerification {
private $db;
private $session;
public function __construct($db, $session) {
$this->db = $db;
$this->session = $session;
}
/**
* 密码二次验证
*/
public function passwordVerify($password) {
// 从session获取当前用户ID
$userId = $this->session->get('user_id');
// 从数据库获取用户密码哈希
$stmt = $this->db->prepare("SELECT password_hash FROM users WHERE id = ?");
$stmt->execute([$userId]);
$user = $stmt->fetch();
if (!$user) {
return false;
}
// 验证密码
return password_verify($password, $user['password_hash']);
}
/**
* 执行敏感操作(带二次验证)
*/
public function sensitiveOperation($operation, $params, $password) {
// 验证密码
if (!$this->passwordVerify($password)) {
throw new Exception('密码验证失败');
}
// 记录操作日志
$this->logOperation($operation, $params);
// 执行具体操作
switch ($operation) {
case 'change_email':
return $this->changeEmail($params['new_email']);
case 'delete_account':
return $this->deleteAccount();
default:
throw new Exception('未知操作');
}
}
}
?>
两步验证(2FA)
使用时间基的一次性密码(TOTP)
<?php
require_once 'vendor/autoload.php'; // 引入 OTP 库
use OTPHP\TOTP;
class TwoFactorAuth {
private $db;
private $totp;
public function __construct($db) {
$this->db = $db;
$this->totp = TOTP::create();
}
/**
* 生成并保存2FA密钥
*/
public function generateSecret($userId) {
$secret = $this->totp->getSecret();
// 保存到数据库
$stmt = $this->db->prepare("UPDATE users SET totp_secret = ? WHERE id = ?");
$stmt->execute([$secret, $userId]);
return $secret;
}
/**
* 验证一次性密码
*/
public function verifyOTP($userId, $otp) {
// 获取用户密钥
$stmt = $this->db->prepare("SELECT totp_secret FROM users WHERE id = ?");
$stmt->execute([$userId]);
$secret = $stmt->fetchColumn();
if (!$secret) {
return false;
}
// 验证OTP
$totp = TOTP::create($secret);
return $totp->verify($otp);
}
/**
* 敏感操作二次验证
*/
public function verifyBeforeOperation($userId, $otp, $callback) {
if (!$this->verifyOTP($userId, $otp)) {
throw new Exception('验证码无效');
}
// 执行回调函数(敏感操作)
return $callback();
}
}
// 使用示例
$twoFactor = new TwoFactorAuth($db);
$userId = $_SESSION['user_id'];
// 验证OTP后执行转账操作
$twoFactor->verifyBeforeOperation($userId, $_POST['otp_code'], function() use ($userId, $amount) {
// 执行转账逻辑
transferMoney($userId, $amount);
});
?>
Token验证机制
创建一个临时的操作令牌用于二次确认。
<?php
class TokenVerification {
private $db;
private $session;
public function __construct($db, $session) {
$this->db = $db;
$this->session = $session;
}
/**
* 生成操作令牌
*/
public function generateToken($userId, $operation, $expireMinutes = 5) {
$token = bin2hex(random_bytes(32));
$expires = date('Y-m-d H:i:s', strtotime("+{$expireMinutes} minutes"));
// 保存令牌信息
$stmt = $this->db->prepare("
INSERT INTO operation_tokens (user_id, token, operation, expires_at)
VALUES (?, ?, ?, ?)
");
$stmt->execute([$userId, $token, $operation, $expires]);
return $token;
}
/**
* 验证令牌
*/
public function verifyToken($token, $operation) {
$stmt = $this->db->prepare("
SELECT * FROM operation_tokens
WHERE token = ? AND operation = ? AND expires_at > NOW()
AND used = 0
");
$stmt->execute([$token, $operation]);
$result = $stmt->fetch();
if (!$result) {
return false;
}
// 标记令牌已使用
$stmt = $this->db->prepare("UPDATE operation_tokens SET used = 1 WHERE id = ?");
$stmt->execute([$result['id']]);
return true;
}
/**
* 敏感操作流程
*/
public function sensitiveOperation($operation, $data) {
// 第一步:生成令牌并发送到用户邮箱/短信
$token = $this->generateToken(
$this->session->get('user_id'),
$operation
);
// 发送令牌到用户手机或邮箱
sendVerificationCode($token);
return [
'status' => 'pending',
'message' => '请查收验证码并确认操作',
'token_id' => $token
];
}
/**
* 确认操作(第二步)
*/
public function confirmOperation($token, $operation, $data) {
if (!$this->verifyToken($token, $operation)) {
throw new Exception('令牌无效或已过期');
}
// 执行敏感操作
switch ($operation) {
case 'delete_assets':
return deleteAssets($data['asset_id']);
case 'transfer_ownership':
return transferOwnership($data);
}
}
}
?>
完整的安全建议
<?php
trait SecurityVerificationTrait {
/**
* 验证请求来源和CSRF保护
*/
private function validateRequest() {
// 验证CSRF令牌
if (!isset($_POST['csrf_token']) ||
$_POST['csrf_token'] !== $_SESSION['csrf_token']) {
throw new Exception('CSRF验证失败');
}
// 验证请求来源
$referrer = parse_url($_SERVER['HTTP_REFERER'] ?? '');
if ($referrer['host'] !== $_SERVER['HTTP_HOST']) {
throw new Exception('非法的请求来源');
}
// 验证请求方法
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
throw new Exception('仅支持POST请求');
}
}
/**
* 操作日志记录
*/
private function logOperation($userId, $operation, $status, $details = []) {
$logData = [
'user_id' => $userId,
'operation' => $operation,
'status' => $status,
'ip' => $_SERVER['REMOTE_ADDR'],
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'details' => json_encode($details),
'created_at' => date('Y-m-d H:i:s')
];
// 保存到日志表
$sql = "INSERT INTO operation_logs (user_id, operation, status, ip, user_agent, details, created_at)
VALUES (:user_id, :operation, :status, :ip, :user_agent, :details, :created_at)";
// 执行SQL...
}
/**
* 限制尝试次数,防止暴力破解
*/
private function checkAttemptLimit($userId) {
$attempts = $this->redis->get("verify_attempts:{$userId}") ?: 0;
if ($attempts >= 5) {
// 锁定账户一段时间
$this->redis->setex("account_locked:{$userId}", 1800, 1);
throw new Exception('尝试次数过多,账户已被锁定30分钟');
}
// 增加尝试次数
$this->redis->incr("verify_attempts:{$userId}");
$this->redis->expire("verify_attempts:{$userId}", 3600);
}
}
// 综合使用示例
class SensitiveOperationController {
use SecurityVerificationTrait;
public function executeSensitiveOperation($operation, $params) {
try {
// 1. 基础验证
$this->validateRequest();
// 2. 检查尝试限制
$userId = $_SESSION['user_id'];
$this->checkAttemptLimit($userId);
// 3. 二次验证
if (!isset($_POST['verify_code'])) {
throw new Exception('请提供验证码');
}
$verifyResult = $this->verifyCode($userId, $_POST['verify_code']);
if (!$verifyResult) {
$this->logOperation($userId, $operation, 'failed', [
'reason' => '验证码错误'
]);
throw new Exception('验证失败');
}
// 4. 执行操作
$result = $this->performOperation($operation, $params);
// 5. 记录成功日志
$this->logOperation($userId, $operation, 'success', $params);
return $result;
} catch (Exception $e) {
// 记录失败日志
$this->logOperation($userId ?? 0, $operation, 'error', [
'error' => $e->getMessage()
]);
throw $e;
}
}
}
?>
总结建议
-
选择合适方案:根据安全性要求选择:简单操作用验证码,高安全用2FA或Token
-
记录日志:所有二次验证尝试和敏感操作都要记录
-
限制尝试次数:防止暴力破解
-
令牌过期机制:设置合理的过期时间(通常5-10分钟)
-
CSRF保护:结合CSRF Token防止跨站请求伪造
-
会话管理:验证过程应基于会话状态,确保用户已登录
-
通知用户:验证成功后通知用户(如邮件、短信)
-
异常处理:妥善处理各种异常情况,避免信息泄露
根据项目具体需求选择实现方式,轻量级项目可使用验证码方案,金融级项目建议使用2FA或多重验证。