如何实现100%安全的投票系统防篡改及AJAX传值防护
Hey there! Let's tackle your vote system security issues head-on—first, building a fully secure setup, then fixing the specific tampering problem you're facing right now.
The golden rule here is never trust frontend input. Browsers give users full control over what's sent to the server, so all validation, permission checks, and business logic must happen on the backend. Here's how to structure it:
- Treat all frontend data as untrusted: Any value sent from HTML/AJAX (like
id_post) can be modified via dev tools, so your backend must independently verify its legitimacy. - Mandate server-side validation for every action: When a user votes, your PHP backend should:
- Confirm the user is authenticated (if voting requires a login) to prevent anonymous spam.
- Check that the submitted
id_postactually exists in yourpostsdatabase table. - Verify the user hasn't already voted for this post (to stop duplicate votes).
- Only execute database updates after all checks pass.
- Hide sensitive logic from the frontend: Don't expose vote counts, user permissions, or database IDs in a way that lets users reverse-engineer your system. For example, avoid embedding raw vote totals in HTML—fetch them via an API that validates the user first.
- Implement CSRF protection: AJAX requests are vulnerable to cross-site request forgery. Generate a unique CSRF token per user session, include it in your HTML (as a hidden input), and require it in all vote requests. Your backend will validate the token matches the one stored in the user's session.
- Use prepared statements for SQL queries: This eliminates SQL injection risks, even if attackers tamper with input values.
id_post Tampering Issue Right now, your backend is blindly accepting the id_post value sent from the frontend—here's how to lock that down quickly:
Step 1: Validate the post ID exists
First, make sure the submitted id_post corresponds to a real post in your database. Use PDO prepared statements to avoid injection:
session_start(); $pdo = new PDO('mysql:host=your_host;dbname=your_db', 'user', 'pass'); $id_post = (int)$_POST['id_post']; // Cast to integer to sanitize input // Check if the post exists $stmt = $pdo->prepare("SELECT id FROM posts WHERE id = ?"); $stmt->execute([$id_post]); $post = $stmt->fetch(PDO::FETCH_ASSOC); if (!$post) { echo json_encode(['success' => false, 'message' => 'Invalid post ID']); exit; }
Step 2: Check for duplicate votes (and user authentication)
Prevent users from voting multiple times, and ensure only logged-in users can vote:
// Verify user is logged in if (!isset($_SESSION['user_id'])) { echo json_encode(['success' => false, 'message' => 'Please log in to vote']); exit; } $user_id = $_SESSION['user_id']; // Check if user already voted for this post $stmt = $pdo->prepare("SELECT id FROM votes WHERE user_id = ? AND post_id = ?"); $stmt->execute([$user_id, $id_post]); $existing_vote = $stmt->fetch(PDO::FETCH_ASSOC); if ($existing_vote) { echo json_encode(['success' => false, 'message' => 'You already voted for this post']); exit; }
Step 3: Safely update the database
Only after passing all checks should you record the vote. Again, use prepared statements:
// Record the vote in the votes table $stmt = $pdo->prepare("INSERT INTO votes (user_id, post_id) VALUES (?, ?)"); $vote_recorded = $stmt->execute([$user_id, $id_post]); // Update the post's vote count (if you have a vote_count column) if ($vote_recorded) { $stmt = $pdo->prepare("UPDATE posts SET vote_count = vote_count + 1 WHERE id = ?"); $stmt->execute([$id_post]); echo json_encode(['success' => true, 'message' => 'Vote submitted successfully']); } else { echo json_encode(['success' => false, 'message' => 'Failed to record vote']); }
Step 4: Add CSRF protection
To block unauthorized requests:
- Generate a CSRF token in your frontend page:
session_start(); if (!isset($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); }
- Include it in your HTML as a hidden input:
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
- Include it in your AJAX request:
const csrfToken = document.querySelector('input[name="csrf_token"]').value; fetch('vote_handler.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `id_post=${yourPostId}&csrf_token=${csrfToken}` })
- Validate the token in your backend:
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) { echo json_encode(['success' => false, 'message' => 'Invalid request']); exit; }
With these changes, even if a user tampers with the id_post value in dev tools, your backend will reject invalid requests, prevent duplicate votes, and block CSRF attacks.
内容的提问来源于stack exchange,提问作者z.yastos

