/ src / utils / ipHasher.js
ipHasher.js
 1  import crypto from "crypto";
 2  import config from "@/config.js";
 3  import { logger } from "@/middleware/logging.js";
 4  
 5  const IpHasherUtils = {
 6    hashUserId: (userId) => {
 7      if (!userId) {
 8        return "";
 9      }
10      return crypto.createHash("sha256").update(userId + config.security.auth.hashSalt).digest("hex").substring(0, 16);
11    },
12  
13    hashIpAddress: (ip, userId) => {
14      if (!ip || typeof ip !== "string") {
15        return null;
16      }
17  
18      const normalizedIp = ip.trim().toLowerCase();
19      const hashedUserId = IpHasherUtils.hashUserId(userId);
20      const salt = `${config.security.auth.hashSalt}-${hashedUserId}`;
21      const hash = crypto.createHash("sha256")
22        .update(normalizedIp + salt)
23        .digest("hex");
24  
25      return hash;
26    },
27  
28    checkIpHashAbuse: async (ipHash, maxSessions = null) => {
29      if (!ipHash || typeof ipHash !== "string") {
30        return { count: 0, exceedsLimit: false };
31      }
32  
33      const limit = maxSessions ?? config.security.maxSessionsPerIp;
34  
35      try {
36        const { User } = await import("@/models/User.js");
37        const count = await User.countDocuments({ lastIpHash: ipHash }).maxTimeMS(config.timeouts.medium);
38        return { count, exceedsLimit: count > limit };
39      } catch (error) {
40        logger.error("Error checking IP hash abuse", { error: error.message, ipHash: ipHash.substring(0, 8) + "..." });
41        return { count: 0, exceedsLimit: false, error: true };
42      }
43    },
44  };
45  
46  export { IpHasherUtils };
47  export const hashUserId = IpHasherUtils.hashUserId;
48  export const hashIpAddress = IpHasherUtils.hashIpAddress;
49  export const checkIpHashAbuse = IpHasherUtils.checkIpHashAbuse;