Skip to content
This repository was archived by the owner on Sep 10, 2021. It is now read-only.

Commit 40a1998

Browse files
author
Jamie Snape
committed
Revise random number, string, and UUID generation to improve security
1 parent b8bae79 commit 40a1998

40 files changed

+288
-127
lines changed

composer.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@
2121
"leafo/scssphp": "~0.1.1",
2222
"maennchen/zipstream-php": "~0.2.2",
2323
"michelf/php-markdown": "~1.4.1",
24+
"moontoast/math": "~1.1.0",
2425
"pear-pear.php.net/XML_Serializer": "~0.20.2",
2526
"reprovinci/solr-php-client": "~1.0.3",
27+
"rhumsaa/uuid": "~2.8.0",
2628
"sendgrid/sendgrid": "~2.1.1",
29+
"symfony/console": "~2.5.7",
2730
"zendframework/zendframework1": "~1.12.9"
2831
},
2932
"require-dev": {
@@ -42,6 +45,7 @@
4245
"ext-imagick": "*",
4346
"ext-ldap": "*",
4447
"ext-memcached": "*",
48+
"ext-openssl": "*",
4549
"ext-zip": "*"
4650
},
4751
"autoload": {

core/controllers/InstallController.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class InstallController extends AppController
2525
{
2626
public $_models = array('User', 'Assetstore');
2727
public $_daos = array('Assetstore');
28-
public $_components = array('Utility');
28+
public $_components = array('Random', 'Utility');
2929
public $_forms = array('Install');
3030

3131
/**
@@ -172,7 +172,16 @@ public function step2Action()
172172
// Must generate and store our password salt before we create our first user
173173
$options = array('allowModifications' => true);
174174
$applicationConfig = new Zend_Config_Ini(CORE_CONFIGS_PATH.'/application.ini', null, $options);
175-
$applicationConfig->global->password->prefix = UtilityComponent::generateRandomString(32);
175+
176+
if (extension_loaded('openssl')) {
177+
$factory = new \RandomLib\Factory();
178+
$generator = $factory->getHighStrengthGenerator();
179+
$prefix = $generator->generateString(32);
180+
} else {
181+
$prefix = $this->Component->Random->generateString(32);
182+
}
183+
184+
$applicationConfig->global->password->prefix = $prefix;
176185
$applicationConfig->global->gravatar = $form->getValue('gravatar');
177186

178187
$writer = new Zend_Config_Writer_Ini();

core/controllers/UserController.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class UserController extends AppController
3838
'Setting',
3939
);
4040
public $_daos = array('User', 'Folder', 'Folderpolicygroup', 'Folderpolicyuser', 'Group');
41-
public $_components = array('Breadcrumb', 'Date', 'Filter', 'Sortdao');
41+
public $_components = array('Breadcrumb', 'Date', 'Filter', 'Random', 'Sortdao');
4242
public $_forms = array('User');
4343

4444
/** Init Controller */
@@ -110,7 +110,7 @@ public function recoverpasswordAction()
110110
}
111111
}
112112

113-
$pass = UtilityComponent::generateRandomString(10);
113+
$pass = $this->Component->Random->generateString(32);
114114
$this->User->changePassword($user, $pass);
115115

116116
$url = $this->getServerURL().$this->view->webroot;
@@ -206,7 +206,7 @@ public function ajaxregisterAction()
206206
$nopass = (bool) $this->getParam('nopassword');
207207
if ($adminCreate && $nopass) {
208208
$form->populate($this->getRequest()->getPost());
209-
$passwd = UtilityComponent::generateRandomString(32);
209+
$passwd = $this->Component->Random->generateString(32);
210210
$form->getElement('password1')->setValue($passwd);
211211
$form->getElement('password2')->setValue($passwd);
212212

@@ -891,7 +891,7 @@ public function settingsAction()
891891
return;
892892
}
893893

894-
$tmpPath = $this->getDataDirectory('thumbnail').'/'.mt_rand(1, 1000);
894+
$tmpPath = $this->getDataDirectory('thumbnail').'/'.$this->Component->Random->generateInt();
895895
if (!file_exists($this->getDataDirectory('thumbnail'))) {
896896
throw new Zend_Exception(
897897
"Thumbnail path does not exist: ".$this->getDataDirectory('thumbnail')
@@ -900,15 +900,15 @@ public function settingsAction()
900900
if (!file_exists($tmpPath)) {
901901
mkdir($tmpPath);
902902
}
903-
$tmpPath .= '/'.mt_rand(1, 1000);
903+
$tmpPath .= '/'.$this->Component->Random->generateInt();
904904
if (!file_exists($tmpPath)) {
905905
mkdir($tmpPath);
906906
}
907-
$destionation = $tmpPath."/".mt_rand(1, 1000).'.jpeg';
908-
while (file_exists($destionation)) {
909-
$destionation = $tmpPath."/".mt_rand(1, 1000).'.jpeg';
907+
$destination = $tmpPath."/".$this->Component->Random->generateInt().'.jpg';
908+
while (file_exists($destination)) {
909+
$destination = $tmpPath."/".$this->Component->Random->generateInt().'.jpg';
910910
}
911-
$pathThumbnail = $destionation;
911+
$pathThumbnail = $destination;
912912

913913
list ($x, $y) = getimagesize($path); //--- get size of img ---
914914
$thumb = 32; //--- max. size of thumb ---

core/controllers/components/ExportComponent.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ public function exportBitstreams($userDao, $targetDir, $itemIds, $shouldSymLink)
124124

125125
// process the items which pass the ITEM level policy check
126126
if (!empty($revisions)) {
127+
/** @var RandomComponent $randomComponent */
128+
$randomComponent = MidasLoader::loadComponent('Random');
129+
127130
foreach ($revisions as $revision) {
128131
$itemId = $revision->getItemId();
129132
$this->_createItemDirectory($targetDir.'/'.$itemId);
@@ -142,7 +145,7 @@ public function exportBitstreams($userDao, $targetDir, $itemIds, $shouldSymLink)
142145
// for symbolic link option,if multiple bitstreams (in a single item revision)
143146
// have the same file name, add a '.new' suffix to distinguish them
144147
if (file_exists($dest)) {
145-
$dest .= '.'.mt_rand().'.new';
148+
$dest .= '.'.$randomComponent->generateInt().'.new';
146149
}
147150
if (!symlink($source, $dest)) {
148151
throw new Zend_Exception("Cannot create symlink: ".$dest."linked to".$source);

core/controllers/components/HttpuploadComponent.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,18 @@ public function generateToken($args, $dirname = '')
6868
throw new Exception('Failed to create temporary upload dir', MIDAS_HTTPUPLOAD_TMP_DIR_CREATION_FAILED);
6969
}
7070
}
71-
$unique_identifier = 'midas'.uniqid().'-'.md5($args['filename']);
71+
/** @var RandomComponent $randomComponent */
72+
$randomComponent = MidasLoader::loadComponent('Random');
73+
$uniqueIdentifier = $randomComponent->generateString(64);
7274
if ($dirname != '') {
73-
$unique_identifier = $dirname.'/'.$unique_identifier;
75+
$uniqueIdentifier = $dirname.'/'.$uniqueIdentifier;
7476
}
75-
if (file_exists(UtilityComponent::getTempDirectory().'/'.$unique_identifier)) {
77+
if (file_exists(UtilityComponent::getTempDirectory().'/'.$uniqueIdentifier)) {
7678
throw new Exception('Failed to generate upload token', MIDAS_HTTPUPLOAD_UPLOAD_TOKEN_GENERATION_FAILED);
7779
}
78-
touch(UtilityComponent::getTempDirectory().'/'.$unique_identifier);
80+
touch(UtilityComponent::getTempDirectory().'/'.$uniqueIdentifier);
7981

80-
return array('token' => $unique_identifier);
82+
return array('token' => $uniqueIdentifier);
8183
}
8284

8385
/** Handle the upload */
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
/*=========================================================================
3+
MIDAS Server
4+
Copyright (c) Kitware SAS. 26 rue Louis Guérin. 69100 Villeurbanne, FRANCE
5+
All rights reserved.
6+
More information http://www.kitware.com
7+
8+
Licensed under the Apache License, Version 2.0 (the "License");
9+
you may not use this file except in compliance with the License.
10+
You may obtain a copy of the License at
11+
12+
http://www.apache.org/licenses/LICENSE-2.0.txt
13+
14+
Unless required by applicable law or agreed to in writing, software
15+
distributed under the License is distributed on an "AS IS" BASIS,
16+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
See the License for the specific language governing permissions and
18+
limitations under the License.
19+
=========================================================================*/
20+
21+
/** Random component for generating random numbers. */
22+
class RandomComponent extends AppComponent
23+
{
24+
/** @var \RandomLib\Factory */
25+
protected $_factory = null;
26+
27+
/** @var \RandomLib\Generator */
28+
protected $_generator = null;
29+
30+
/**
31+
* Generate a medium-strength random integer within the given range.
32+
*
33+
* @param int $minimum lower bound of the range
34+
* @param int $maximum upper bound of the range
35+
* @return int
36+
*/
37+
public function generateInt($minimum = 0, $maximum = PHP_INT_MAX)
38+
{
39+
if (is_null($this->_factory)) {
40+
$this->_factory = new \RandomLib\Factory;
41+
$this->_generator = $this->_factory->getMediumStrengthGenerator();
42+
}
43+
44+
return $this->_generator->generateInt($minimum, $maximum);
45+
}
46+
47+
/**
48+
* Generate a medium-strength random string of the given length.
49+
*
50+
* @param int $length length of the generated string
51+
* @param string $characters characters to use to generate the string
52+
* @return string
53+
*/
54+
public function generateString($length, $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
55+
{
56+
if (is_null($this->_factory)) {
57+
$this->_factory = new \RandomLib\Factory;
58+
$this->_generator = $this->_factory->getMediumStrengthGenerator();
59+
}
60+
61+
return $this->_generator->generateString($length, $characters);
62+
}
63+
}

core/controllers/components/UtilityComponent.php

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -315,12 +315,7 @@ public static function safedelete($filename)
315315
/** Function to run a SQL script */
316316
public static function run_sql_from_file($db, $sqlfile)
317317
{
318-
try {
319-
$db->getConnection();
320-
} catch (Zend_Exception $exception) {
321-
throw new Zend_Exception("Unable to connect.");
322-
}
323-
318+
$db->getConnection();
324319
$sql = '';
325320
$lines = file($sqlfile);
326321
foreach ($lines as $line) {
@@ -630,28 +625,19 @@ public static function getServerURL()
630625
}
631626

632627
/**
633-
* Generate a string of random characters. Seeds RNG within the function using microtime.
628+
* Generate a medium-strength random string of the given length.
634629
*
635-
* @param $length The length of the random string
636-
* @param $alphabet (Optional) The alphabet string; if none provided, uses [a-zA-z0-9]
630+
* @deprecated since 3.3.0
631+
* @param int $length length of the generated string
632+
* @param string $characters characters to use to generate the string
633+
* @return string
637634
*/
638-
public static function generateRandomString($length, $alphabet = null)
635+
public static function generateRandomString($length, $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
639636
{
640-
if (!is_string($alphabet) || empty($alphabet)) {
641-
$alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
642-
}
643-
644-
// Seed RNG with microtime (for lack of something more difficult to guess)
645-
list($usec, $sec) = explode(' ', microtime());
646-
srand((float) $sec + ((float) $usec * 100000));
647-
648-
$salt = '';
649-
$max = strlen($alphabet) - 1;
650-
for ($i = 0; $i < $length; $i++) {
651-
$salt .= substr($alphabet, mt_rand(0, $max), 1);
652-
}
637+
/** @var RandomComponent $randomComponent */
638+
$randomComponent = MidasLoader::loadComponent('Random');
653639

654-
return $salt;
640+
return $randomComponent->generateString($length, $characters);
655641
}
656642

657643
/**

core/controllers/components/UuidComponent.php

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,47 +18,72 @@
1818
limitations under the License.
1919
=========================================================================*/
2020

21-
/** UuidComponent component */
21+
/** UUID component for generating UUIDs and searching by UUID. */
2222
class UuidComponent extends AppComponent
2323
{
24-
/** Get using id */
24+
/**
25+
* Generate a version 4 UUID.
26+
*
27+
* @return string
28+
*/
29+
public function generate()
30+
{
31+
return str_replace('-', '', \Rhumsaa\Uuid\Uuid::uuid4()->toString());
32+
}
33+
34+
/**
35+
* Return a resource given its unique id.
36+
*
37+
* @param string $uuid UUID
38+
* @return false|CommunityDao|FolderDao|ItemDao|ItemRevisionDao|UserDao
39+
*/
2540
public function getByUid($uuid)
2641
{
42+
/** @var CommunityModel $model */
2743
$model = MidasLoader::loadModel('Community');
2844
$dao = $model->getByUuid($uuid);
29-
if ($dao != false) {
45+
46+
if ($dao !== false) {
3047
$dao->resourceType = MIDAS_RESOURCE_COMMUNITY;
3148

3249
return $dao;
3350
}
3451

52+
/** @var FolderModel $model */
3553
$model = MidasLoader::loadModel('Folder');
3654
$dao = $model->getByUuid($uuid);
37-
if ($dao != false) {
55+
56+
if ($dao !== false) {
3857
$dao->resourceType = MIDAS_RESOURCE_FOLDER;
3958

4059
return $dao;
4160
}
4261

62+
/** @var ItemModel $model */
4363
$model = MidasLoader::loadModel('Item');
4464
$dao = $model->getByUuid($uuid);
45-
if ($dao != false) {
65+
66+
if ($dao !== false) {
4667
$dao->resourceType = MIDAS_RESOURCE_ITEM;
4768

4869
return $dao;
4970
}
5071

72+
/** @var ItemRevisionModel $model */
5173
$model = MidasLoader::loadModel('ItemRevision');
5274
$dao = $model->getByUuid($uuid);
53-
if ($dao != false) {
75+
76+
if ($dao !== false) {
5477
$dao->resourceType = MIDAS_RESOURCE_REVISION;
5578

5679
return $dao;
5780
}
5881

82+
/** @var UserModel $model */
5983
$model = MidasLoader::loadModel('User');
6084
$dao = $model->getByUuid($uuid);
61-
if ($dao != false) {
85+
86+
if ($dao !== false) {
6287
$dao->resourceType = MIDAS_RESOURCE_USER;
6388

6489
return $dao;

core/controllers/forms/ImportForm.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ public function createImportForm($assetstores)
3131
$form->setMethod('post');
3232
$form->setAttrib('class', 'importForm');
3333

34+
$randomComponent = MidasLoader::loadComponent('Random');
35+
3436
// Hidden upload id
35-
$uploadId = new Zend_Form_Element_Hidden('uploadid', array('value' => mt_rand()));
37+
$uploadId = new Zend_Form_Element_Hidden('uploadid', array('value' => $randomComponent->generateInt()));
3638
$uploadId->setDecorators(
3739
array(
3840
'ViewHelper',

core/models/base/CommunityModelBase.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ abstract public function getByFolder($folder);
106106
public function save($dao)
107107
{
108108
if (!isset($dao->uuid) || empty($dao->uuid)) {
109-
$dao->setUuid(uniqid().md5(mt_rand()));
109+
/** @var UuidComponent $uuidComponent */
110+
$uuidComponent = MidasLoader::loadComponent('Uuid');
111+
$dao->setUuid($uuidComponent->generate());
110112
}
111113
$name = $dao->getName();
112114
if (empty($name) && $name !== '0') {

0 commit comments

Comments
 (0)