HIGH zip slipexpresshmac signatures

Zip Slip in Express with Hmac Signatures

Zip Slip in Express with Hmac Signatures — how this specific combination creates or exposes the vulnerability

Zip Slip is a path traversal vulnerability that occurs when an archive extraction uses user-supplied paths without proper validation. In Express, this risk can be compounded when Hmac Signatures are used to verify webhook or API payloads but the signature verification is decoupled from safe path handling. An attacker who can influence the archive contents or the payload used to derive the Hmac may pair a valid signature with a malicious archive that contains traversing file paths (e.g., ../../etc/passwd). Because the signature checks out, server-side logic that trusts the payload may proceed to extract the archive and write files to unsafe locations, leading to arbitrary file write or overwrite.

Consider an Express route that accepts a signed payload containing archive metadata and a destination path. If the route verifies the Hmac correctly but then uses the provided path directly in extraction, the trust chain is broken: the signature authenticates the request but does not guarantee safe path resolution. Attackers can supply nested zip entries with absolute paths or sequences like "../../../malicious", which can escape intended directories. This is especially dangerous when combined with predictable filenames or when the server overwrites existing files. The vulnerability is not in Hmac itself, but in the failure to canonicalize and validate paths after signature verification.

In practice, this combination can expose sensitive files or enable server-side template injection when extracted files are later read and served. For example, an attacker could craft an archive containing a file named ../../config/database.json that overwrites or exposes configuration. Because the Hmac validates the request origin, server logs and monitoring may incorrectly mark the activity as legitimate. Therefore, Express applications must validate and sanitize paths independently of signature checks, ensuring extraction destinations are confined to a safe base directory and that no path escapes that base.

Hmac Signatures-Specific Remediation in Express — concrete code fixes

To remediate Zip Slip risks when using Hmac Signatures in Express, validate and sanitize all paths before extraction and ensure signature verification does not implicitly authorize unsafe filesystem operations. Below are concrete code examples demonstrating secure handling in Express with Node.js built-in modules.

1. Hmac verification and safe extraction (secure pattern)

const express = require('express');
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const zlib = require('zlib');
const { promisify } = require('util');
const gunzip = promisify(zlib.gunzip);

const app = express();
app.use(express.json());

const SHARED_SECRET = process.env.WEBHOOK_SECRET; // store securely

function verifyHmac(payload, receivedSignature) {
  const hmac = crypto.createHmac('sha256', SHARED_SECRET);
  hmac.update(payload);
  return hmac.digest('hex') === receivedSignature;
}

function sanitizeExtractPath(baseDir, suppliedPath) {
  const resolved = path.resolve(baseDir, suppliedPath);
  if (!resolved.startsWith(path.resolve(baseDir))) {
    throw new Error('Path traversal attempt detected');
  }
  return resolved;
}

app.post('/webhook/extract', async (req, res) => {
  const sig = req.get('X-Hub-Signature-256');
  if (!sig || !verifyHmac(JSON.stringify(req.body), sig.replace('sha256=', ''))) {
    return res.status(401).send('Invalid signature');
  }

  const { archiveBase64, destinationPath } = req.body;
  const baseDir = path.resolve('./safe-extract');

  try {
    const filePath = sanitizeExtractPath(baseDir, destinationPath);
    const buffer = Buffer.from(archiveBase64, 'base64');
    const decompressed = await gunzip(buffer);
    // Here you would use a safe archive parser that prevents path traversal,
    // e.g., tar-stream with explicit entry paths or adm-zip with validation.
    fs.writeFileSync(filePath, decompressed);
    res.status(200).send('Extraction scheduled safely');
  } catch (err) {
    console.error(err);
    res.status(400).send('Extraction failed');
  }
});

app.listen(3000, () => console.log('Server running on port 3000'));

2. Validate each archive entry (recommended for robust security)

const { finished } = require('stream/promises');
const tar = require('tar');

app.post('/webhook/extract-tar', async (req, res) => {
  const sig = req.get('X-Hmac-Signature');
  if (!sig || !verifyHmac(JSON.stringify(req.body), sig.replace('sha256=', ''))) {
    return res.status(401).send('Invalid signature');
  }

  const { tarBase64 } = req.body;
  const baseDir = path.resolve('./safe-extract');

  const buffer = Buffer.from(tarBase64, 'base64');
  const stream = tar.tz({ cwd: baseDir, strip: 0, preservePaths: false });

  // Ensure no entry escapes baseDir
  stream.on('entry', (entry) => {
    const normalized = path.resolve(baseDir, entry.path);
    if (!normalized.startsWith(baseDir)) {
      entry.abort();
      throw new Error('Invalid entry path: ' + entry.path);
    }
    entry.resume(); // or handle file writing safely
  });

  try {
    await finished(stream.end(buffer));
    res.status(200).send('Tar extracted safely');
  } catch (err) {
    console.error(err);
    res.status(400).send('Tar extraction failed');
  }
});

Key remediation practices

  • Always resolve and validate extracted paths against a strict base directory; reject any path that escapes.
  • Use archive libraries that enforce path sanitization or process entries individually to inspect paths.
  • Keep the Hmac secret out of logs and do not use it to authorize filesystem paths directly; treat signature verification as authentication only.
  • Apply principle of least privilege to the process performing extraction, limiting file write scope.

Frequently Asked Questions

Why does Hmac verification not prevent Zip Slip if the signature is valid?
Hmac verification confirms the request came from a party holding the secret, but it does not validate the safety of file paths or archive contents. Path traversal must be prevented separately by canonicalizing and constraining extraction destinations.
What specific practice reduces Zip Slip risk when using Hmac Signatures in Express?
Independently validate and sanitize paths before extraction, use archive libraries that reject or normalize dangerous entries, and restrict extraction to a confined base directory regardless of signature validity.