User IP Hashes are now salted with 24 bits from a cryptographically secure random generation function formatted into base 64 for extra privacy/security.

This commit is contained in:
rainbow napkin 2025-11-03 19:07:38 -05:00
parent 75301ec7d9
commit ade2a4210d
4 changed files with 32 additions and 14 deletions

View file

@ -132,7 +132,6 @@ class chatPreprocessor{
commandObj.links = []; commandObj.links = [];
//For each link sent from the client //For each link sent from the client
//this.rawData.links.forEach((link) => {
for (const link of commandObj.rawData.links){ for (const link of commandObj.rawData.links){
//Add a marked link object to our links array //Add a marked link object to our links array
commandObj.links.push(await linkUtils.markLink(link)); commandObj.links.push(await linkUtils.markLink(link));

View file

@ -73,8 +73,6 @@ const userBanSchema = new mongoose.Schema({
* @returns {Mongoose.Document} Found ban Document if one exists. * @returns {Mongoose.Document} Found ban Document if one exists.
*/ */
userBanSchema.statics.checkBanByIP = async function(ip){ userBanSchema.statics.checkBanByIP = async function(ip){
//Get hash of ip
const ipHash = hashUtil.hashIP(ip);
//Get all bans //Get all bans
const banDB = await this.find({}); const banDB = await this.find({});
//Create null variable to hold any found ban //Create null variable to hold any found ban
@ -106,7 +104,7 @@ userBanSchema.statics.checkBanByIP = async function(ip){
const curHash = ban.ips.hashed[ipIndex]; const curHash = ban.ips.hashed[ipIndex];
//Check the current hash against the given hash //Check the current hash against the given hash
if(ipHash == curHash){ if(hashUtil.compareIPHash(ip, curHash)){
//If it matches we found the ban //If it matches we found the ban
foundBan = ban; foundBan = ban;

View file

@ -757,8 +757,6 @@ userSchema.methods.tattooIPRecord = async function(ip){
lastLog: new Date() lastLog: new Date()
}; };
//We should really start using for loops and stop acting like its 2008
//Though to be quite honest this bit would be particularly brutal without them
//For every user in the userlist //For every user in the userlist
for(let curUser of users){ for(let curUser of users){
//Ensure we're not checking the user against itself //Ensure we're not checking the user against itself
@ -766,7 +764,7 @@ userSchema.methods.tattooIPRecord = async function(ip){
//For every IP record in the current user //For every IP record in the current user
for(let curRecord of curUser.recentIPs){ for(let curRecord of curUser.recentIPs){
//If it matches the current ipHash //If it matches the current ipHash
if(curRecord.ipHash == ipHash){ if(hashUtil.compareIPHash(ip, curRecord.ipHash)){
//Check if we've already marked the user as an alt //Check if we've already marked the user as an alt
const foundAlt = this.alts.indexOf(curUser._id); const foundAlt = this.alts.indexOf(curUser._id);
@ -803,7 +801,7 @@ userSchema.methods.tattooIPRecord = async function(ip){
//Look for matching ip record //Look for matching ip record
function checkHash(ipRecord){ function checkHash(ipRecord){
//return matching records //return matching records
return ipRecord.ipHash == ipHash; return hashUtil.compareIPHash(ip, ipRecord.ipHash);
} }
} }

View file

@ -60,17 +60,40 @@ module.exports.compareLegacyPassword = function(pass, hash){
* *
* Provides a basic level of privacy by only logging salted hashes of IP's * Provides a basic level of privacy by only logging salted hashes of IP's
* @param {String} ip - IP to hash * @param {String} ip - IP to hash
* @returns {String} Hashed/Peppered IP Adress * @param {String} salt - (optional) string to salt IP with, leave empty to default to a securely generated string encoded in base64
* @returns {String} Hashed/Peppered/Salted IP Address
*/ */
module.exports.hashIP = function(ip){ module.exports.hashIP= function(ip, salt){
//Create hash object //Create hash object
const hashObj = crypto.createHash('sha512'); const hashObj = crypto.createHash('sha512');
//add IP and pepper to the hash //If we wheren't provided salt
hashObj.update(`${ip}${config.secrets.ipSecret}`); if(salt == null){
//Generate salt with cryptographically secure rng function
const rawSalt = crypto.randomBytes(24);
//Convert generated salt to base64
salt = rawSalt.toString('base64');
}
//return the IP hash as a string //Generate new salted hash
return hashObj.digest('hex'); hashObj.update(`${ip}${config.secrets.ipSecret}${salt}`);
//Convert hash data into a base64 string
const hash = hashObj.digest('base64');
//Return salty hash
return `${salt}$${hash}`;
}
module.exports.compareIPHash = function(ip, hash){
//Split hash by salt delimiter
const splitHash = hash.split("$");
//Re-generate hash from received plaintext IP and salt scraped from existing hash
const tempHash = module.exports.hashIP(ip, splitHash[0]);
//If the hash we calculates matches the original
return tempHash == hash;
} }
/** /**