Last active
November 15, 2017 11:21
-
-
Save SamMousa/d97576a09beb80f9a93974d29ef62c74 to your computer and use it in GitHub Desktop.
Secure? secret storage.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace SamIT\Yii2; | |
class Secret | |
{ | |
private $secret; | |
private $key; | |
private $length; | |
public function __construct() | |
{ | |
// The secret is stored in the file system and its not kept in memory. | |
$this->secret = fopen('php://temp/maxmemory:0', 'w'); | |
} | |
public function setValue(string $value) | |
{ | |
// Key has same length as secret, we use XOR for encryption. | |
$value = $this->unpack($value); | |
$this->length = count($value); | |
$this->key = $this->unpack(random_bytes($this->length)); | |
// Truncate the file | |
ftruncate($this->secret, 0); | |
rewind($this->secret); | |
fwrite($this->secret, $this->pack($this->apply_xor($this->key, $value))); | |
} | |
/** | |
* Convert a string to a array of bytes (which are of type int) | |
* @param string $value | |
* @return int[] | |
*/ | |
public function unpack(string $value): array | |
{ | |
return unpack('C*', $value); | |
} | |
/** | |
* Convert an array of bytes to a string | |
* @param int[] $value | |
* @return string | |
*/ | |
public function pack(array $value): string | |
{ | |
return pack('C*', ...$value); | |
} | |
/** | |
* @return string The secret | |
* @throws \Exception if decryption fails | |
*/ | |
public function getValue(): string | |
{ | |
if (!isset($this->secret)) { | |
return ''; | |
} | |
rewind($this->secret); | |
return $this->pack($this->apply_xor($this->key, $this->unpack(fread($this->secret, $this->length)))); | |
} | |
/** | |
* Prevents accidental leakage | |
* @return array | |
*/ | |
public function __debugInfo() | |
{ | |
return []; | |
} | |
/** | |
* Prevents accidental leakage | |
* @return array | |
*/ | |
public function __sleep() | |
{ | |
unset($this->secret, $this->key, $this->length); | |
} | |
/** | |
* Prevents accidental leakage | |
* @return string | |
*/ | |
public function __toString() | |
{ | |
return '<< secret >>'; | |
} | |
/** | |
* @param array $key Must be an array of integers. | |
* @param array $value Must be an array of integers. | |
* @return array | |
* @throws \Exception | |
*/ | |
public function apply_xor(array $key, array $value) | |
{ | |
if (count($key) != count($value)) { | |
throw new \Exception("Lengths not equal: " . count($key) . '-' . count($value)); | |
} | |
$result = []; | |
for ($i = 1; $i <= count($value); $i++) { | |
$result[] = $key[$i] ^ $value[$i]; | |
} | |
return $result; | |
} | |
} |
Idea: use some kind of authorization before allowing access to the secret:
private function authorize()
{
$frames = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 3);
if ($frames[1]['function'] == '__toString') {
$hash = hash('sha256', $frames['2']['line'] . $frames['2']['file']);
}
if ('fb3b763f4bc2ae1848ea8b32aa2dc5c47b22f00fee413ceedb0cd1e8cf571b44' !== $hash) {
die('no');
}
}
(Example hash is for yii\db\Connection
line 660).
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Todo: a way of loading the secrets into php...