email attachments, security, mime, malware scanning, file processing

How to Process Email Attachments Safely

Treat every attachment as hostile. Learn a safe Relay pipeline for authenticated downloads, MIME checks, size limits, checksums, isolated storage, malware scanning, sandbox parsing, and controlled access.

Attachments turn an email pipeline into a file-processing boundary. A PDF may contain active content, an archive may expand far beyond its compressed size, and an XML document may attempt to resolve external entities. A familiar filename or expected sender does not make those risks disappear.

Relay exposes attachments through an authenticated REST route, allowing your application to retrieve bytes for controlled processing. Safety comes from what happens around that download: strict resource limits, content validation, isolated storage, scanning, sandboxed parsers, and narrow delivery permissions.

Email attachment metadata inside a safe processing sandbox boundary

Download through a controlled ingestion boundary

The attachment route is GET https://www.relaymcp.net/api/v1/inboxes/:id/attachments/:attachmentId. Send the Bearer key on the server and validate that the attachment belongs to the expected inbox and message before retrieval. Never forward process.env.RELAY_API_KEY to browsers or downstream workers.

import { createHash } from 'node:crypto';

export async function fetchAttachment(inboxId, attachmentId) {
  const url = `https://www.relaymcp.net/api/v1/inboxes/${inboxId}/attachments/${attachmentId}`;
  const response = await fetch(url, {
    headers: { Authorization: `Bearer ${process.env.RELAY_API_KEY}` },
    redirect: 'manual',
    signal: AbortSignal.timeout(15000)
  });
  if (!response.ok) throw new Error(`Attachment download failed: ${response.status}`);
  if (!response.body) throw new Error('Attachment body is empty');

  const hash = createHash('sha256');
  const chunks = [];
  let size = 0;
  for await (const chunk of response.body) {
    size += chunk.length;
    if (size > 10 * 1024 * 1024) throw new Error('Attachment exceeds policy limit');
    hash.update(chunk);
    chunks.push(chunk);
  }
  return { bytes: Buffer.concat(chunks), size, sha256: hash.digest('hex') };
}

This example buffers only after enforcing a small application-specific limit; larger systems should stream to quarantine storage with the same byte counter. Apply connection, idle, and total-time limits, and avoid following unexpected redirects to credential-bearing destinations.

Validate identity, type, and size independently

A filename extension is a label, not a content type. MIME metadata is also supplied by the sender or generated during transport and cannot prove safety. Record both, then inspect magic bytes with a maintained detection library before selecting a parser. Reject or quarantine mismatches rather than silently choosing the most convenient interpretation.

Claimed file Basic checks Processing concern
PDF signature, parser limits active content and parser exploits
PNG or JPEG magic bytes, dimensions decompression bombs
ZIP entry count, expanded size traversal and recursion
DOCX or XLSX ZIP and XML policy external relationships and macros in related formats
CSV encoding, rows, cell length spreadsheet formula injection on export

Enforce limits at several layers: encoded message size, downloaded bytes, individual file size, pixel dimensions, archive entries, expanded bytes, parser time, and memory. Return a clear quarantine outcome when any limit is exceeded. The Relay API reference is the source for endpoint behavior; your file policy remains application-owned.

Use checksums and quarantine storage as evidence

Compute SHA-256 during ingestion and use the digest as the object identity where practical. Store bytes in a private quarantine bucket or volume with no public listing and no execution permissions. Keep metadata separately: inbox ID, message ID, attachment ID, digest, byte count, detected type, ingestion time, and scan state.

A checksum proves that two copies have identical bytes; it does not prove they are safe. Use it for deduplication, integrity checks, scan-cache keys, and audit trails. Avoid using the original filename as a storage key. Generate an opaque object name and retain a sanitized display name only as metadata.

Isolate credentials as well as bytes. The ingestion service needs Relay read access and quarantine write access, but not production database credentials. Scanner and parser workers need access only to their assigned object, ideally through a short-lived read capability.

Scan first, then parse in a sandbox

Run a maintained malware scanner before any rich parser touches the file. Define behavior for clean, malicious, unsupported, encrypted, and scanner-unavailable outcomes. Fail closed for workflows that require a clean result, and place indeterminate files in review rather than treating a timeout as clean.

Parsing should occur in a disposable sandbox with a read-only input mount, unprivileged user, minimal system call surface, no default network, and strict CPU, memory, process, and wall-clock limits. Do not run document macros, embedded scripts, or external renderers on the ingestion host. Keep parser images patched because file-format bugs are a common attack path.

Archives need traversal-safe extraction: reject absolute paths, parent traversal, links that escape the extraction root, nested archives beyond a set depth, and excessive compression ratios. Disable XML external entities and DTD retrieval; prefer a parser configuration that does not resolve external resources at all. For spreadsheets, preserve formulas as untrusted text rather than evaluating them.

Serve only authorized results through signed downloads

Do not expose quarantine objects directly. After policy checks, move or mark the exact digest as approved and require application authorization on every download. A signed URL should identify the approved object, expire quickly, constrain response headers, and be issued only after the caller's inbox or message access is checked.

Set a safe Content-Type, use Content-Disposition: attachment for files not intended for inline display, and add X-Content-Type-Options: nosniff. Never reflect an untrusted filename into a header without strict encoding and control-character removal. For browser preview, transform content into a constrained safe representation rather than serving the original active document.

Lifecycle rules should remove downloaded bytes and derived artifacts when no longer required. Apply your retention policy to downloaded copies independently of the source inbox. Changing Relay addresses does not remove stored mail; review the privacy policy and contact support for questions about source data handling. Manage key scope and revocation through your Relay account.

Frequently asked questions

Does a clean malware scan make a document safe to parse?

No. Scans detect known or suspected threats but cannot prove that a parser input is harmless. Keep rich parsing sandboxed and resource-limited even after a clean result, and quarantine scanner failures according to policy.

Should workers use the original filename for storage?

No. Original names can contain path separators, control characters, misleading extensions, or collisions. Store by an opaque identifier or digest and keep a sanitized name as display-only metadata.

Can I give users a permanent public attachment URL?

Avoid it. Email attachments often contain sensitive data and access requirements change. Authorize each request and issue short-lived signed URLs only for the approved object and current caller.

Make attachment handling a staged security pipeline

Safe processing is a sequence of explicit decisions: authenticate the Relay download, cap bytes, verify type, record the digest, quarantine, scan, sandbox parsing, and authorize delivery. Each stage should fail without granting the next stage more trust. Start with a narrow allowlist of formats and small limits, then expand only with tests and operational visibility. Use Relay's documentation for mailbox access and keep your attachment pipeline isolated from both credentials and production systems.

FROM THE COLLECTIONS

Continue with related guides

More practical reading on agent-friendly email, testing, privacy, and secure automation.