DEV Community

Anslem Owhofasa
Anslem Owhofasa

Posted on Originally published at anslem.hashnode.dev on

Why exporting support tickets is a security risk and how to fix it local first

When a critical bug, billing dispute, or compliance issue happens, someone on the team inevitably needs to export a support conversation. A security engineer might need the raw thread to investigate a report, or a customer might request an official copy of their history.

In most help desks, exporting a conversation means clicking a button that generates a plain text file, a PDF, or a raw JSON dump sent through email or a third party server.

That approach creates three quiet problems that most engineering teams ignore until a compliance audit forces them to deal with it.

1. Unchecked PII Exposure

Support threads are full of sensitive customer data: email addresses, phone numbers, IP logs, internal staff notes, and occasionally accidental API keys or passwords pasted by users. When you export a raw thread to a third party server or email attachment, you duplicate that sensitive data across unmonitored systems.

2. Zero Proof of Integrity

Standard text or PDF exports can be modified by anyone with a text editor. If a customer or an auditor asks for proof of what was said at a specific timestamp, a standard text file holds zero cryptographic proof. There is no easy way to verify if an attachment or message was altered after the fact.

3. Heavy Server to Server Overhead

Building server integrations between your help desk and external audit platforms usually requires storing API keys, maintaining webhook infrastructure, and paying for third party storage just to hold static logs.

The Local First Solution: Client Side Handoffs and Canonical Hashes

To solve this without adding heavy server infrastructure, we can shift the export workflow to be local first.

Instead of sending conversation data to an external server, the help desk hands the structured payload directly to a hosted web app via a postMessage handshake, the same-origin-safe way two browser tabs exchange data without either one hitting a server.

Here is how that workflow operates step by step:

  1. Structure the payload in the help desk. The help desk application extracts the conversation, attachments, and basic metadata, then formats it into a standardized schema.

  2. Generate cryptographic fingerprints. Instead of relying on file names, each attachment gets hashed with SHA-256 the moment it's packaged. The whole payload is then canonicalized (RFC 8785) and hashed again as a single envelope fingerprint, so both individual files and the record as a whole can be checked for tampering later.

  3. Perform a zero-server handoff. The help desk opens the evidence viewer in a new tab and the two tabs negotiate a handshake (readyevidenceimported). The payload moves directly between the two tabs; Tracepack's own server only ever serves static JavaScript and never sees the evidence itself.

  4. Review before saving. Because the data lives in the browser the whole time, the person handling the export can review the record and apply redactions locally before producing a hashed, portable evidence file.

What a Portable Evidence Schema Looks Like

To keep evidence files consistent across different help desks and tools, the underlying export should follow a simple, deterministic schema.

Here is an example of how a conversation thread converts into a portable record (the real tracepack-evidence v1 format):

{
  "schema_version": 1,
  "source": {
    "producer_id": "freescout-tracepack-module",
    "producer_name": "FreeScout",
    "producer_version": "1.0.0"
  },
  "capture_timestamp": "2026-08-18T10:00:00Z",
  "source_url": "https://support.example.com/conversation/1042",
  "evidence_type": "support_conversation",
  "attachments": [
    {
      "id": "att_01",
      "filename": "screenshot.png",
      "mime_type": "image/png",
      "size": 48213,
      "content_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
      "encoding": "base64",
      "data": "..."
    }
  ],
  "observations": [
    {
      "id": "obs_01",
      "kind": "message",
      "label": "Customer message",
      "detail": "Transaction timed out on checkout page.",
      "attachment_ref": "att_01"
    }
  ],
  "integrity": {
    "algorithm": "sha256",
    "canonicalization": "RFC8785",
    "payload_hash": "..."
  }
}

Enter fullscreen mode Exit fullscreen mode

A few things worth calling out about the real shape:

  • attachments[] and observations[] are kept separate rather than merged into one generic "records" list. Attachments are the files; observations are the structured claims about the conversation (a message, a note, a flagged detail).

  • There are two hashes, not one: a content_hash per attachment (raw file bytes), and a single integrity.payload_hash for the canonicalized envelope as a whole.

  • source.producer_name and source.producer_id are self-reported by whatever tool packaged the evidence. They are not cryptographically verified, so the export UI and any copy describing it should say "reported by," not "verified."

By standardizing this payload, any local tool can read the thread, verify the hashes, and allow safe redactions without breaking the historical record.

Why This Matters for Open Source Maintainers

Building local first workflows keeps systems lean. Help desk maintainers do not have to worry about storing customer data on third party servers or managing complex webhook queues. The entire operation happens right in the browser, tab to tab.

If you are interested in exploring how portable evidence packages work in practice, check out the open source tracepack-evidence v1 specification and CLI on GitHub. I built it to help developers and support teams handle local evidence files safely, and feedback or contributions are always welcome.

Top comments (0)