immudb Blog Posts

The Power of Immutable Storage for JSON Files: Enhancing Data Integrity with vault.immudb.io

Written by blog | Jun 24, 2024 6:20:51 AM

Data breaches and tampering are everywhere, and developers constantly grapple with data security, particularly when handling JSON files that often contain sensitive or critical information. Some use cases require the data to be stored in a cloud database that is accessible from anywhere, like in mobile-oriented applications that need to store data outside the device.

However, traditional cloud databases lack robust mechanisms to ensure data remains untampered over its lifecycle. This is where vault.immudb.io, a cloud database based on the immudb immutable database, comes into play. Immudb provides cryptographic proof on retrieval that the data has not been tampered with. With vault.immudb.io, the full capabilities of immudb are available in the cloud as a free offering for developers, for the first data bucket.

This guide will walk you through how to store and retrieve JSON files securely using vault.immudb.io, following the steps provided in the Quickstart guide.

Accessing vault.immudb.io

Sign Up and Log In: At vault.immudb.io, you can create an account if you don’t already have one. Log in to access your dashboard.

Create a Vault: From your dashboard, create a new vault instance. This will give you the necessary credentials and connection details to interact with your vault.

Storing JSON Data in vault.immudb.io

Use the following Python script to store JSON data in your vault:

import requests

headers = {
    'accept': 'application/json',
    'X-API-Key': 'default.#####.###-7rfQlxGI9ja',
    'Content-Type': 'application/json',
}

json_data = {
    'name': 'John Doe',
    'id': 1,
    'timestamp': '2023-05-10T12:00:00Z',
    'email': 'johndoe@example.com',
    'age': 30,
}

response = requests.put(
    'https://vault.immudb.io/ics/api/v1/ledger/default/collection/default/document',
    headers=headers,
    json=json_data,
)

If you use Apple’s Swift language instead, do this:

import Foundation
let jsonData = [
    "name": "John Doe",
    "id": 1,
    "timestamp": "2023-05-10T12:00:00Z",
    "email": "johndoe@example.com",
    "age": 30
] as [String : Any]
let data = try! JSONSerialization.data(withJSONObject: jsonData, options: [])
// let data = "{\n    \"name\": \"John Doe\",\n    \"id\": 1,\n    \"timestamp\": \"2023-05-10T12:00:00Z\",\n    \"email\": \"johndoe@example.com\",\n    \"age\": 30\n  }".data(using: .utf8)
let url = URL(string: "https://vault.immudb.io/ics/api/v1/ledger/default/collection/default/document")!
let headers = [
    "accept": "application/json",
    "X-API-Key": "default.#####.###-7rfQlxGI9ja",
    "Content-Type": "application/json"
]
var request = URLRequest(url: url)
request.httpMethod = "PUT"
request.allHTTPHeaderFields = headers
request.httpBody = data as Data

let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
    if let error = error {
        print(error)
    } else if let data = data {
        let str = String(data: data, encoding: .utf8)
        print(str ?? "")
    }
}
task.resume()

Retrieving JSON Data

When it’s time to retrieve your data, you can use this code to query the JSON data stored in your vault:

import requests
headers = {
    'accept': 'application/json',
    'X-API-Key': 'defaultro.#####.####',
    'Content-Type': 'application/json',
}
json_data = {
    'page': 1,
    'perPage': 100,
}
response = requests.post(
    'https://vault.immudb.io/ics/api/v1/ledger/default/collection/default/documents/search',
    headers=headers,
    json=json_data,
)

And here is the Swift version:

import Foundation
let jsonData = [
    "page": 1,
    "perPage": 100
] as [String : Any]
let data = try! JSONSerialization.data(withJSONObject: jsonData, options: [])

let url = URL(string: "https://vault.immudb.io/ics/api/v1/ledger/default/collection/default/documents/search")!
let headers = [
    "accept": "application/json",
    "X-API-Key": "defaultro.#####.####",
    "Content-Type": "application/json"
]

var request = URLRequest(url: url)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = data as Data

let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
    if let error = error {
        print(error)
    } else if let data = data {
        let str = String(data: data, encoding: .utf8)
        print(str ?? "")
    }
}
task.resume()

Verifying Data Integrity

One of the key features of vault.immudb.io is the ability to provide cryptographic proof that the data has not been tampered with. This is achieved through cryptographic hashing and Merkle tree structures inside the database, but these complex structures are abstracted from you, the developer. Each entry in the database is cryptographically linked to the previous one, making it impossible to alter any record without breaking the chain. To verify data integrity, you retrieve the transaction and use the hash to ensure the data matches what was originally stored.

Here’s a Python program to verify the data integrity:

import requests
import base64
import hashlib

def sha256_hash(data: bytes) -> bytes:
    sha256_hash = hashlib.sha256()
    sha256_hash.update(data)

    return base64.b64encode(sha256_hash.digest()).decode('utf-8')

headers = {
    'accept': '*/*',
    'Content-Type': 'application/json',
    'X-API-Key': '<Your API KEY>',
}

# retrieve the dual (consistency + merkle tree) proof for a specific document

response = requests.post(
    'https://vault.immudb.io/ics/api/v1/ledger/default/collection/default/document/<documentId>/proof',
    json={"proofSinceTransactionId": 0, "transactionId": 0},
    headers=headers,
)

if response.status_code == 200:
    data = response.json()
    encodedDoc = base64.b64decode(data['encodedDocument'])

    # For the sake of simplicity, we only perform a simple hash check against the document provided by the server.
    # Real world code requires proper validation of the merkle tree and consistency proofs.

    if data['verifiableTx']['tx']['entries'][0]['hValue'] == sha256_hash(encodedDoc):
        print('transaction has been validated successfully')
    else:
        print('validation failed')
else:
    print('error retrieving data')

And, the Swift code to verify the authenticity of your data:

import Foundation
import CommonCrypto
func sha256Hash(data: Data) -> String {
    var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
    data.withUnsafeBytes {
        _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash)
    }
    let hashData = Data(hash)
    return hashData.base64EncodedString()
}
let headers = [
    "accept": "*/*",
    "Content-Type": "application/json",
    "X-API-Key": "<Your API KEY>"
]
let parameters: [String: Any] = [
    "proofSinceTransactionId": 0,
    "transactionId": 0
]
guard let url = URL(string: "https://vault.immudb.io/ics/api/v1/ledger/default/collection/default/document/<documentId>/proof") else {
    print("Invalid URL")
    return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
do {
    request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: [])
} catch {
    print("Error serializing JSON: \(error)")
    return
}
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error in
    if let error = error {
        print("Error retrieving data: \(error)")
        return
    }
    guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
        print("Error retrieving data")
        return
    }
    guard let data = data else {
        print("No data received")
        return
    }
    do {
        if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
           let encodedDocument = json["encodedDocument"] as? String,
           let verifiableTx = json["verifiableTx"] as? [String: Any],
           let tx = verifiableTx["tx"] as? [String: Any],
           let entries = tx["entries"] as? [[String: Any]],
           let hValue = entries[0]["hValue"] as? String {
            
            if let documentData = Data(base64Encoded: encodedDocument) {
                if hValue == sha256Hash(data: documentData) {
                    print("Transaction has been validated successfully")
                } else {
                    print("Validation failed")
                }
            } else {
                print("Error decoding base64 encoded document")
            }
        } else {
            print("Error parsing JSON")
        }
    } catch {
        print("Error parsing JSON: \(error)")
    }
}
task.resume()

The above boilerplate programs show you how to store data in vault.immudb.io, query it again, and get cryptographic proof that your data has not been tampered with. By following these steps, you can securely store and retrieve JSON files using vault.immudb.io, ensuring data integrity and protection against tampering. Vault.immudb.io's robust cryptographic proof mechanisms provide developers with a reliable way to manage sensitive data, making it an essential tool for modern, secure application development.