Skip to content

Instantly share code, notes, and snippets.

@katanacrimson
Created February 29, 2012 20:52
Show Gist options
  • Save katanacrimson/1944275 to your computer and use it in GitHub Desktop.
Save katanacrimson/1944275 to your computer and use it in GitHub Desktop.
Session hijack detection with IP "chunk" validation...
<?php
// licensed: MIT license
// very OOP'd in-lib, just a test case
echo fingerprint('1.2.3.4', 6, 3) . ' - ' . fingerprint('1.2.3.3', 6, 3) . "<br>\n";
echo fingerprint('1:2:3:4:5:6:7:8', 6, 3) . ' - ' . fingerprint('1:2:3:4:5:6::8', 6, 3) . "<br>\n";
echo fingerprint('1:2:3:4:5:6::8', 6, 3) . ' - ' . fingerprint('1:2:3:4:5::8', 6, 3) . "<br>\n";
function expandIPv6(array $ip_chunks)
{
if($ip_chunks === array('', '', ''))
{
return array_fill(0, 8, '0'); // basically 0:0:0:0:0:0:0:0
}
else
{
$chunk_count = count($ip_chunks);
for($i = 0; $i < 8; $i++)
{
if($ip_chunks[$i] == '')
{
if($i == 0) // at the start - ::ip
{
return array_merge(array_fill(0, 2, 0), array_slice($ip_chunks, 2));
}
elseif($i == 7) // at the end - ip::
{
return array_merge(array_slice($ip_chunks, 2), array_fill(0, 2, '0'));
}
else // in-between, ip::ip
{
// do the splits, drop the empty chunk in the middle, and use DARK MAGICK.
// @note: (8 - ($chunk_count - 1)) is the number of IP chunks that the :: is covering for.
array_splice($ip_chunks, $i, 1, array_fill($i, (8 - ($chunk_count - 1)), '0'));
return $ip_chunks;
}
}
}
}
}
function fingerprint($ip, $ipv6_val, $ipv4_val)
{
$is_ipv6 = (strpos($ip, ':') !== false) ? true : false;
if($is_ipv6)
{
$validation_level = $ipv6_val;
$expand_ipv6 = (strpos($ip, '::') !== false) ? true : false;
}
else
{
$validation_level = $ipv4_val;
}
$chunks = explode((($is_ipv6) ? ':' : '.'), $ip);
if($is_ipv6 && $expand_ipv6)
{
$chunks = expandIPv6($chunks);
}
return hash('sha256', 'omgwtfbbq 1.0 random ass browser' /*useragent*/ . implode('', array_slice($chunks, 0, $validation_level)) . 'abcdef'/*random seed*/);
}
<?php
namespace codebite\tsundere\Session;
use \R;
use \sigmabb\sigmabb\WebKernel as Sigma;
// licensed: MIT license
class Session
{
private $app, $_sid_entry, $sid, $session, $ipv4_validation_level, $ipv6_validation_level;
public function __construct()
{
$this->app = Sigma::getInstance();
$this->app->cookie->setCookiePrefix('tsun');
$this->setIdentifier('COOKIE::' . $this->app->cookie->getCookiePrefix() . 'sid');
$this->setIPv4ValidationLevel(4);
$this->setIPv6ValidationLevel(6);
}
private function sid()
{
if(empty($this->sid))
{
$this->sid = $this->app->input->getInput($this->_sid_entry, '');
}
return $this->sid;
}
public function setIPv4ValidationLevel($level)
{
// maximum validation level is 4, because IPv4 has 4 chunks total.
if($level < 1)
{
$level = 1;
}
elseif($level > 4)
{
$level = 4;
}
$this->ipv4_validation_level = (int) $level;
}
public function setIPv6ValidationLevel($level)
{
// maximum validation level is 8, because IPv6 has 8 chunks total.
if($level < 1)
{
$level = 1;
}
elseif($level > 8)
{
$level = 8;
}
$this->ipv6_validation_level = (int) $level;
}
public function setIdentifier($sid)
{
$this->_sid_entry = $sid;
$this->sid = NULL;
}
public function hasSession()
{
return $this->sid()->getWasSet();
}
public function loadSession()
{
if($this->hasSession())
{
$this->session = R::findOne('tsunsession', 'sid = ? ORDER BY last LIMIT 1', array($this->sid()));
}
// validate session...
if(!empty($this->session))
{
// check fingerprints..
if($this->session->fingerprint != $this->getFingerprint())
{
$this->session = NULL;
}
}
if(empty($this->session))
{
$seed = $this->app->seeder->buildRandomString(14);
$this->session = R::dispense('tsunsession');
$this->session->seed = $seed;
$this->session->sid = $this->app->seeder->buildRandomString(32, $seed);
$this->session->fingerprint = $this->getFingerprint();
$this->session->useragent = $this->app->request->getUseragent();
$this->session->ip = $this->app->request->getIP();
R::store($this->session);
$this->app->cookie->setCookie('sid')
->setCookieValue($this->session->sid);
}
}
public function getSID()
{
if(empty($this->session))
{
$this->loadSession();
}
return $this->session->sid;
}
public function getSessionSeed()
{
if(empty($this->session))
{
$this->loadSession();
}
return $this->session->seed;
}
private function getFingerprint()
{
$ip = $this->app->request->getIP();
$is_ipv6 = (strpos($ip, ':') !== false) ? true : false;
if($is_ipv6)
{
$validation_level = $this->ipv6_validation_level;
$expand_ipv6 = (strpos($ip, '::') !== false) ? true : false;
}
else
{
$validation_level = $this->ipv4_validation_level;
}
$chunks = explode((($is_ipv6) ? ':' : '.'), $ip);
if($is_ipv6 && $expand_ipv6)
{
$chunks = $this->expandIPv6($chunks);
}
return hash('sha256', $this->app->request->getUseragent() . implode('', array_slice($chunks, 0, $validation_level)) . $this->app['app.seed']);
}
private function expandIPv6($ip_chunks)
{
if($ip_chunks === array('', '', ''))
{
return array_fill(0, 8, '0'); // basically 0:0:0:0:0:0:0:0
}
else
{
$chunk_count = count($ip_chunks);
for($i = 0; $i < 8; $i++)
{
if($ip_chunks[$i] == '')
{
if($i == 0) // at the start - ::ip
{
return array_merge(array_fill(0, 2, 0), array_slice($ip_chunks, 2));
}
elseif($i == 7) // at the end - ip::
{
return array_merge(array_slice($ip_chunks, 2), array_fill(0, 2, '0'));
}
else // in-between, ip::ip
{
// do the splits, drop the empty chunk in the middle, and use DARK MAGICK.
// @note: (8 - ($chunk_count - 1)) is the number of IP chunks that the :: is covering for.
array_splice($ip_chunks, $i, 1, array_fill($i, (8 - ($chunk_count - 1)), '0'));
return $ip_chunks;
}
}
}
return $ip_chunks; // wtf bad data.
}
}
public function __destruct()
{
R::store($this->session);
}
public function offsetExists($offset)
{
return isset($this->session->data[$offset]) ? true : false;
}
public function offsetGet($offset)
{
if(!isset($this->session->data[$offset]))
{
return NULL;
}
return $this->session->data[$offset];
}
public function offsetSet($offset, $value)
{
$this->session->data[$offset] = $value;
}
public function offsetUnset($offset)
{
unset($this->session->data[$offset]);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment