如何在登录时正确存储设备/应用/浏览器信息(PHP+JS)
Hey Rahul, awesome idea! You’re already off to a good start with using $_SERVER variables, but let’s address the limitations of relying solely on HTTP_USER_AGENT and build a robust, accurate device tracking system similar to Facebook’s.
First, Let’s Fix the Core Issues with Your Initial Approach
REMOTE_ADDRmight not be the real user IP: If your site uses a CDN, reverse proxy, or users are on a VPN/proxy,REMOTE_ADDRwill return the proxy’s IP instead of the user’s actual address.HTTP_USER_AGENTis unreliable: It can be easily spoofed, and parsing it manually to get device/browser details is error-prone (different browsers format it differently).- Missing context: PHP alone can’t get details like screen resolution, timezone, or exact device model—you’ll need JavaScript to fill those gaps.
Step-by-Step Implementation
1. Get the User’s Real IP Address
Create a helper function to fetch the actual user IP, accounting for proxies. Make sure to whitelist your trusted proxies (like Cloudflare, Nginx) to prevent spoofing:
function getRealUserIp() { $ipKeys = [ 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP', 'REMOTE_ADDR' ]; foreach ($ipKeys as $key) { if (!empty($_SERVER[$key])) { // Split multiple IPs (comma-separated) and take the first valid one $ipList = explode(',', $_SERVER[$key]); foreach ($ipList as $ip) { $ip = trim($ip); if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { // Add your trusted proxy IPs here to skip them $trustedProxies = ['192.168.1.1', '10.0.0.1']; if (!in_array($ip, $trustedProxies)) { return $ip; } } } } } return $_SERVER['REMOTE_ADDR'] ?? 'Unknown'; }
2. Parse User Agent into Useful Details
Instead of parsing HTTP_USER_AGENT manually, use a dedicated library to get accurate device/browser info. Two great options:
- Mobile_Detect: Lightweight, focused on mobile devices
- ua-parser-php: More comprehensive, covers browsers, OS, and devices
Here’s an example with Mobile_Detect:
require_once 'Mobile_Detect.php'; $detect = new Mobile_Detect(); $deviceInfo = [ 'is_mobile' => $detect->isMobile(), 'is_tablet' => $detect->isTablet(), 'browser' => $detect->browser() . ' ' . $detect->version($detect->browser()), 'os' => $detect->operatingSystem() . ' ' . $detect->version($detect->operatingSystem()), 'device' => $detect->getDeviceName() ?? 'Unknown Device' ];
3. Fetch Additional Details with JavaScript
Use JS to grab details PHP can’t access, then send them to your backend on login:
// Get device/context details const deviceDetails = { screen_resolution: `${screen.width}x${screen.height}`, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, language: navigator.language, is_touch_enabled: 'ontouchstart' in window }; // Send to PHP via AJAX (or include in login form as hidden fields) fetch('/save-device-details.php', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content // Add CSRF protection! }, body: JSON.stringify({ session_id: '<?php echo session_id(); ?>', // Pass current session ID user_id: '<?php echo $_SESSION['user_id']; ?>', details: deviceDetails }) });
4. Database Schema for Storing Device Sessions
Create a table to track each login session’s device info:
CREATE TABLE user_sessions ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, session_id VARCHAR(255) NOT NULL, ip_address VARCHAR(45) NOT NULL, -- IPv6 support user_agent TEXT NOT NULL, -- Store raw UA for fallback parsed_device JSON NOT NULL, -- Store parsed device/browser info additional_details JSON NOT NULL, -- Store JS-fetched details login_timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, last_activity DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, is_active BOOLEAN DEFAULT TRUE, FOREIGN KEY (user_id) REFERENCES users(id) );
5. Save Device Info on Successful Login
When the user logs in, save all the collected data to the database:
// After successful authentication session_start(); $userId = $_SESSION['user_id']; $sessionId = session_id(); $realIp = getRealUserIp(); $rawUserAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown'; // Combine parsed device info and JS details (fetch JS details from POST if using form submission) $fullDeviceData = [ 'parsed' => $deviceInfo, 'additional' => $_POST['device_details'] ?? [] ]; // Insert into database $pdo = new PDO('mysql:host=localhost;dbname=your_db', 'user', 'pass'); $stmt = $pdo->prepare("INSERT INTO user_sessions (user_id, session_id, ip_address, user_agent, parsed_device, additional_details) VALUES (?, ?, ?, ?, ?, ?)"); $stmt->execute([ $userId, $sessionId, $realIp, $rawUserAgent, json_encode($fullDeviceData['parsed']), json_encode($fullDeviceData['additional']) ]);
6. Display the Device List to Users
Create a page where users can view their active sessions, similar to Facebook’s settings:
// Fetch active sessions for the logged-in user $stmt = $pdo->prepare("SELECT * FROM user_sessions WHERE user_id = ? AND is_active = TRUE ORDER BY login_timestamp DESC"); $stmt->execute([$userId]); $sessions = $stmt->fetchAll(PDO::FETCH_ASSOC); ?> <h2>Your Active Sessions</h2> <ul> <?php foreach ($sessions as $session): ?> <?php $parsed = json_decode($session['parsed_device'], true); ?> <?php $additional = json_decode($session['additional_details'], true); ?> <li> <strong><?php echo $parsed['device']; ?></strong> (<?php echo $parsed['browser']; ?> on <?php echo $parsed['os']; ?>)<br> IP: <?php echo $session['ip_address']; ?><br> Last active: <?php echo date('F j, Y g:i a', strtotime($session['last_activity'])); ?><br> <?php if (!empty($additional['timezone'])): ?> Timezone: <?php echo $additional['timezone']; ?><br> <?php endif; ?> <button onclick="logoutSession('<?php echo $session['session_id']; ?>')">Log out this device</button> </li> <?php endforeach; ?> </ul> <script> function logoutSession(sessionId) { fetch('/logout-session.php', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content }, body: JSON.stringify({session_id: sessionId}) }).then(() => window.location.reload()); } </script>
7. Add "Log Out This Device" Functionality
Create the backend endpoint to invalidate a session:
session_start(); $pdo = new PDO('mysql:host=localhost;dbname=your_db', 'user', 'pass'); $data = json_decode(file_get_contents('php://input'), true); $sessionId = $data['session_id']; $userId = $_SESSION['user_id']; // Mark session as inactive $stmt = $pdo->prepare("UPDATE user_sessions SET is_active = FALSE WHERE session_id = ? AND user_id = ?"); $stmt->execute([$sessionId, $userId]); // Optional: Destroy the session if it's the current one if ($sessionId === session_id()) { session_destroy(); echo json_encode(['success' => true, 'redirect' => '/login']); } else { echo json_encode(['success' => true]); }
Key Security & Privacy Notes
- CSRF Protection: Always include CSRF tokens in AJAX requests and form submissions to prevent cross-site request forgery.
- Privacy Compliance: Make sure to disclose this tracking in your privacy policy—users have a right to know what data you’re collecting and why (it’s for account security, after all).
- Limit Data Retention: Don’t keep old session data forever—set a cleanup cron job to delete inactive sessions after 30-90 days.
- Avoid Overcollecting: Only store what you need for security purposes—no unnecessary data.
内容的提问来源于stack exchange,提问作者Rahul

