npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@md-oss/security

v0.1.7

Published

Cryptographic utilities for encryption, signed URLs, and secure streaming

Downloads

242

Readme

@md-oss/security

Cryptographic utilities for encryption, signed URLs, and secure streaming.

Features

  • AES-256-GCM Encryption - Secure string and buffer encryption with authentication
  • Signed URLs - Generate and verify cryptographically signed URLs with optional expiration
  • Decryption Streams - Stream-based decryption for large files with memory efficiency
  • Range Streaming - Support for partial file reads

Installation

pnpm add @md-oss/security

Usage

Encryption & Decryption

import { generateKey, encrypt, decrypt } from '@md-oss/security';

// Generate a random encryption key (base64 encoded)
const key = generateKey(32); // 32 bytes for AES-256

// Encrypt a string
const encrypted = encrypt('Hello, World!', key);
console.log(encrypted); // Base64 encoded string

// Decrypt it back
const decrypted = decrypt(encrypted, key);
console.log(decrypted); // 'Hello, World!'

// Works with buffers too
const buffer = Buffer.from('Binary data');
const encryptedBuffer = encrypt(buffer, key);
const decryptedBuffer = decrypt(encryptedBuffer, key);

How It Works

  • Uses AES-256-GCM (Galois/Counter Mode) for authenticated encryption
  • Generates a random 12-byte IV for each encryption
  • Automatically includes the IV and authentication tag in the encrypted output
  • Returns base64-encoded strings for safe transport
  • Provides authentication to prevent tampering

Signed URLs

Generate Signed URLs

import { generateSignedUrl } from '@md-oss/security';

const secret = 'your-base64-encoded-secret';
const path = '/api/files/document.pdf';
const expires = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours from now

const { url, expiresUnixTs, sig } = generateSignedUrl({
  secret,
  path,
  expires
});

console.log(url); // /api/files/document.pdf?sig=abc123&expires=1234567890

// URLs without expiration
const permanentUrl = generateSignedUrl({
  secret,
  path,
  expires: null
});
console.log(permanentUrl.url); // /api/files/document.pdf?sig=abc123

Verify Signed URLs

import { verifySignedUrl } from '@md-oss/security';

const result = verifySignedUrl({
  secret,
  path: '/api/files/document.pdf',
  expires: 1234567890, // UNIX timestamp in seconds
  sig: 'abc123'
});

if (result === 'InvalidSignatureError') {
  console.log('Signature is invalid');
} else if (result === 'ExpiredSignatureError') {
  console.log('URL has expired');
} else {
  console.log('Verified:', result); // { path, expires, sig }
}

Verify from Request

import { verifySignedUrlFromRequest } from '@md-oss/security';

// In your request handler
const signedAccess = verifySignedUrlFromRequest({
  secret: process.env.SIGNED_URL_SECRET || '',
  expectedPath: req.path,
  query: req.query
});

if (signedAccess.type === 'verified') {
  // URL signature is valid
  console.log('Access granted');
} else {
  // Handle invalid/expired signature
  console.log('Access denied:', signedAccess.error);
  // Possible errors: 'MISSING_SIGNATURE' | 'INVALID_SIGNATURE' | 'EXPIRED_SIGNATURE'
}

With Access Control

import { withSignedAccess } from '@md-oss/security';

const result = await withSignedAccess(
  signedAccess,
  async () => {
    // This runs if signature is missing or invalid
    // Return true to continue without signature
    // Return error or data to handle differently
    return true; // Allow access without signature
  }
);

if (result instanceof APIError) {
  // Signature validation failed
  res.status(result.statusCode).json(result.body);
} else if (result === true) {
  // Access granted (either verified or allowed without)
  // Continue processing
}

Decryption Streams

Basic Decryption Stream

import { createDecryptionStream } from '@md-oss/security';

const decryptionKey = 'your-base64-encoded-key';
const filePath = '/path/to/encrypted/file';

const decryptedStream = createDecryptionStream(filePath, decryptionKey);

// Pipe to response for download
res.setHeader('Content-Type', 'application/octet-stream');
res.setHeader('Content-Disposition', 'attachment; filename="file.pdf"');
decryptedStream.pipe(res);

// Or save to file
import { createWriteStream } from 'node:fs';
const writeStream = createWriteStream('/path/to/decrypted/file');
decryptedStream.pipe(writeStream);

// Handle errors
decryptedStream.on('error', (err) => {
  console.error('Decryption error:', err);
});

Range Streaming

import { streamWithRange } from '@md-oss/security';

// Stream specific byte range (e.g., for HTTP Range requests)
const start = 0;
const end = 1024 * 1024; // 1MB

const rangeStream = streamWithRange(filePath, start, end);
rangeStream.pipe(res);

Security Best Practices

Key Management

// Generate secure random keys
const encryptionKey = generateKey(32); // 32 bytes for AES-256
const signingSecret = generateKey(32);

// Store securely (e.g., environment variables, KMS)
process.env.ENCRYPTION_KEY = encryptionKey;
process.env.SIGNED_URL_SECRET = signingSecret;

// Never hardcode keys in source code
// Rotate keys periodically
// Use different keys for different purposes

Signed URL Best Practices

// Always set expiration times
const shortLivedUrl = generateSignedUrl({
  secret,
  path,
  expires: new Date(Date.now() + 30 * 60 * 1000) // 30 minutes
});

// Use different secrets for different purposes
const downloadSecret = process.env.DOWNLOAD_SIGNED_URL_SECRET;
const uploadSecret = process.env.UPLOAD_SIGNED_URL_SECRET;

// Include path in signature to prevent URL manipulation
// Verify path matches expected value
const expectedPath = '/api/files/user-123/document.pdf';
const userProvidedPath = req.query.path;

if (expectedPath !== userProvidedPath) {
  throw new Error('Path mismatch');
}

Encryption Best Practices

// Use strong keys
const key = generateKey(32); // 32 bytes = 256 bits for AES-256

// Each encryption generates a new random IV
const data1 = encrypt('secret', key); // Different each time
const data2 = encrypt('secret', key);
// data1 !== data2, both decrypt to 'secret'

// Verify authenticity before decrypting
// AES-GCM provides authentication automatically

// Don't reuse keys across different purposes
const userDataKey = generateKey(32);
const sessionDataKey = generateKey(32);

Types

import type {
  SignedUrlSchema,
  VerifySignedUrlFromRequestOptions,
  SignedAccessError,
  SignedAccess
} from '@md-oss/security';

Error Handling

import { createDecryptionStream } from '@md-oss/security';

const stream = createDecryptionStream(filePath, key);

stream.on('error', (err) => {
  if (err.code === 'ENOENT') {
    console.error('File not found');
  } else if (err.code === 'ERR_OSSL_EVP_BAD_DECRYPT') {
    console.error('Decryption failed - wrong key or corrupted data');
  } else {
    console.error('Unknown error:', err);
  }
});

stream.pipe(destination);

Performance Considerations

  • Encryption/Decryption: O(n) where n is data size
  • Stream Processing: Memory-efficient for large files (64KB chunks by default)
  • Signed URLs: O(1) - constant time verification
  • Timing-safe Comparison: Uses crypto.timingSafeEqual to prevent timing attacks

API Reference

Encryption

  • generateKey(length?: number): string - Generate base64-encoded random key
  • encrypt<T>(input: T, key: string): T - Encrypt string or buffer
  • decrypt<T>(input: T, key: string): T - Decrypt string or buffer

Signed URLs

  • generateSignedUrl(options): { url, expiresUnixTs, sig }
  • verifySignedUrl(options): string | { path, expires, sig }
  • verifySignedUrlFromRequest(options): SignedAccess
  • withSignedAccess(access, callback): Promise

Streams

  • createDecryptionStream(filePath, key): PassThrough
  • streamWithRange(filePath, start, end): PassThrough