---
title: "Is There a Sample Script for Hash Verification?"
url: "https://dm0bkdes5q7a2ls.productfruits.help/en/article/3af26a7f-3242-49b8-98d8-f4606cf12558"
language: "en"
last_updated: "2026-04-30T14:16:15.952Z"
---

# Is There a Sample Script for Hash Verification?

To verify the integrity of your exported Spirion logs, you can use a Python script that generates a SHA-256 hash of the log file immediately after export and compares it against a previously stored "known-good" hash.

## Sample Python Script for Hash Generation and Verification

This sample Python script performs two functions:

1.  **Generate:** Creates a hash for a new export.
2.  **Verify:** Checks an existing file against a stored hash to detect tampering.

```py
import hashlib
import os

# --- CONFIGURATION ---
LOG_FILE = "spirion_audit_export.json"
HASH_STORE = "log_integrity_hashes.txt"

def calculate_sha256(file_path):
    """Calculates the SHA-256 hash of a file in chunks to save memory."""
    sha256_hash = hashlib.sha256()
    try:
        with open(file_path, "rb") as f:
            # Read file in 4KB chunks
            for byte_block in iter(lambda: f.read(4096), b""):
                sha256_hash.update(byte_block)
        return sha256_hash.hexdigest()
    except FileNotFoundError:
        return None

def record_new_hash(file_path):
    """Calculates and saves the hash for a new log export."""
    file_hash = calculate_sha256(file_path)
    if file_hash:
        with open(HASH_STORE, "a") as f:
            # Store filename and hash (standard sha256sum format)
            f.write(f"{file_hash}  {file_path}\n")
        print(f"Integrity record created for {file_path}")
        print(f"Hash: {file_hash}")
    else:
        print(f"Error: File {file_path} not found.")

def verify_integrity(file_path):
    """Compares current file hash against the last recorded hash."""
    current_hash = calculate_sha256(file_path)
    if not current_hash:
        print(f"Error: File {file_path} not found.")
        return False

    try:
        with open(HASH_STORE, "r") as f:
            lines = f.readlines()
            # Find the last entry for this specific file
            matching_hashes = [l for l in lines if file_path in l]
            if not matching_hashes:
                print(f"No integrity record found for {file_path}.")
                return False
            
            last_recorded_hash = matching_hashes[-1].split()[0]

        if current_hash == last_recorded_hash:
            print(f"VERIFICATION SUCCESS: {file_path} integrity is intact.")
            return True
        else:
            print(f"!!! VERIFICATION FAILURE !!!")
            print(f"Expected: {last_recorded_hash}")
            print(f"Actual:   {current_hash}")
            print(f"The file may have been tampered with or corrupted.")
            return False
    except FileNotFoundError:
        print(f"Error: Hash store {HASH_STORE} not found.")
        return False

if __name__ == "__main__":
    # Example Workflow:
    # 1. After your API export script runs, record the hash:
    # record_new_hash(LOG_FILE)
    
    # 2. Before your SIEM ingests or during an audit, verify it:
    verify_integrity(LOG_FILE)
```

### How to Operationalize This

1.  **Post-Export Hook:** Add the `record_new_hash()` function to the end of your Spirion API export script. This ensures every new file is "signed" immediately.
2.  **Pre-Ingestion Check:** Run `verify_integrity()` before your SIEM (like Splunk or Sentinel) pulls the data. If it fails, stop the ingestion and trigger a security alert.
3.  **Secure the Hash Store:** The `log_integrity_hashes.txt` file is your "Source of Truth." It must be stored on a different volume or server than the log files themselves to prevent an attacker from modifying both the log and the hash.

### Recommendations

-   **Use JSONL for Granularity:** If you use JSON Lines (one event per line), you can technically hash *each individual line* and include the hash as a field in the JSON object. This allows you to detect if a single event was deleted from the middle of a large file.
-   **Salt Your Hashes:** For high-security environments, add a "secret salt" (a random string) to the hash calculation. This prevents an attacker from simply re-calculating a valid hash if they modify the log file.
-   **Automate Alerts:** If `verify_integrity()` returns `False`, have your script send an automated email or create a high-priority ticket in **ServiceNow**. A failed integrity check is a strong indicator of unauthorized administrative activity.

### Summary

This script provides a simple but powerful way to ensure your Spirion log exports remain untampered.

By combining **SHA-256 hashing** with a **secure hash store**, you can provide auditors with mathematical proof that your sensitive data logs are accurate and complete.

## Follow up: Is it Possible to Add Salting to the Hash Script?

Adding a **salt** to your hashing process significantly increases security by ensuring that an attacker cannot simply re-calculate a valid hash if they modify the log file. Even if they know you are using SHA-256, they would also need to know your "secret salt" to produce a matching hash.

### Python Script for Hash Generation and Verification with Salting

In this version, we combine the file's content with a secret string before generating the final hash.

```
import hashlib
import os
# --- CONFIGURATION ---
LOG_FILE = "spirion_audit_export.json"
HASH_STORE = "log_integrity_hashes.txt"
# SECRET_SALT should be stored in a secure environment variable or vault.
# Never hardcode this in a production script.
SECRET_SALT = os.getenv("SPIRION_LOG_SALT", "YourSuperSecretSaltString123!")
def calculate_salted_sha256(file_path, salt):
    """Calculates a salted SHA-256 hash of a file."""
    sha256_hash = hashlib.sha256()
    # 1. Start the hash with the secret salt
    sha256_hash.update(salt.encode('utf-8'))
    try:
        with open(file_path, "rb") as f:
            # 2. Update the hash with the file content in chunks
            for byte_block in iter(lambda: f.read(4096), b""):
                sha256_hash.update(byte_block)
        # 3. Optional: Update the hash with the salt again at the end (Double Salting)
        sha256_hash.update(salt.encode('utf-8'))
        return sha256_hash.hexdigest()
    except FileNotFoundError:
        return None
def record_salted_hash(file_path):
    """Calculates and saves the salted hash for a new log export."""
    file_hash = calculate_salted_sha256(file_path, SECRET_SALT)
    if file_hash:
        with open(HASH_STORE, "a") as f:
            f.write(f"{file_hash}  {file_path}\n")
        print(f"Salted integrity record created for {file_path}")
    else:
        print(f"Error: File {file_path} not found.")
def verify_salted_integrity(file_path):
    """Verifies the file against the stored salted hash."""
    current_hash = calculate_salted_sha256(file_path, SECRET_SALT)
    if not current_hash:
        return False
    try:
        with open(HASH_STORE, "r") as f:
            lines = f.readlines()
            matching_hashes = [l for l in lines if file_path in l]
            if not matching_hashes:
                print(f"No record found for {file_path}.")
                return False
            last_recorded_hash = matching_hashes[-1].split()[0]
        if current_hash == last_recorded_hash:
            print(f"SUCCESS: Salted integrity intact for {file_path}.")
            return True
        else:
            print(f"!!! ALERT: Salted integrity check FAILED for {file_path} !!!")
            return False
    except FileNotFoundError:
        print(f"Error: Hash store {HASH_STORE} not found.")
        return False
if __name__ == "__main__":
    # record_salted_hash(LOG_FILE)
    verify_salted_integrity(LOG_FILE)
```

### Why Salting Matters for Spirion Logs

1.  **Prevents "Pre-Computed" Attacks:** An attacker cannot use a "Rainbow Table" (a list of pre-calculated hashes for common log patterns) to guess your log content.
2.  **Authentication of the Exporter:** Because only your export script and your verification script know the `SECRET_SALT`, the hash acts as a simple **Message Authentication Code (MAC)**. It proves the file was generated by *your* script and not an external actor.
3.  **Protection Against "Hash Swapping":** If an attacker modifies the log file, they cannot simply run `sha256sum` on the new file and update your `HASH_STORE` because they don't have the salt required to generate the *correct* hash.

### Best Practices for Salting

-   **Environment Variables:** As shown in the script, use `os.getenv("SPIRION_LOG_SALT")`. This keeps the salt out of your source code repository (Git/Bitbucket).
-   **Salt Length:** Use a salt that is at least 16–32 characters long and includes a mix of letters, numbers, and symbols.
-   **Unique Salts per Environment:** Use a different salt for your Production, Staging, and Dev environments. This ensures that a compromise in a lower environment doesn't affect your production log integrity.
-   **Rotate the Salt:** If you suspect your export server has been compromised, rotate the salt immediately. Note that this will make all *previous* hashes in your `HASH_STORE` invalid for verification, so keep a record of old salts if you need to verify historical logs.

### Summary

Adding a salt to your hashing script transforms a simple integrity check into a robust security control. It ensures that your Spirion log exports are not only **unchanged** but also **authenticated** as coming from your trusted export process.
