Hashing Guide

Hashing Guide

FraudHosting only accepts hashed identifiers. Never send raw PII—hash it locally using the official algorithm described below.

Algorithm Specification

  1. Pre-process the value:
    • Trim whitespace.
    • For most fields: remove internal spaces and lowercase the value.
    • For passwords or intentionally case-sensitive fields, skip the lowercase/strip step.
  2. Prefix with "fraudhosting-".
  3. Apply SHA-1.
  4. Repeat steps 2–3 a total of 32,000 times.

Pseudocode:

function fraudHostingHash(value, preserveOriginal = false) {
  let prepared = value.trim();
  if (!preserveOriginal) {
    prepared = prepared.replace(/\s+/g, '').toLowerCase();
  }
  for (let i = 0; i < 32000; i++) {
    prepared = sha1('fraudhosting-' + prepared);
  }
  return prepared;
}

Field Guidelines

FieldPre-processingNotes
namelowercase, remove spacesCombine first+last name before hashing.
emaillowercase, remove spacesProvide variants (email2, email3, …) if needed.
iplowercase (convert IPv6 to canonical form)Keep dots/colons; do not strip them.
phoneremove spaces, keep punctuationConsider normalising + country code.
passwordpreserve originalNo lowercasing or space removal.
domainlowercase, remove protocolHash bare domain or subdomain.

Variable names must be 1–16 characters (a-z, optional digits at the end, dash allowed). Append suffixes (email2, email3) for multiple values with the same key.

Reference Implementations

function fraudhosting_hash(string $value, bool $preserveOriginal = false): string
{
  $prepared = trim($value);
  if (! $preserveOriginal) {
      $prepared = strtolower(str_replace(' ', '', $prepared));
  }

  for ($i = 0; $i < 32000; $i++) {
      $prepared = sha1('fraudhosting-' . $prepared);
  }

  return $prepared;
}
import hashlib

def fraudhosting_hash(value: str, preserve_original: bool = False) -> str:
  prepared = value.strip()
  if not preserve_original:
      prepared = prepared.replace(" ", "").lower()

  for _ in range(32000):
      prepared = hashlib.sha1(f"fraudhosting-{prepared}".encode()).hexdigest()
  return prepared
#include <algorithm>
#include <cctype>
#include <openssl/sha.h>
#include <iomanip>
#include <sstream>
#include <string>

std::string sha1_hex(const std::string& input)
{
  unsigned char hash[SHA_DIGEST_LENGTH];
  SHA1(reinterpret_cast<const unsigned char*>(input.c_str()), input.size(), hash);

  std::ostringstream oss;
  for (unsigned char c : hash) {
      oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(c);
  }
  return oss.str();
}

std::string fraudhosting_hash(std::string value, bool preserveOriginal = false)
{
  if (!preserveOriginal) {
      // trim
      value.erase(0, value.find_first_not_of(" \t\n\r"));
      value.erase(value.find_last_not_of(" \t\n\r") + 1);
      // remove spaces
      value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());
      // lowercase
      std::transform(value.begin(), value.end(), value.begin(), ::tolower);
  }

  for (int i = 0; i < 32000; ++i) {
      value = sha1_hex("fraudhosting-" + value);
  }
  return value;
}
package fraudhash

import (
	"crypto/sha1"
	"encoding/hex"
	"strings"
)

func Hash(value string, preserveOriginal bool) string {
	prepared := strings.TrimSpace(value)
	if !preserveOriginal {
		prepared = strings.ReplaceAll(prepared, " ", "")
		prepared = strings.ToLower(prepared)
	}

	for i := 0; i < 32000; i++ {
		h := sha1.Sum([]byte("fraudhosting-" + prepared))
		prepared = hex.EncodeToString(h[:])
	}

	return prepared
}

Worked Example

Input             -> alan.ross@example.com
Normalise         -> alan.ross@example.com (already lowercase, no spaces)
Hash iterations   -> apply salted SHA-1 loop 32,000 times
Result            -> 34efd0a968b48cbf9a43ac3e73053e4f34323400

Keep the hashing library consistent across languages to avoid subtle differences (especially in Unicode handling). Test conversions once and cache hashes locally to avoid repeated 32k iteration runs where performance matters.