Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Issue #10730: Implement new crypto_api
This implements the foundation of a new Cryptography API which is
responsible for providing cryptography functionality to MantisBT.

For now, the only feature available in this new API is the generation of
secure and strong randomness using openssl_random_pseudo_bytes in PHP
5.3 (if available), /dev/urandom if available on the system or an
enhanced mt_rand generator built on top of PHP's existing Mersenne
Twister pseudo random number.

We used to just rely on a single mt_rand() for generating nonces or
providing other cryptographic functionality. This posed a number of
problems including the leakage of the internal state of the Mersenne
Twister PRNG, enabling users to predict all future outputs of the PRNG.
Additionally, the total number of combinations available from mt_rand()
is very small when in many cases we need more than a few million
combinations of keys.

The new approach calls mt_rand() multiple times and then using a secret
unique salt known only to each MantisBT installation, hashes the output
using the Whirlpool algorithm. This produces 512bits of output that can
be used for creating a random string/nonce. If more than 512bits of
output are required, we simply perform this operation multiple times
until we have generated enough output.

While the new Mersenne Twister method for generating random strings is
still anything but strong or secure, it does raise the bar
significantly. It is hoped that this method is only used as a last
resort when no other options for generating strong randomness are
available.

A new configuration option $g_crypto_master_salt was also added to form
the basis of salting and hashing functions in the future. Currently we
use different keys for RSS, signup/lost password verification and so
forth when it'd be much easier to just derive keys as needed from the
master salt.

If $g_crypto_master_salt is not defined by the user, MantisBT will
refuse to work. This salt must be at least 16 characters long in the
hope that users who don't understand the importance of setting a strong
master salt are informed of their mistake. This refusal to work unless
the user sets a strong $g_crypto_master_salt value in config_inc.php is
necessary because it forms the basis for a lot of the security features
implemented in MantisBT. We don't want users to forgetting to set
$g_crypto_master_salt and using a default value known to the entire
world.
  • Loading branch information
davidhicks committed Feb 8, 2010
1 parent cca2566 commit 045a897
Show file tree
Hide file tree
Showing 10 changed files with 266 additions and 20 deletions.
5 changes: 4 additions & 1 deletion admin/check.php
Expand Up @@ -23,7 +23,10 @@

error_reporting( E_ALL );

$g_skip_open_db = true; # don't open the database in database_api.php
# Load the MantisDB core in maintenance mode. This mode will assume that
# config_inc.php hasn't been specified. Thus the database will not be opened
# and plugins will not be loaded.
define( 'MANTIS_MAINTENANCE_MODE', true );

/**
* MantisBT Core API's
Expand Down
9 changes: 6 additions & 3 deletions admin/install.php
Expand Up @@ -25,9 +25,12 @@

/** @todo put this somewhere */
@set_time_limit( 0 );
$g_skip_open_db = true; # don't open the database in database_api.php
define( 'MANTIS_INSTALLER', true );
define( 'PLUGINS_DISABLED', true );

# Load the MantisDB core in maintenance mode. This mode will assume that
# config_inc.php hasn't been specified. Thus the database will not be opened
# and plugins will not be loaded.
define( 'MANTIS_MAINTENANCE_MODE', true );

@require_once( dirname( dirname( __FILE__ ) ) . DIRECTORY_SEPARATOR . 'core.php' );
require_api( 'install_helper_functions_api.php' );
$g_error_send_page_header = false; # bypass page headers in error handler
Expand Down
3 changes: 1 addition & 2 deletions admin/test_langs.php
Expand Up @@ -22,7 +22,7 @@
*/

define( 'PLUGINS_DISABLED', true );
$g_skip_lang_load = true;
define( 'LANG_LOAD_DISABLED', true );

/**
* MantisBT Core API's
Expand All @@ -45,7 +45,6 @@
die;
}

unset( $g_skip_lang_load ) ;
lang_push( 'english' );

access_ensure_global_level( config_get_global( 'admin_site_threshold' ) );
Expand Down
6 changes: 5 additions & 1 deletion admin/upgrade_unattended.php
Expand Up @@ -23,7 +23,11 @@

@set_time_limit( 0 );

$g_skip_open_db = true; # don't open the database in database_api.php
# Load the MantisDB core in maintenance mode. This mode will assume that
# config_inc.php hasn't been specified. Thus the database will not be opened
# and plugins will not be loaded.
define( 'MANTIS_MAINTENANCE_MODE', true );

/**
* MantisBT Core API's
*/
Expand Down
34 changes: 34 additions & 0 deletions config_defaults_inc.php
Expand Up @@ -272,6 +272,39 @@
*/
$g_form_security_validation = ON;

/*****************************
* Security and Cryptography *
*****************************/

/**
* Master salt value used for cryptographic hashing throughout MantisBT. This
* value must be kept secret at all costs. You must generate a unique and
* random salt value for each installation of MantisBT you control. The
* minimum length of this string must be at least 16 characters.
*
* The value you select for this salt should be a long string generated using
* a secure random number generator. An example for Linux systems is:
* cat /dev/urandom | head -c 64 | base64
* Note that the number of bits of entropy per byte of output from /dev/urandom
* is not 8. If you're particularly paranoid and don't mind waiting a long
* time, you could use /dev/random to get much closer to 8 bits of entropy per
* byte. Moving the mouse (if possible) while generating entropy via
* /dev/random will greatly improve the speed at which /dev/random produces
* entropy.
*
* WARNING: This configuration option has a profound impact on the security of
* your MantisBT installation. Failure to set this configuration option
* correctly could lead to your MantisBT installation being compromised. Ensure
* that this value remains secret. Treat it with the same security that you'd
* treat the password to your MantisDB database.
*
* This setting is blank by default. MantisBT will not operate in this state.
* Hence you are forced to change the value of this configuration option.
*
* @global string $g_crypto_master_salt
*/
$g_crypto_master_salt = '';

/****************************
* Signup and Lost Password *
****************************/
Expand Down Expand Up @@ -3975,6 +4008,7 @@
'compress_html',
'content_expire',
'cookie',
'crypto_master_salt',
'custom_headers',
'database_name',
'^db_',
Expand Down
30 changes: 17 additions & 13 deletions core.php
Expand Up @@ -33,6 +33,7 @@
* @uses config_defaults_inc.php
* @uses config_inc.php
* @uses constant_inc.php
* @uses crypto_api.php
* @uses custom_constants_inc.php
* @uses custom_functions_inc.php
* @uses database_api.php
Expand Down Expand Up @@ -199,24 +200,25 @@ function __autoload( $className ) {
}
}

# Initialise cryptographic keys
require_api( 'crypto_api.php' );
crypto_init();

# Connect to the database
require_api( 'database_api.php' );
require_api( 'config_api.php' );

if( !isset( $g_skip_open_db ) ) {
if ( !defined( 'MANTIS_MAINTENANCE_MODE' ) ) {
if( OFF == $g_use_persistent_connections ) {
db_connect( config_get_global( 'dsn', false ), $g_hostname, $g_db_username, $g_db_password, $g_database_name, config_get_global( 'db_schema' ) );
} else {
db_connect( config_get_global( 'dsn', false ), $g_hostname, $g_db_username, $g_db_password, $g_database_name, config_get_global( 'db_schema' ), true );
}
} else {
if (!defined('PLUGINS_DISABLED') )
define( 'PLUGINS_DISABLED', true );
}

# Initialise plugins
require_api( 'plugin_api.php' );
if ( !defined( 'PLUGINS_DISABLED' ) ) {
if ( !defined( 'PLUGINS_DISABLED' ) && !defined( 'MANTIS_MAINTENANCE_MODE' ) ) {
require_api( 'plugin_api.php' );
plugin_init_installed();
}

Expand All @@ -239,13 +241,13 @@ function __autoload( $className ) {
}

require_api( 'authentication_api.php' );
require_api( 'user_pref_api.php' );
if( auth_is_user_authenticated() ) {
require_api( 'user_pref_api.php' );
date_default_timezone_set( user_pref_get_pref( auth_get_current_user_id(), 'timezone' ) );
}

require_api( 'collapse_api.php' );
if ( !defined( 'MANTIS_INSTALLER' ) ) {
if ( !defined( 'MANTIS_MAINTENANCE_MODE' ) ) {
require_api( 'collapse_api.php' );
collapse_cache_token();
}

Expand All @@ -260,11 +262,13 @@ function __autoload( $className ) {
http_all_headers();

# Push default language to speed calls to lang_get
require_api( 'lang_api.php' );
if ( !isset( $g_skip_lang_load ) ) {
if ( !defined( 'LANG_LOAD_DISABLED' ) ) {
require_api( 'lang_api.php' );
lang_push( lang_get_default() );
}

# Signal plugins that the core system is loaded
require_api( 'event_api.php' );
event_signal( 'EVENT_CORE_READY' );
if ( !defined( 'PLUGINS_DISABLED' ) && !defined( 'MANTIS_MAINTENANCE_MODE' ) ) {
require_api( 'event_api.php' );
event_signal( 'EVENT_CORE_READY' );
}
4 changes: 4 additions & 0 deletions core/constant_inc.php
Expand Up @@ -413,6 +413,10 @@
# ERROR_FORM_*
define( 'ERROR_FORM_TOKEN_INVALID', 2800 );

# ERROR_CRYPTO_*
define( 'ERROR_CRYPTO_MASTER_SALT_INVALID', 2900 );
define( 'ERROR_CRYPTO_CAN_NOT_GENERATE_STRONG_RANDOMNESS', 2901 );

# Generic position constants
define( 'POSITION_NONE', 0 );
define( 'POSITION_TOP', 1 );
Expand Down
145 changes: 145 additions & 0 deletions core/crypto_api.php
@@ -0,0 +1,145 @@
<?php
# MantisBT - A PHP based bugtracking system

# MantisBT is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# MantisBT is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with MantisBT. If not, see <http://www.gnu.org/licenses/>.

/**
* Crypto API
*
* @package CoreAPI
* @subpackage CryptoAPI
* @copyright Copyright (C) 2000 - 2002 Kenzaburo Ito - kenito@300baud.org
* @copyright Copyright (C) 2002 - 2010 MantisBT Team - mantisbt-dev@lists.sourceforge.net
* @link http://www.mantisbt.org
*
* @uses config_api.php
* @uses constant_inc.php
* @uses error_api.php
*/

require_api( 'config_api.php' );
require_api( 'constant_inc.php' );
require_api( 'error_api.php' );

/**
* Initialise the CryptoAPI subsystem. This function checks whether the master
* salt is specified correctly within the configuration. If not, a fatal error
* is produced to protect against invalid configuration impacting the security
* of the MantisBT installation.
* @return null
*/
function crypto_init() {
if ( !defined( 'MANTIS_MAINTENANCE_MODE' ) ) {
if ( strlen( config_get_global( 'crypto_master_salt' ) ) < 16 ) {
trigger_error( ERROR_CRYPTO_MASTER_SALT_INVALID, ERROR );
}
}
return;
}

/**
* Generate a random string (raw binary output) for cryptographic purposes such
* as nonces, IVs, default passwords, etc. This function will attempt to
* generate strong randomness but can optionally be used to generate weaker
* randomness if less security is needed or a strong source of randomness isn't
* available. The use of weak randomness for cryptographic purposes is strongly
* discouraged because it contains low entropy and is predictable.
* @param int $p_bytes Number of bytes of randomness required
* @param bool $p_require_strong_generator Whether or not a weak source of randomness can be used by this function
* @return string|null Raw binary string containing the requested number of bytes of random output or null if the output couldn't be created
*/
function crypto_generate_random_string( $p_bytes, $p_require_strong_generator = true ) {

# First we attempt to use the secure PRNG provided by OpenSSL in PHP 5.3
if ( function_exists( 'openssl_random_pseudo_bytes' ) ) {
$t_random_bytes = openssl_random_pseudo_bytes( $p_bytes, $t_strong );
if ( $t_random_bytes !== false ) {
if ( $p_require_strong_generator && $t_strong === true ) {
$t_random_string = $t_random_bytes;
} else if ( !$p_require_strong_generator ) {
$t_random_string = $t_random_bytes;
}
}
}

# Next we try to use the /dev/urandom PRNG provided on Linux systems. This
# is nowhere near as secure as /dev/random but it is still satisfactory for
# the needs of MantisBT, especially given the fact that we don't want this
# function to block while waiting for the system to generate more entropy.
$t_urandom_fp = @fopen( '/dev/urandom', 'rb' );
if ( $t_urandom_fp !== false ) {
$t_random_bytes = @fread( $t_urandom_fp, $p_bytes );
if ( $t_random_bytes !== false ) {
$t_random_string = $t_random_bytes;
}
@fclose( $t_urandom_fp );
}

# For Windows systems, we can try using Microsoft CryptoAPI to retrieve
# more reliable PRNG output than what PHP can provide by itself.
# !TODO

# At this point we've run out of possibilities for generating randomness
# from a strong source. Unless weak output is specifically allowed by the
# $p_require_strong_generator argument, we should return null as we've
# failed to generate randomness to a satisfactory security level.
if ( !isset( $t_random_string ) && $p_require_strong_generator ) {
return null;
}

# As a last resort we have to fall back to using the insecure Mersenne
# Twister pseudo random number generator provided in PHP. This DOES NOT
# produce cryptographically secure randomness and thus the output of the
# PRNG is easily guessable. In an attempt to make it harder to guess the
# internal state of the PRNG, we salt the PRNG output with a known secret
# and hash it.
if ( !isset( $t_random_string ) ) {
$t_secret_key = 'prng' . config_get_global( 'crypto_master_salt' );
$t_random_bytes = '';
for ( $i = 0; $i < $p_bytes; $i += 64 ) {
$t_random_segment = '';
for ( $j = 0; $j < 64; $j++ ) {
$t_random_segment .= base_convert( mt_rand(), 10, 36 );
}
$t_random_segment .= $i;
$t_random_segment .= $t_secret_key;
$t_random_bytes .= hash( 'whirlpool', $t_random_segment, true );
}
$t_random_string = substr( $t_random_bytes, 0, $p_bytes );
if ( $t_random_string === false ) {
return null; # Unexpected error
}
}

return $t_random_string;
}

/**
* Generate a strong random string (raw binary output) for cryptographic
* purposes such as nonces, IVs, default passwords, etc. If a strong source
* of randomness is not available, this function will fail and produce an
* error. Strong randomness is different from weak randomness in that a strong
* randomness generator doesn't produce predictable output and has much higher
* entropy. Where randomness is being used for cryptographic purposes, a strong
* source of randomness should always be used.
* @param int $p_bytes Number of bytes of strong randomness required
* @return string Raw binary string containing the requested number of bytes of random output
*/
function crypto_generate_strong_random_string( $p_bytes ) {
$t_random_string = crypto_generate_random_string( $p_bytes, true );
if ( $t_random_string === null ) {
trigger_error( ERROR_CRYPTO_CAN_NOT_GENERATE_STRONG_RANDOMNESS, ERROR );
}
return $t_random_string;
}
48 changes: 48 additions & 0 deletions docbook/adminguide/en/configuration.sgml
Expand Up @@ -161,6 +161,54 @@
</variablelist>
</section>

<section id="admin.config.security">
<title>Security and Cryptography</title>

<variablelist>
<varlistentry>
<term>$g_crypto_master_salt</term>
<listitem>
<para>Master salt value used for cryptographic hashing
throughout MantisBT. This value must be kept secret at
all costs. You must generate a unique and random salt
value for each installation of MantisBT you control.
The minimum length of this string must be at least 16
characters.
</para>
<para>The value you select for this salt should be a long
string generated using a secure random number
generator. An example for Linux systems is:
</para>
<para>cat /dev/urandom | head -c 64 | base64</para>
<para>Note that the number of bits of entropy per byte of
output from /dev/urandom is not 8. If you're
particularly paranoid and don't mind waiting a long
time, you could use /dev/random to get much closer to
8 bits of entropy per byte. Moving the mouse
(if possible) while generating entropy via /dev/random
will greatly improve the speed at which /dev/random
produces entropy.
</para>
<para>This setting is blank by default. MantisBT will not
operate in this state. Hence you are forced to change
the value of this configuration option.
</para>
<warning>
<para>WARNING: This configuration option has a profound
impact on the security of your MantisBT
installation. Failure to set this configuration
option correctly could lead to your MantisBT
installation being compromised. Ensure that this
value remains secret. Treat it with the same
security that you'd treat the password to your
MantisDB database.
</para>
</warning>
</listitem>
</varlistentry>

</section>

<section id="admin.config.signup">
<title>Signup and Lost Password</title>

Expand Down

0 comments on commit 045a897

Please sign in to comment.