Node.js RESTful API安全咨询:Unity3D游戏玩家信息收集场景
Hey there! Let's walk through a practical, robust security plan for your Unity3D game's API—since you already know your way around Node.js servers and have a basic grasp of JWT, we'll build directly on that foundation.
1. Lock Down Data Transmission with HTTPS
First things first: never send player data over plain HTTP. That’s equivalent to shouting sensitive info like phone numbers across a crowded room—anyone listening in can intercept it.
- For your Node.js server, set up HTTPS using a free SSL certificate (from Let’s Encrypt, for example). Here’s a quick Express example:
const https = require('https'); const fs = require('fs'); const express = require('express'); const app = express(); const sslOptions = { key: fs.readFileSync('/path/to/private-key.pem'), cert: fs.readFileSync('/path/to/certificate.pem') }; https.createServer(sslOptions, app).listen(443); - In your Unity code, make sure all API calls use
https://instead ofhttp://.
2. Use JWT to Validate Legitimate Game Requests
JWT isn’t just for user logins—you can use it to ensure requests are coming from your actual Unity game, not random bots or malicious scripts. Here’s how to implement it properly:
Step 1: Issue a JWT to Your Unity Game
- When your Unity game starts up, have it send a request to your Node server with a secret game ID (store this ID in Unity’s PlayerSettings as an encrypted string, not hard-coded!).
- Your Node server generates a short-lived JWT (e.g., 1 hour expiry) using a strong secret key (store this in environment variables, never in your code!):
const jwt = require('jsonwebtoken'); app.get('/get-game-token', (req, res) => { const gameId = req.headers['x-game-id']; if (gameId !== process.env.UNITY_GAME_ID) { return res.status(401).send('Invalid game credentials'); } const token = jwt.sign({ gameId }, process.env.JWT_SECRET, { expiresIn: '1h' }); res.json({ token }); });
Step 2: Validate JWT on Data Submission
- Unity includes the JWT in the
Authorizationheader of every player data submission request:// Unity C# example var request = new UnityWebRequest("https://your-api.com/submit-player-data", "POST"); request.SetRequestHeader("Authorization", $"Bearer {gameToken}"); - Your Node server verifies the token before processing any data:
app.post('/submit-player-data', (req, res) => { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).send('Unauthorized: No token provided'); } const token = authHeader.split(' ')[1]; jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => { if (err) { return res.status(403).send('Unauthorized: Invalid or expired token'); } // Proceed to handle player data }); }); - Important: Don’t put sensitive data (like player info) in the JWT payload—it’s Base64-encoded, not encrypted, so anyone can decode it.
3. Validate and Sanitize Player Input
Never trust raw input from players—malicious users could send invalid data, SQL injection attacks, or other junk. Use a validation library like express-validator to clean and check inputs:
const { body, validationResult } = require('express-validator'); app.post('/submit-player-data', [ // Validate and sanitize name: trim whitespace, escape special chars, limit length body('name').trim().escape().isLength({ min: 1, max: 50 }).withMessage('Name must be 1-50 characters'), // Validate phone number format (adjust for your region, e.g., 'zh-CN' for China) body('phone').trim().isMobilePhone('zh-CN').withMessage('Invalid phone number') ], (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } // Input is safe—process and store it });
- For database operations, always use parameterized queries (e.g., with Sequelize, Knex, or PostgreSQL’s
pglibrary) to avoid SQL injection.
4. Prevent Abuse with Rate Limiting
Stop bots from spamming your API with fake data by limiting how many requests a single client can make in a window. Use the express-rate-limit package:
const rateLimit = require('express-rate-limit'); const submissionLimiter = rateLimit({ windowMs: 60 * 1000, // 1 minute max: 5, // Allow 5 requests per minute message: 'Too many submissions—please try again later.' }); app.use('/submit-player-data', submissionLimiter);
5. Encrypt Sensitive Data Before Storing
Never store phone numbers or other sensitive info in plain text in your database. Use AES-256 encryption to encrypt data before saving it (you’ll need the key to decrypt it later if needed):
const crypto = require('crypto'); const algorithm = 'aes-256-cbc'; const encryptionKey = Buffer.from(process.env.ENCRYPTION_KEY, 'hex'); // 32-byte key function encrypt(text) { const iv = crypto.randomBytes(16); // Initialization vector const cipher = crypto.createCipheriv(algorithm, encryptionKey, iv); let encrypted = cipher.update(text); encrypted = Buffer.concat([encrypted, cipher.final()]); return { iv: iv.toString('hex'), data: encrypted.toString('hex') }; } // When storing player data: const encryptedPhone = encrypt(req.body.phone); // Save encryptedPhone.iv and encryptedPhone.data to your database
- Store your encryption key in environment variables, not in code.
6. Restrict CORS (If Applicable)
If your API is only meant to be accessed by your Unity game, limit Cross-Origin Resource Sharing (CORS) to trusted origins. Use the cors package:
const cors = require('cors'); app.use(cors({ origin: ['https://your-unity-game-domain.com'], // Adjust to your game's domain credentials: true }));
- Note: For standalone Unity games, the client origin might be dynamic, so JWT validation is still your primary line of defense here.
7. Log and Monitor Activity
Keep track of API requests, errors, and suspicious activity to catch issues early. Use a logging library like winston to write structured logs:
const winston = require('winston'); const logger = winston.createLogger({ level: 'info', format: winston.format.json(), transports: [new winston.transports.File({ filename: 'api-logs.log' })] }); // Log successful submissions logger.info('Player data submitted', { playerName: req.body.name }); // Log errors logger.error('Invalid JWT', { error: err.message });
That’s a complete, practical security setup tailored to your use case. Start with HTTPS and JWT validation—those are your biggest wins—then add the other layers as you go.
内容的提问来源于stack exchange,提问作者ATHellboy

