Skip to content
← Back to Functions
Code

Remote File Download Guard

Validates remote download metadata before fetching or storing files.

Function signature

ogValidateRemoteDownload(metadata = array(), policy = array())

Categories

  • APIs and Webhooks

Parameters

metadataURL, headers, and content length metadata. Recognized keys: `content_length`, `content_type`, `url`.policyDownload policy options. Recognized keys: `allowed_content_types`, `max_bytes`.

Return value

Short public-safe status message.

  • url
  • host
  • content_length
  • max_bytes
  • content_type
  • approved

Compatibility

Existing function name and call order preserved; metadata signature corrected to source.

Minimum PHP version: 7.4

Security notes

Validate request method, identity, permissions, policy arrays, URLs, signatures, and caller-owned allowlists before use; keep secrets and internal paths out of public output.

Code

<?php

/*
 * Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
 * Free for personal and internal use. Paid project use requires visible credit
 * to Jeffery L. Paris. Corporate use requires a paid license fee unless a
 * separate written license states otherwise.
 */

/**
 * Validates remote download metadata before a server-side fetch.
 *
 * The function blocks non-HTTPS URLs, localhost/private IP hosts, oversized
 * files, and disallowed content types before any download is attempted.
 *
 * @param array $metadata URL, headers, and content length metadata.
 * @param array $policy Download policy options.
 * @return array Approved or rejected remote download metadata.
 */
function ogValidateRemoteDownload($metadata = array(), $policy = array()) {
	$result = array(
		'success' => false,
		'message' => '',
		'data' => array()
	);

	if (!is_array($metadata)) {
		$result['message'] = 'Download metadata must be an array.';
		return $result;
	}

	if (!is_array($policy)) {
		$policy = array();
	}

	$url = '';
	if (!empty($metadata['url'])) {
		$url = trim((string)$metadata['url']);
	}
	if (filter_var($url, FILTER_VALIDATE_URL) === false) {
		$result['message'] = 'Remote URL is invalid.';
		return $result;
	}
	if (stripos($url, 'https://') !== 0) {
		$result['message'] = 'Remote URL must use HTTPS.';
		return $result;
	}

	$parts = parse_url($url);
	$host = '';
	if (!empty($parts['host'])) {
		$host = strtolower($parts['host']);
	}
	if (empty($host) || $host == 'localhost') {
		$result['message'] = 'Remote host is not allowed.';
		return $result;
	}

	if (filter_var($host, FILTER_VALIDATE_IP)) {
		if (!filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
			$result['message'] = 'Private or reserved IP downloads are not allowed.';
			return $result;
		}
	}

	$max_bytes = 10485760;
	if (!empty($policy['max_bytes'])) {
		$max_bytes = (int)$policy['max_bytes'];
	}
	if ($max_bytes < 1024) {
		$max_bytes = 1024;
	}

	$content_length = 0;
	if (!empty($metadata['content_length'])) {
		$content_length = (int)$metadata['content_length'];
	}
	if ($content_length > 0 && $content_length > $max_bytes) {
		$result['message'] = 'Remote file is larger than the allowed limit.';
		return $result;
	}

	$allowed_types = array('image/jpeg', 'image/png', 'image/webp', 'application/pdf', 'text/csv', 'application/json');
	if (!empty($policy['allowed_content_types']) && is_array($policy['allowed_content_types'])) {
		$allowed_types = $policy['allowed_content_types'];
	}

	$content_type = '';
	if (!empty($metadata['content_type'])) {
		$content_type = strtolower(trim((string)$metadata['content_type']));
		$content_type = trim(strtok($content_type, ';'));
	}
	if (!empty($content_type) && !in_array($content_type, $allowed_types, true)) {
		$result['message'] = 'Remote content type is not allowed.';
		return $result;
	}

	$result['success'] = true;
	$result['message'] = 'Remote download metadata approved.';
	$result['data'] = array(
		'url' => $url,
		'host' => $host,
		'content_length' => $content_length,
		'max_bytes' => $max_bytes,
		'content_type' => $content_type,
		'approved' => true
	);

	return $result;
}