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;
}