Skip to content

Instantly share code, notes, and snippets.

@SamMousa
Last active November 15, 2017 11:21
Show Gist options
  • Save SamMousa/d97576a09beb80f9a93974d29ef62c74 to your computer and use it in GitHub Desktop.
Save SamMousa/d97576a09beb80f9a93974d29ef62c74 to your computer and use it in GitHub Desktop.
Secure? secret storage.
<?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;
}
}
@SamMousa
Copy link
Author

Todo: a way of loading the secrets into php...

@SamMousa
Copy link
Author

SamMousa commented Nov 15, 2017

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