We release security updates for the following versions:
| Version | Supported |
|---|---|
| 1.0.x | ✅ |
The default shuffle implementation now uses cryptographically secure random numbers when available.
However, if you're deploying for real-money poker games, you MUST understand these security considerations:
import { randomBytes } from "crypto";
import { PokerEngine } from "@pokertools/engine";
// Cryptographically secure RNG
const secureRng = () => {
const buffer = randomBytes(4);
return buffer.readUInt32BE(0) / 0x100000000;
};
const engine = new PokerEngine({
smallBlind: 10,
bigBlind: 20,
randomProvider: secureRng, // Always provide this for production
});// ❌ NEVER DO THIS IN PRODUCTION
const engine = new PokerEngine({
smallBlind: 10,
bigBlind: 20,
// Using default without providing randomProvider
// Falls back to Math.random() in browser environments
});Math.random() uses a predictable pseudo-random algorithm:
- State is guessable - Given enough observations, attackers can predict future cards
- Seed extraction - Browser implementations leak seed via timing attacks
- Not cryptographically secure - Designed for animations, not security
Real-world attack:
- Attacker observes 10-20 hands
- Reverse-engineers the PRNG state
- Predicts all future shuffles
- Knows everyone's hole cards
Use one of these methods:
import { randomBytes } from "crypto";
const secureRng = () => randomBytes(4).readUInt32BE(0) / 0x100000000;// Using hardware random number generator
const hardwareRng = () => {
// Your hardware RNG implementation
};import { createHash } from "crypto";
// Combine server seed + client seed for provably fair
function provablyFairRng(serverSeed: string, clientSeed: string, nonce: number) {
return () => {
const hash = createHash("sha256").update(`${serverSeed}:${clientSeed}:${nonce++}`).digest();
return hash.readUInt32BE(0) / 0x100000000;
};
}NEVER send full game state to clients:
// ❌ BAD: Exposes all hole cards and deck
socket.emit("gameState", engine.state);
// ✅ GOOD: Only send what player can see
const playerView = engine.view("playerId");
socket.emit("gameState", playerView);ALWAYS validate actions on the server:
// ✅ Server validates all actions
app.post("/action", (req, res) => {
try {
const action = req.body;
// Engine throws if action is illegal
engine.act(action);
res.json({ success: true });
} catch (error) {
if (error instanceof IllegalActionError) {
res.status(400).json({ error: error.message });
}
}
});Enable audit mode in production:
import { getInitialChips } from "@pokertools/engine";
// After every action
const totalChips = getInitialChips(engine.state);
if (totalChips !== EXPECTED_TOTAL) {
// CRITICAL ERROR: Freeze game immediately
logger.critical("Chip integrity violation!", {
expected: EXPECTED_TOTAL,
actual: totalChips,
gameId: game.id,
});
// Freeze table and alert admins
freezeTable(game.id);
}The engine uses integer-only arithmetic to prevent floating-point exploits:
// All currency values in smallest unit (cents/satoshis)
const engine = new PokerEngine({
smallBlind: 500, // $5.00 = 500 cents
bigBlind: 1000, // $10.00 = 1000 cents
});
// Never use floating point
const stack = 10000; // ✅ 10000 cents
const stack = 100.0; // ❌ NEVER do thisProtect against automated attacks:
import rateLimit from "express-rate-limit";
const actionLimiter = rateLimit({
windowMs: 1000, // 1 second
max: 5, // Max 5 actions per second per IP
message: "Too many actions, please slow down",
});
app.post("/action", actionLimiter, handleAction);Use secure session tokens:
import session from "express-session";
app.use(
session({
secret: process.env.SESSION_SECRET, // 256-bit random key
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // HTTPS only
httpOnly: true, // No JavaScript access
sameSite: "strict",
maxAge: 24 * 60 * 60 * 1000, // 24 hours
},
})
);DO NOT open a public issue for security vulnerabilities.
Instead:
-
Email:
aurelions@protonmail.com -
Include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if any)
-
Response Time:
- Initial response: Within 48 hours
- Status update: Within 7 days
- Fix timeline: Depends on severity
-
Critical (P0): RNG prediction, chip duplication
- Fix: Within 24 hours
- Bounty: Up to $5,000
-
High (P1): Card visibility, bypass validation
- Fix: Within 1 week
- Bounty: Up to $1,000
-
Medium (P2): Denial of service, timing attacks
- Fix: Within 2 weeks
- Bounty: Up to $500
-
Low (P3): Minor information disclosure
- Fix: Next release
- Bounty: Recognition in CHANGELOG
Before launching to production:
- Using cryptographically secure RNG
- Server-side validation enabled
- View masking implemented
- Chip conservation auditing active
- Rate limiting configured
- HTTPS only (no HTTP)
- Secure session management
- Database credentials encrypted
- Secrets in environment variables (not code)
- Logging enabled (but don't log sensitive data)
- Backup and disaster recovery tested
- Penetration testing completed
The evaluator uses lookup tables which have constant-time evaluation regardless of hand strength. However, network latency and server load can leak information.
Mitigation: Add random delay jitter to all responses:
const delay = Math.floor(Math.random() * 50); // 0-50ms jitter
await new Promise((resolve) => setTimeout(resolve, delay));The engine stores deck state in memory. If an attacker gains memory access, they can see the deck.
Mitigation:
- Use encrypted memory (TEE/SGX) for high-stakes games
- Rotate processes frequently
- Use process isolation
Without proper nonce/timestamp validation, attackers can replay old actions.
Mitigation:
// Add timestamp validation
if (Date.now() - action.timestamp > 5000) {
throw new Error("Action expired");
}
// Add nonce tracking
const processedNonces = new Set();
if (processedNonces.has(action.nonce)) {
throw new Error("Duplicate action");
}
processedNonces.add(action.nonce);Subscribe to security updates:
- GitHub Watch: Click "Watch" → "Custom" → "Security alerts"
- NPM:
npm auditwill detect known vulnerabilities - RSS: Subscribe to https://github.com/aaurelions/pokertools/releases.atom
This library follows:
- OWASP Top 10 secure coding practices
- CWE/SANS Top 25 software errors prevention
- PCI DSS requirements for real-money gaming (when configured correctly)
This security policy is part of the Pokertools project and follows the same MIT license.
Last Updated: 2025-11-29 Version: 1.0.0