/ opencodehub / src / lib / storage-auth.ts
swadhinbiswas feat: implement production-grade security and fix critical vulnerabilities · 1/1/2026
0c17327
65 lines · 1.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
 * Storage Authentication Utilities
 * HMAC-based signature verification for secure storage access
 */

import crypto from "crypto";

const STORAGE_SECRET = import.meta.env.STORAGE_SECRET || crypto.randomBytes(32).toString("hex");

/**
 * Generate a signed URL for storage access
 */
export function generateStorageSignature(key: string, expiresInSeconds: number = 3600): { signature: string; expires: string } {
    const expires = Math.floor(Date.now() / 1000) + expiresInSeconds;
    const expiresStr = expires.toString();

    const data = `${key}:${expiresStr}`;
    const signature = crypto
        .createHmac("sha256", STORAGE_SECRET)
        .update(data)
        .digest("hex");

    return { signature, expires: expiresStr };
}

/**
 * Verify a storage access signature
 */
export function verifyStorageSignature(key: string, signature: string | null, expires: string | null): boolean {
    // If no secret configured, allow public access (development mode)
    if (!import.meta.env.STORAGE_SECRET) {
        return true;
    }

    if (!signature || !expires) {
        return false;
    }

    // Check expiration
    const expiresTime = parseInt(expires, 10);
    if (isNaN(expiresTime) || Date.now() / 1000 > expiresTime) {
        return false;
    }

    // Verify signature
    const data = `${key}:${expires}`;
    const expectedSignature = crypto
        .createHmac("sha256", STORAGE_SECRET)
        .update(data)
        .digest("hex");

    return crypto.timingSafeEqual(
        Buffer.from(signature, "hex"),
        Buffer.from(expectedSignature, "hex")
    );
}

/**
 * Generate a signed storage URL
 */
export function getSignedStorageUrl(key: string, baseUrl: string = "", expiresIn: number = 3600): string {
    const { signature, expires } = generateStorageSignature(key, expiresIn);
    return `${baseUrl}/api/storage/${encodeURIComponent(key)}?sig=${signature}&exp=${expires}`;
}