8 Commits
0.18 ... 0.19

Author SHA1 Message Date
El RIDO
8881b3047a changing version string 2015-08-16 00:04:14 +02:00
Sebastien SAUVAGE
43a439e7d0 Time attack protection on hmac comparison
This fixes issue 2.7 of https://defuse.ca/audits/zerobin.htm, and thus
(with commit a24212afda90ca3e4b4ff5ce30d2012709b58a28) also issue 2.8.

(cherry picked from commit 0b4db7ece313dd268e51fc47a0293a649927558a)

Conflicts:
	index.php
2015-08-15 23:44:03 +02:00
Sebastien SAUVAGE
daf5522b1e Potentiel security bug corrected
Bug reproduction: 1) paste texte containing html/javascript. 2) send 3)
clic "Raw text"  4) refresh: The html/javascript is interpreted instead
of just displayed.
Under some versions of Chrome, it happens without refreshing.
This bug was corrected.

(cherry picked from commit 4f8750bbddcb137213529875e45e3ace3be9a769)
2015-08-15 22:24:25 +02:00
Sebastien SAUVAGE
e7feca0e53 Stronger server salt
ZeroBin now generates a much stronger salt. This fixes issue #68
(mentioned in section 2.1 of https://defuse.ca/audits/zerobin.htm)

(cherry picked from commit a24212afda90ca3e4b4ff5ce30d2012709b58a28)

Conflicts:
	lib/serversalt.php
	lib/vizhash16x16.php
2015-08-15 22:18:57 +02:00
4f72f04eda Prevent inconstitent /data/trafic_limiter.php due to file read while writing
(cherry picked from commit 71a7f6adaea9a86a84fa8ebbcb9e5c506a785527)

Conflicts:
	index.php
2015-08-15 22:10:05 +02:00
Sébastien SAUVAGE
5b54ca34ad Update index.php
Removed ugly error message when paste identifier is invalid (eg. http://mydomain.com/zerobin?foo)
(cherry picked from commit 43fa904979a29e4c205b9f4f08e1c487555bbe1c)

Conflicts:
	index.php
2015-08-15 22:07:07 +02:00
Sebastien SAUVAGE
bc8b23d35e XSS flaw correction
With a client IE < 10 there was a XSS security flaw. Other browsers were
not affected.
Also corrected spacing display with IE<10.

(cherry picked from commit 28813cd82ae47e556b610da3c7302a6709e27431)

Conflicts:
	CHANGELOG.md
	index.php
	js/zerobin.js
	lib/vizhash16x16.php
2015-08-15 22:01:43 +02:00
Sebastien SAUVAGE
d9930978ba Make sure there is enough entropy.
This patch will improve key randomness by requiring the user to move the
mouse if there is not enough entropy.

(cherry picked from commit c6e98045aa833dff824f892eb3392744c03a59f7)
2015-08-15 21:52:14 +02:00
20 changed files with 187 additions and 83 deletions

View File

@@ -63,4 +63,7 @@
* ADDED: Better json checking (including entropy).
* ADDED: Added version to js/css assets URLs in order to prevent some abusive caches to serve an obsolete version of these files when ZeroBin is upgraded.
* "Burn after reading" option has been moved out of Expiration combo to a separate checkbox. Reason is: You can prevent a read-once paste to be available ad vitam eternam on the net.
* **Alpha 0.19 (2013-07-05)**:
* Corrected XSS security flaw which affected IE<10. Other browsers were not affected.
* Corrected spacing display in IE<10.

View File

@@ -1,4 +1,4 @@
ZeroBin 0.18 Alpha
ZeroBin 0.19 Alpha
==== THIS IS ALPHA SOFTWARE - USE AT YOUR OWN RISKS ====

View File

@@ -1,4 +1,4 @@
/* ZeroBin 0.18 - http://sebsauvage.net/wiki/doku.php?id=php:zerobin */
/* ZeroBin 0.19 - http://sebsauvage.net/wiki/doku.php?id=php:zerobin */
/* CSS Reset from YUI 3.4.1 (build 4118) - Copyright 2011 Yahoo! Inc. All rights reserved.

View File

@@ -7,7 +7,7 @@
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
* @version 0.18
* @version 0.19
*/
// change this, if your php files and data is outside of your webservers document root

View File

@@ -6,7 +6,7 @@
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
* @version 0.18
* @version 0.19
*/
// Immediately start random number generator collector.
@@ -152,6 +152,9 @@ function pasteID() {
return window.location.search.substring(1);
}
function htmlEntities(str) {
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
/**
* Set text of a DOM element (required for IE)
* This is equivalent to element.text(text)
@@ -161,9 +164,9 @@ function pasteID() {
function setElementText(element, text) {
// For IE<10.
if ($('div#oldienotice').is(":visible")) {
// IE<10 do not support white-space:pre-wrap; so we have to do this BIG UGLY STINKING THING.
element.text(text.replace(/\n/ig,'{BIG_UGLY_STINKING_THING__OH_GOD_I_HATE_IE}'));
element.html(element.text().replace(/{BIG_UGLY_STINKING_THING__OH_GOD_I_HATE_IE}/ig,"\n<br />"));
// IE<10 does not support white-space:pre-wrap; so we have to do this BIG UGLY STINKING THING.
var html = htmlEntities(text).replace(/\n/ig,"\r\n<br>");
element.html('<pre>'+html+'</pre>');
}
// for other (sane) browsers:
else {
@@ -306,6 +309,7 @@ function send_comment(parentid) {
});
}
/**
* Send a new paste to server
*/
@@ -314,7 +318,17 @@ function send_data() {
if ($('textarea#message').val().length == 0) {
return;
}
// If sjcl has not collected enough entropy yet, display a message.
if (!sjcl.random.isReady())
{
showStatus('Sending paste (Please move your mouse for more entropy)...', spin=true);
sjcl.random.addEventListener('seeded', function(){ send_data(); });
return;
}
showStatus('Sending paste...', spin=true);
var randomkey = sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0), 0);
var cipherdata = zeroCipher(randomkey, $('textarea#message').val());
var data_to_send = { data: cipherdata,
@@ -425,10 +439,9 @@ function stateExistingPaste() {
*/
function rawText()
{
history.pushState(document.title, document.title, 'document.txt');
var paste = $('div#cleartext').text();
var newDoc = document.open('text/plain', 'replace');
newDoc.write(paste);
var paste = $('div#cleartext').html();
var newDoc = document.open('text/html', 'replace');
newDoc.write('<pre>'+paste+'</pre>');
newDoc.close();
}

View File

@@ -7,7 +7,7 @@
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
* @version 0.18
* @version 0.19
*/
spl_autoload_register('auto::loader');

View File

@@ -7,7 +7,7 @@
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
* @version 0.18
* @version 0.19
*/
/**
@@ -50,4 +50,35 @@ class filter
}
return number_format($size, ($i ? 2 : 0), '.', ' ') . ' ' . $iec[$i];
}
/**
* validate paste ID
*
* @access public
* @param string $dataid
* @return bool
*/
public static function is_valid_paste_id($dataid)
{
return (bool) preg_match('#\A[a-f\d]{16}\z#', $dataid);
}
/**
* fixed time string comparison operation to prevent timing attacks
* https://crackstation.net/hashing-security.htm?=rd#slowequals
*
* @access public
* @param string $a
* @param string $b
* @return bool
*/
public static function slow_equals($a, $b)
{
$diff = strlen($a) ^ strlen($b);
for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
{
$diff |= ord($a[$i]) ^ ord($b[$i]);
}
return $diff === 0;
}
}

View File

@@ -7,7 +7,7 @@
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
* @version 0.18
* @version 0.19
*/
/**
@@ -105,7 +105,7 @@ abstract class persistence
{
self::_initialize();
$file = self::$_path . '/' . $filename;
file_put_contents($file, $data);
file_put_contents($file, $data, LOCK_EX);
chmod($file, 0705);
return $file;
}

View File

@@ -7,7 +7,7 @@
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
* @version 0.18
* @version 0.19
*/
/**
@@ -39,8 +39,15 @@ class serversalt extends persistence
public static function generate()
{
$randomSalt = '';
for($i=0; $i<16; ++$i) {
$randomSalt .= base_convert(mt_rand(), 10, 16);
if (function_exists('mcrypt_create_iv'))
{
$randomSalt = bin2hex(mcrypt_create_iv(256, MCRYPT_DEV_URANDOM));
}
else // fallback to mt_rand()
{
for($i = 0; $i < 16; ++$i) {
$randomSalt .= base_convert(mt_rand(), 10, 16);
}
}
self::$_salt = $randomSalt;
return self::$_salt;

View File

@@ -7,7 +7,7 @@
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
* @version 0.18
* @version 0.19
*/
/**

View File

@@ -7,7 +7,7 @@
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
* @version 0.18
* @version 0.19
*/
/**

View File

@@ -8,7 +8,7 @@
* @link http://sebsauvage.net/wiki/doku.php?id=php:vizhash_gd
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
* @version 0.0.4 beta ZeroBin 0.18
* @version 0.0.4 beta ZeroBin 0.19
*/
/**

View File

@@ -7,7 +7,7 @@
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
* @version 0.18
* @version 0.19
*/
/**
@@ -20,7 +20,7 @@ class zerobin
/*
* @const string version
*/
const VERSION = 'Alpha 0.18';
const VERSION = 'Alpha 0.19';
/**
* @access private
@@ -272,8 +272,8 @@ class zerobin
$pasteid = $_POST['pasteid'];
$parentid = $_POST['parentid'];
if (
!preg_match('/\A[a-f\d]{16}\z/', $pasteid) ||
!preg_match('/\A[a-f\d]{16}\z/', $parentid)
!filter::is_valid_paste_id($pasteid) ||
!filter::is_valid_paste_id($parentid)
) $this->_return_message(1, 'Invalid data.');
// Comments do not expire (it's the paste that expires)
@@ -340,18 +340,21 @@ class zerobin
private function _delete($dataid, $deletetoken)
{
// Is this a valid paste identifier?
if (preg_match('\A[a-f\d]{16}\z', $dataid))
if (!filter::is_valid_paste_id($dataid))
{
// Check that paste exists.
if (!$this->_model()->exists($dataid))
{
$this->_error = 'Paste does not exist, has expired or has been deleted.';
return;
}
$this->_error = 'Invalid paste ID.';
return;
}
// Check that paste exists.
if (!$this->_model()->exists($dataid))
{
$this->_error = 'Paste does not exist, has expired or has been deleted.';
return;
}
// Make sure token is valid.
if ($deletetoken != hash_hmac('sha1', $dataid , serversalt::get()))
if (filter::slow_equals($deletetoken, hash_hmac('sha1', $dataid , serversalt::get())))
{
$this->_error = 'Wrong deletion token. Paste was not deleted.';
return;
@@ -372,60 +375,63 @@ class zerobin
private function _read($dataid)
{
// Is this a valid paste identifier?
if (preg_match('#\A[a-f\d]{16}\z#', $dataid))
if (!filter::is_valid_paste_id($dataid))
{
// Check that paste exists.
if ($this->_model()->exists($dataid))
$this->_error = 'Invalid paste ID.';
return;
}
// Check that paste exists.
if ($this->_model()->exists($dataid))
{
// Get the paste itself.
$paste = $this->_model()->read($dataid);
// See if paste has expired.
if (
isset($paste->meta->expire_date) &&
$paste->meta->expire_date < time()
)
{
// Get the paste itself.
$paste = $this->_model()->read($dataid);
// See if paste has expired.
if (
isset($paste->meta->expire_date) &&
$paste->meta->expire_date < time()
)
{
// Delete the paste
$this->_model()->delete($dataid);
$this->_error = 'Paste does not exist, has expired or has been deleted.';
}
// If no error, return the paste.
else
{
// We kindly provide the remaining time before expiration (in seconds)
if (
property_exists($paste->meta, 'expire_date')
) $paste->meta->remaining_time = $paste->meta->expire_date - time();
// The paste itself is the first in the list of encrypted messages.
$messages = array($paste);
// If it's a discussion, get all comments.
if (
property_exists($paste->meta, 'opendiscussion') &&
$paste->meta->opendiscussion
)
{
$messages = array_merge(
$messages,
$this->_model()->readComments($dataid)
);
}
$this->_data = json_encode($messages);
// If the paste was meant to be read only once, delete it.
if (
property_exists($paste->meta, 'burnafterreading') &&
$paste->meta->burnafterreading
) $this->_model()->delete($dataid);
}
// Delete the paste
$this->_model()->delete($dataid);
$this->_error = 'Paste does not exist, has expired or has been deleted.';
}
// If no error, return the paste.
else
{
$this->_error = 'Paste does not exist or has expired.';
// We kindly provide the remaining time before expiration (in seconds)
if (
property_exists($paste->meta, 'expire_date')
) $paste->meta->remaining_time = $paste->meta->expire_date - time();
// The paste itself is the first in the list of encrypted messages.
$messages = array($paste);
// If it's a discussion, get all comments.
if (
property_exists($paste->meta, 'opendiscussion') &&
$paste->meta->opendiscussion
)
{
$messages = array_merge(
$messages,
$this->_model()->readComments($dataid)
);
}
$this->_data = json_encode($messages);
// If the paste was meant to be read only once, delete it.
if (
property_exists($paste->meta, 'burnafterreading') &&
$paste->meta->burnafterreading
) $this->_model()->delete($dataid);
}
}
else
{
$this->_error = 'Paste does not exist or has expired.';
}
}
/**

View File

@@ -7,7 +7,7 @@
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
* @version 0.18
* @version 0.19
*/
/**

View File

@@ -7,7 +7,7 @@
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
* @version 0.18
* @version 0.19
*/
/**

View File

@@ -7,7 +7,7 @@
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
* @version 0.18
* @version 0.19
*/
/**

View File

@@ -44,4 +44,25 @@ class filterTest extends PHPUnit_Framework_TestCase
$this->assertEquals('1.00 YiB', filter::size_humanreadable(1024 * $exponent));
$this->assertEquals('1.21 YiB', filter::size_humanreadable(1234 * $exponent));
}
public function testPasteIdValidation()
{
$this->assertTrue(filter::is_valid_paste_id('a242ab7bdfb2581a'), 'valid paste id');
$this->assertFalse(filter::is_valid_paste_id('foo'), 'invalid hex values');
$this->assertFalse(filter::is_valid_paste_id('../bar/baz'), 'path attack');
}
public function testSlowEquals()
{
$this->assertTrue(filter::slow_equals('foo', 'foo'), 'same string');
$this->assertFalse(filter::slow_equals('foo', true), 'string and boolean');
$this->assertFalse(filter::slow_equals('foo', 0), 'string and integer');
$this->assertFalse(filter::slow_equals('123foo', 123), 'string and integer');
$this->assertFalse(filter::slow_equals('123foo', '123'), 'different strings');
$this->assertFalse(filter::slow_equals('6', ' 6'), 'strings with space');
$this->assertFalse(filter::slow_equals('4.2', '4.20'), 'floats as strings');
$this->assertFalse(filter::slow_equals('1e3', '1000'), 'integers as strings');
$this->assertFalse(filter::slow_equals('9223372036854775807', '9223372036854775808'), 'large integers as strings');
$this->assertFalse(filter::slow_equals('61529519452809720693702583126814', '61529519452809720000000000000000'), 'larger integers as strings');
}
}

17
tst/mcrypt_mock.php Normal file
View File

@@ -0,0 +1,17 @@
<?php
define('MCRYPT_DEV_URANDOM', 1);
function mcrypt_create_iv($int, $flag)
{
$randomSalt = '';
for($i = 0; $i < 16; ++$i) {
$randomSalt .= base_convert(mt_rand(), 10, 16);
}
// hex2bin requires an even length, pad if necessary
if (strlen($randomSalt) % 2)
{
$randomSalt = '0' . $randomSalt;
}
return hex2bin($randomSalt);
}

View File

@@ -1,6 +1,7 @@
<phpunit bootstrap="bootstrap.php" colors="true">
<testsuite name="ZeroBin Test Suite">
<directory suffix=".php">./</directory>
<exclude>mcrypt_mock.php</exclude>
</testsuite>
<filter>
<whitelist>

View File

@@ -37,5 +37,10 @@ class vizhash16x16Test extends PHPUnit_Framework_TestCase
$this->assertEquals('image/png', $finfo->file($this->_file));
$this->assertNotEquals($pngdata, $vz->generate('2001:1620:2057:dead:beef::cafe:babe'));
$this->assertEquals($pngdata, $vz->generate('127.0.0.1'));
// generating new salt
$salt = serversalt::get();
require 'mcrypt_mock.php';
$this->assertNotEquals($salt, serversalt::generate());
}
}