Skip to content
← Back to Functions
Code

Session Fingerprint Guard

Checks whether a session still matches expected browser/IP risk signals without hard-locking mobile users.

Function signature

ogValidateSessionFingerprint(expected, current, options = array())

Categories

  • Security

Parameters

expectedStored fingerprint data. Recognized keys: `created_at`, `ip_prefix`, `user_agent_hash`.currentCurrent request fingerprint data. Recognized keys: `ip_address`, `user_agent`.optionsRisk threshold options. Recognized keys: `max_age`.

Return value

Short public-safe status message.

  • status
  • risk_score
  • signals

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, and caller-owned allowlists before use; keep secrets 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.
 */

/**
 * Scores whether a session still matches expected request fingerprint signals.
 *
 * The function avoids brittle full-IP locking. It uses a risk score so mobile
 * networks and proxy changes can be handled by controller policy.
 *
 * @param array $expected Stored fingerprint data.
 * @param array $current Current request fingerprint data.
 * @param array $options Risk threshold options.
 * @return array Fingerprint status and risk details.
 */
function ogValidateSessionFingerprint($expected, $current, $options = array()) {
	$result = array(
		'success' => false,
		'message' => '',
		'data' => array()
	);

	if (!is_array($expected) || !is_array($current)) {
		$result['message'] = 'Expected and current fingerprint data must be arrays.';
		return $result;
	}

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

	$risk_score = 0;
	$signals = array();

	if (!empty($expected['user_agent_hash']) && !empty($current['user_agent'])) {
		$current_agent_hash = hash('sha256', (string)$current['user_agent']);
		if (!hash_equals((string)$expected['user_agent_hash'], $current_agent_hash)) {
			$risk_score += 40;
			$signals[] = 'user_agent_changed';
		}
	}

	if (!empty($expected['ip_prefix']) && !empty($current['ip_address'])) {
		$current_ip = (string)$current['ip_address'];
		if (strpos($current_ip, (string)$expected['ip_prefix']) !== 0) {
			$risk_score += 25;
			$signals[] = 'ip_prefix_changed';
		}
	}

	if (!empty($expected['created_at'])) {
		$created_at = (int)$expected['created_at'];
		$max_age = 86400;
		if (!empty($options['max_age'])) {
			$max_age = (int)$options['max_age'];
		}
		if ($created_at > 0 && time() - $created_at > $max_age) {
			$risk_score += 15;
			$signals[] = 'fingerprint_age_exceeded';
		}
	}

	$status = 'valid';
	if ($risk_score >= 60) {
		$status = 'invalid';
	} elseif ($risk_score > 0) {
		$status = 'risky';
	}

	$result['success'] = true;
	$result['message'] = 'Session fingerprint evaluated.';
	$result['data'] = array(
		'status' => $status,
		'risk_score' => $risk_score,
		'signals' => $signals
	);

	return $result;
}