Skip to content

Commit

Permalink
Version 1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
paragonie-scott committed Apr 23, 2016
1 parent 606274f commit 6720f2a
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 67 deletions.
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@
},
"require": {
"php": "^5.6|^7.0",
"paragonie/random_compat": "^1.1.5"
"paragonie/constant_time_encoding": "^1|^2",
"paragonie/random_compat": "^1|^2"
},
"require-dev": {
"phpunit/phpunit": "4.5.*"
"phpunit/phpunit": "^4|^5"
}
}
108 changes: 43 additions & 65 deletions src/AntiCSRF.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<?php
namespace ParagonIE\AntiCSRF;

use \ParagonIE\ConstantTime\Base64;
use \ParagonIE\ConstantTime\Binary;

/**
* Copyright (c) 2015 - 2016 Paragon Initiative Enterprises <https://paragonie.com>
*
Expand Down Expand Up @@ -43,7 +46,7 @@
*
*
* If you would like to use this library under different terms, please
* contact Resonant Core to inquire about a license exemption.
* contact Paragon Initiative Enterprises to inquire about a license exemption.
*/
class AntiCSRF
{
Expand Down Expand Up @@ -105,9 +108,9 @@ public function insertToken($lockTo = null, $echo = true)
\array_map(
function($key, $value) {
return "<!--\n-->".
"<input type=\"hidden\"".
" name=\"".$key."\"".
" value=\"".self::noHTML($value)."\"".
"<input type=\"hidden\"" .
" name=\"".$key."\"" .
" value=\"".self::noHTML($value)."\"" .
" />";
},
\array_keys($token_array),
Expand All @@ -120,17 +123,26 @@ function($key, $value) {
}
return $ret;
}


/**
* @return string
*/
public function getSessionIndex()
{
return $this->sessionIndex;
}


/**
* @return string
*/
public function getFormIndex()
{
return $this->formIndex;
}


/**
* @return string
*/
public function getFormToken()
{
return $this->formToken;
Expand All @@ -154,20 +166,20 @@ public function getTokenArray($lockTo = null)
}

if (\preg_match('#/$#', $lockTo)) {
$lockTo = \substr($lockTo, 0, strlen($lockTo) - 1);
$lockTo = Binary::safeSubstr($lockTo, 0, Binary::safeStrlen($lockTo) - 1);
}

list($index, $token) = $this->generateToken($lockTo);

if ($this->hmac_ip !== false) {
// Use HMAC to only allow this particular IP to send this request
$token = $this->encode(
$token = Base64::encode(
\hash_hmac(
$this->hashAlgo,
isset($this->server['REMOTE_ADDR'])
? $this->server['REMOTE_ADDR']
: '127.0.0.1',
\base64_decode($token),
Base64::decode($token),
true
)
);
Expand Down Expand Up @@ -209,6 +221,10 @@ public function validateRequest()
return false;
}

if (!\is_string($index) || !\is_string($token)) {
return false;
}

// Grab the value stored at $index
$stored = $this->session[$this->sessionIndex][$index];

Expand All @@ -219,7 +235,11 @@ public function validateRequest()
$lockTo = $this->server['REQUEST_URI'];
if (\preg_match('#/$#', $lockTo)) {
// Trailing slashes are to be ignored
$lockTo = substr($lockTo, 0, strlen($lockTo) - 1);
$lockTo = Binary::safeSubstr(
$lockTo,
0,
Binary::safeStrlen($lockTo) - 1
);
}

if (!\hash_equals($lockTo, $stored['lockTo'])) {
Expand All @@ -233,7 +253,7 @@ public function validateRequest()
$expected = $stored['token'];
} else {
// We mixed in the client IP address to generate the output
$expected = $this->encode(
$expected = Base64::encode(
\hash_hmac(
$this->hashAlgo,
isset($this->server['REMOTE_ADDR'])
Expand All @@ -252,6 +272,7 @@ public function validateRequest()
* Only use this if you know what you are doing.
*
* @param array $options
* @return self
*/
public function reconfigure(array $options = [])
{
Expand Down Expand Up @@ -283,12 +304,8 @@ public function reconfigure(array $options = [])
*/
protected function generateToken($lockTo)
{
$index = $this->encode(
\random_bytes(18)
);
$token = $this->encode(
\random_bytes(32)
);
$index = Base64::encode(\random_bytes(18));
$token = Base64::encode(\random_bytes(33));

$this->session[$this->sessionIndex][$index] = [
'created' => \intval(
Expand All @@ -300,7 +317,11 @@ protected function generateToken($lockTo)
'token' => $token
];
if (\preg_match('#/$#', $lockTo)) {
$lockTo = self::subString($lockTo, 0, self::stringLength($lockTo) - 1);
$lockTo = Binary::safeSubstr(
$lockTo,
0,
Binary::safeStrlen($lockTo) - 1
);
}
$this->session[$this->sessionIndex][$index]['lockTo'] = $lockTo;

Expand All @@ -311,12 +332,14 @@ protected function generateToken($lockTo)
/**
* Enforce an upper limit on the number of tokens stored in session state
* by removing the oldest tokens first.
*
* @return self
*/
protected function recycleTokens()
{
if (!$this->expire_old) {
// This is turned off.
return;
return $this;
}
// Sort by creation time
\uasort(
Expand All @@ -343,49 +366,4 @@ protected static function noHTML($untrusted)
{
return \htmlentities($untrusted, ENT_QUOTES, 'UTF-8');
}

/**
* Encode string with base64, but strip padding.
* PHP base64_decode does not croak on that.
*
* @param string $s
* @return string
*/
protected static function encode($s)
{
return \rtrim(
\base64_encode($s),
'='
);
}

/**
* Binary-safe substr() implementation
*
* @param string $str
* @param int $start
* @param int|null $length
* @return string
*/
protected static function subString($str, $start, $length = null)
{
if (\function_exists('\\mb_substr')) {
return \mb_substr($str, $start, $length, '8bit');
}
return \substr($str, $start, $length);
}

/**
* Binary-safe strlen() implementation
*
* @param string $str
* @return string
*/
protected static function stringLength($str)
{
if (\function_exists('\\mb_substr')) {
return \mb_strlen($str, '8bit');
}
return \strlen($str);
}
}

0 comments on commit 6720f2a

Please sign in to comment.