diff --git a/cfg/conf.ini.sample b/cfg/conf.ini.sample index 60c25871..8e1204f9 100644 --- a/cfg/conf.ini.sample +++ b/cfg/conf.ini.sample @@ -53,6 +53,12 @@ languageselection = false ; the pastes encryption key ; urlshortener = "https://shortener.example.com/api?link=" +; (optional) vizhash is a weak mechanism to detect if a comment was from a +; different user when the same username was used in a comment. It is based on +; the IP and might be used to get the posters IP if the server salt is leaked +; and a rainbow table is generated for all IPs. Enabled by default. +; vizhash = false + ; stay compatible with PrivateBin Alpha 0.19, less secure ; if enabled will use base64.js version 1.7 instead of 2.1.9 and sha1 instead of ; sha256 in HMAC for the deletion token diff --git a/lib/configuration.php b/lib/configuration.php index 18a6a401..c99ddda3 100644 --- a/lib/configuration.php +++ b/lib/configuration.php @@ -44,6 +44,7 @@ class configuration 'languageselection' => false, 'languagedefault' => '', 'urlshortener' => '', + 'vizhash' => true, 'zerobincompatibility' => false, ), 'expire' => array( diff --git a/lib/model/comment.php b/lib/model/comment.php index 1d180bfc..9393b016 100644 --- a/lib/model/comment.php +++ b/lib/model/comment.php @@ -174,16 +174,19 @@ class model_comment extends model_abstract if (!sjcl::isValid($nickname)) throw new Exception('Invalid data.', 66); $this->_data->meta->nickname = $nickname; - // Generation of the anonymous avatar (Vizhash): - // If a nickname is provided, we generate a Vizhash. - // (We assume that if the user did not enter a nickname, he/she wants - // to be anonymous and we will not generate the vizhash.) - $vh = new vizhash16x16(); - $pngdata = $vh->generate(trafficlimiter::getIp()); - if ($pngdata != '') + if ($this->_conf->getKey('vizhash')) { - $this->_data->meta->vizhash = 'data:image/png;base64,' . base64_encode($pngdata); + // Generation of the anonymous avatar (Vizhash): + // If a nickname is provided, we generate a Vizhash. + // (We assume that if the user did not enter a nickname, he/she wants + // to be anonymous and we will not generate the vizhash.) + $vh = new vizhash16x16(); + $pngdata = $vh->generate(trafficlimiter::getIp()); + if ($pngdata != '') + { + $this->_data->meta->vizhash = 'data:image/png;base64,' . base64_encode($pngdata); + } + // Once the avatar is generated, we do not keep the IP address, nor its hash. } - // Once the avatar is generated, we do not keep the IP address, nor its hash. } } diff --git a/lib/privatebin/db.php b/lib/privatebin/db.php index 89ff7847..08b495e1 100644 --- a/lib/privatebin/db.php +++ b/lib/privatebin/db.php @@ -325,6 +325,13 @@ class privatebin_db extends privatebin_abstract */ public function createComment($pasteid, $parentid, $commentid, $comment) { + foreach (array('nickname', 'vizhash') as $key) + { + if (!array_key_exists($key, $comment['meta'])) + { + $comment['meta'][$key] = null; + } + } return self::_exec( 'INSERT INTO ' . self::_sanitizeIdentifier('comment') . ' VALUES(?,?,?,?,?,?,?)', @@ -367,9 +374,9 @@ class privatebin_db extends privatebin_abstract $comments[$i]->data = $row['data']; $comments[$i]->meta = new stdClass; $comments[$i]->meta->postdate = (int) $row['postdate']; - if (array_key_exists('nickname', $row)) + if (array_key_exists('nickname', $row) && !empty($row['nickname'])) $comments[$i]->meta->nickname = $row['nickname']; - if (array_key_exists('vizhash', $row)) + if (array_key_exists('vizhash', $row) && !empty($row['vizhash'])) $comments[$i]->meta->vizhash = $row['vizhash']; } ksort($comments); diff --git a/tst/model.php b/tst/model.php index 8009f892..92f8b652 100644 --- a/tst/model.php +++ b/tst/model.php @@ -208,4 +208,59 @@ class modelTest extends PHPUnit_Framework_TestCase $paste->store(); $paste->getComment(helper::getPasteId())->delete(); } + + public function testCommentWithDisabledVizhash() + { + $options = parse_ini_file(CONF, true); + $options['main']['vizhash'] = false; + $options['model'] = array( + 'class' => 'privatebin_db', + ); + $options['model_options'] = array( + 'dsn' => 'sqlite::memory:', + 'usr' => null, + 'pwd' => null, + 'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION), + ); + helper::confBackup(); + helper::createIniFile(CONF, $options); + $model = new model(new configuration); + + $pasteData = helper::getPaste(); + $this->_model->getPaste(helper::getPasteId())->delete(); + $paste = $model->getPaste(helper::getPasteId()); + $this->assertFalse($paste->exists(), 'paste does not yet exist'); + + $paste = $model->getPaste(); + $paste->setData($pasteData['data']); + $paste->setOpendiscussion(); + $paste->setFormatter($pasteData['meta']['formatter']); + $paste->store(); + + $paste = $model->getPaste(helper::getPasteId()); + $this->assertTrue($paste->exists(), 'paste exists after storing it'); + $paste = $paste->get(); + $this->assertEquals($pasteData['data'], $paste->data); + foreach (array('opendiscussion', 'formatter') as $key) { + $this->assertEquals($pasteData['meta'][$key], $paste->meta->$key); + } + + // storing comments + $commentData = helper::getComment(); + $paste = $model->getPaste(helper::getPasteId()); + $comment = $paste->getComment(helper::getPasteId(), helper::getCommentId()); + $this->assertFalse($comment->exists(), 'comment does not yet exist'); + + $comment = $paste->getComment(helper::getPasteId()); + $comment->setData($commentData['data']); + $comment->setNickname($commentData['meta']['nickname']); + $comment->store(); + + $comment = $paste->getComment(helper::getPasteId(), helper::getCommentId()); + $this->assertTrue($comment->exists(), 'comment exists after storing it'); + $comment = $comment->get(); + $this->assertEquals($commentData['data'], $comment->data); + $this->assertEquals($commentData['meta']['nickname'], $comment->meta->nickname); + $this->assertFalse(property_exists($comment->meta, 'vizhash'), 'vizhash was not generated'); + } }