Confirm the integrity of your Salesforce backup data using a cryptographic hash stored on a public ledger. Using blockchain verification helps you meet compliance requirements and prevent fraud by maintaining a time-stamped audit trail.
- Navigate to the Backup History.
- Select the backup to verify.
- Click Integrity details.
-
Click Download zip file.
A Backup Export job will begin.
- When the job is complete, click the Click here to download link to download the backup files.
- Sort all files in the backup in ascending order.
-
Hash each file with the SHA-256 algorithm.
Example of the hashed file:
8c4bdaf46e3764ce4b53bdc9e410d5b6d89fa586bfb418fc572f7b4cedf2d93a -
Concatenate the hashes with their object name, separated by a semi-colon (;).
Then concatenate all the hashes, each separated by a line break (ASCII 10),
without white spaces. Make sure the final line ends with a line break. The
concatenated string must be encoded with UTF-8.
Example of the hashed file:
8c4bdaf46e3764ce4b53bdc9e410d5b6d89fa586bfb418fc572f7b4cedf2d93a;accepted_event_relations.csv.gz dd27948844ce32d5510a4a2d7f1f054c58248e3e754f81330a3a7f33c5d2e6c2;account_brand_shares.csv.gz 2cfa4d7c8d830ccf5a1f9c37d5f1a705d4573269aa6acc9468eda0d99af22842;account_brands.csv.gz -
Hash the concatenated hashes with the SHA-256 algorithm.
The final hash should appear similar to the following example:
4c6d3f1f97e7a6001f9764f08b0b5d9e9a8602b2c5386b02a26c4dc1db749bbfThe following is an example hash script in Python.Note The following is meant to provide an example of how the script could be written and should not be used as-is.import hashlib import json import os import argparse LINE_SEP = "\n" BUF_SIZE = 65536 def hash_backup(directory_path): """ Hash all .csv.gz files in the specified directory. Args: directory_path (str): Path to directory containing backup files. Returns: str: JSON string containing the backup_hash """ # Check if directory exists if not os.path.exists(directory_path): raise FileNotFoundError(f"Directory not found: {directory_path}") if not os.path.isdir(directory_path): raise NotADirectoryError(f"Path is not a directory: {directory_path}") hashes = [] # Local variable instead of global try: for filename in sorted( os.listdir(directory_path) ): # loops over each file in directory (backup folder), in ascending order if filename.endswith(".csv.gz"): # only hashes the object (.csv.gz) files file_path = os.path.join(directory_path, filename) sha = hashlib.sha256() try: with open(file_path, "rb") as f: while True: # reads the file in BUF_SIZE buffers data = f.read(BUF_SIZE) if not data: break # hashes the file with the SHA-256 algorithm sha.update(data) hashes.append( "{} {}".format(sha.hexdigest(), filename) ) # adds the hash and object name to the hashes list except (IOError, OSError) as e: print(f"Warning: Could not read file {filename}: {e}") continue if not hashes: raise ValueError(f"No .csv.gz files found in directory: {directory_path}") hashes_string = ( LINE_SEP.join(hashes) + LINE_SEP ) # concatenates all the hashes in the hashes list # Replace all double spaces with semicolons (more robust) encoded = hashes_string.replace(" ", ";").encode("utf-8") final_hash = hashlib.sha256( encoded ).hexdigest() # hashes the concatenated hashes with the SHA-256 algorithm return json.dumps({"backup_hash": final_hash}) except Exception as e: raise RuntimeError(f"Error processing directory {directory_path}: {e}") def main(): parser = argparse.ArgumentParser(description="Hash all .csv.gz files in a directory") parser.add_argument("-f", "--file-directory", required=True, help="Directory containing .csv.gz files to hash") args = parser.parse_args() try: result = hash_backup(args.file_directory) print(f"backup hash: {result}") except Exception as e: print(f"Error: {e}") exit(1) if __name__ == "__main__": main()