Files
evidencija-rezija/app/lib/shareChecksum.ts
Knee Cola a6ab35a959 feat: add core security utilities for checksum-based share links
- Add HMAC-SHA256 checksum generation and validation (shareChecksum.ts)
- Add PDF magic bytes validation to prevent file spoofing (pdfValidator.ts)
- Add IP-based rate limiting for upload abuse prevention (uploadRateLimiter.ts)
- Update BillingLocation interface with shareTTL and shareFirstVisitedAt fields
- Add environment variables for share link security and TTL configuration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-08 00:14:20 +01:00

53 lines
1.4 KiB
TypeScript

import crypto from 'crypto';
/**
* Generate share link checksum for location
* Uses HMAC-SHA256 for cryptographic integrity
*
* SECURITY: Prevents location ID enumeration while allowing stateless validation
*/
export function generateShareChecksum(locationId: string): string {
const secret = process.env.SHARE_LINK_SECRET;
if (!secret) {
throw new Error('SHARE_LINK_SECRET environment variable not configured');
}
return crypto
.createHmac('sha256', secret)
.update(locationId)
.digest('hex')
.substring(0, 16); // 64 bits of entropy (sufficient for share links)
}
/**
* Validate share link checksum
* Uses constant-time comparison to prevent timing attacks
*
* @param locationId - The location ID from URL
* @param providedChecksum - The checksum from URL
* @returns true if checksum is valid
*/
export function validateShareChecksum(
locationId: string,
providedChecksum: string
): boolean {
try {
const expectedChecksum = generateShareChecksum(locationId);
// Convert to buffers for timing-safe comparison
const expected = Buffer.from(expectedChecksum);
const provided = Buffer.from(providedChecksum);
// Length check (prevents timing attack on different lengths)
if (expected.length !== provided.length) {
return false;
}
// Constant-time comparison (prevents timing attacks)
return crypto.timingSafeEqual(expected, provided);
} catch {
return false;
}
}