PHP Classes

File: src/HiddenString.php

Recommend this page to a friend!
  Classes of Scott Arciszewski   PHP String Hidden Characters   src/HiddenString.php   Download  
File: src/HiddenString.php
Role: Class source
Content type: text/plain
Description: Class source
Class: PHP String Hidden Characters
Hide strings to avoid appearing in stack traces
Author: By
Last change:
Date: 4 years ago
Size: 4,456 bytes
 

Contents

Class file image Download
<?php
declare(strict_types=1);
namespace
ParagonIE\HiddenString;

use
ParagonIE\ConstantTime\Binary;

/**
 * Class HiddenString
 *
 * The purpose of this class is to encapsulate strings and hide their contents
 * from stack traces should an unhandled exception occur.
 *
 * The only things that should be protected:
 * - Passwords
 * - Plaintext (before encryption)
 * - Plaintext (after decryption)
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
final class HiddenString
{
   
/**
     * @var string
     */
   
protected $internalStringValue = '';

   
/**
     * Disallow the contents from being accessed via __toString()?
     *
     * @var bool
     */
   
protected $disallowInline = false;

   
/**
     * Disallow the contents from being accessed via __sleep()?
     *
     * @var bool
     */
   
protected $disallowSerialization = false;

   
/**
     * HiddenString constructor.
     * @param string $value
     * @param bool $disallowInline
     * @param bool $disallowSerialization
     *
     * @throws \TypeError
     */
   
public function __construct(
       
string $value,
       
bool $disallowInline = false,
       
bool $disallowSerialization = false
   
) {
       
$this->internalStringValue = self::safeStrcpy($value);
       
$this->disallowInline = $disallowInline;
       
$this->disallowSerialization = $disallowSerialization;
    }

   
/**
     * @param HiddenString $other
     * @return bool
     * @throws \TypeError
     */
   
public function equals(HiddenString $other)
    {
        return \
hash_equals(
           
$this->getString(),
           
$other->getString()
        );
    }

   
/**
     * Hide its internal state from var_dump()
     *
     * @return array
     */
   
public function __debugInfo()
    {
        return [
           
'internalStringValue' =>
               
'*',
           
'attention' =>
               
'If you need the value of a HiddenString, ' .
               
'invoke getString() instead of dumping it.'
       
];
    }

   
/**
     * Wipe it from memory after it's been used.
     * @return void
     */
   
public function __destruct()
    {
        if (\
is_callable('sodium_memzero')) {
            try {
                \
sodium_memzero($this->internalStringValue);
                return;
            } catch (\
Throwable $ex) {
            }
        }

       
// Last-ditch attempt to wipe existing values if libsodium is not
        // available. Don't rely on this.
       
$zero = \str_repeat("\0", (int) Binary::safeStrlen($this->internalStringValue));
       
$this->internalStringValue = $this->internalStringValue ^ (
           
$zero ^ $this->internalStringValue
       
);
        unset(
$zero);
        unset(
$this->internalStringValue);
    }

   
/**
     * Explicit invocation -- get the raw string value
     *
     * @return string
     * @throws \TypeError
     */
   
public function getString(): string
   
{
        return
self::safeStrcpy($this->internalStringValue);
    }

   
/**
     * Returns a copy of the string's internal value, which should be zeroed.
     * Optionally, it can return an empty string.
     *
     * @return string
     * @throws \TypeError
     */
   
public function __toString(): string
   
{
        if (!
$this->disallowInline) {
            return
self::safeStrcpy($this->internalStringValue);
        }
        return
'';
    }

   
/**
     * @return array
     */
   
public function __sleep(): array
    {
        if (!
$this->disallowSerialization) {
            return [
               
'internalStringValue',
               
'disallowInline',
               
'disallowSerialization'
           
];
        }
        return [];
    }

   
/**
     * PHP 7 uses interned strings. We don't want altering this one to alter
     * the original string.
     *
     * @param string $string
     * @return string
     * @throws \TypeError
     */
   
public static function safeStrcpy(string $string): string
   
{
       
$length = Binary::safeStrlen($string);
       
$return = '';
       
/** @var int $chunk */
       
$chunk = $length >> 1;
        if (
$chunk < 1) {
           
$chunk = 1;
        }
        for (
$i = 0; $i < $length; $i += $chunk) {
           
$return .= Binary::safeSubstr($string, $i, $chunk);
        }
        return
$return;
    }
}