introduce new zerobincompatibility option, replacing the base64 one, if it is enabled, delete tokens use sha256; added per paste salt with server salt fallback; this resolves the points 2.2 & 2.9 in #103
This commit is contained in:
parent
6b0b814dc6
commit
0e217a42c5
8 changed files with 87 additions and 38 deletions
|
@ -39,10 +39,6 @@ template = "bootstrap"
|
|||
; (optional) notice to display
|
||||
; notice = "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service."
|
||||
|
||||
; base64.js library version, defaults to 2.1.9
|
||||
; use "1.7" if you are upgrading from a ZeroBin Alpha 0.19 installation
|
||||
base64version = "2.1.9"
|
||||
|
||||
; by default ZeroBin will guess the visitors language based on the browsers
|
||||
; settings. Optionally you can enable the language selection menu, which uses
|
||||
; a session cookie to store the choice until the browser is closed.
|
||||
|
@ -57,6 +53,11 @@ languageselection = false
|
|||
; the pastes encryption key
|
||||
; urlshortener = "https://shortener.example.com/api?link="
|
||||
|
||||
; stay compatible with ZeroBin 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
|
||||
zerobincompatibility = false
|
||||
|
||||
[expire]
|
||||
; expire value that is selected per default
|
||||
; make sure the value exists in [expire_options]
|
||||
|
@ -121,4 +122,4 @@ dir = PATH "data"
|
|||
;dsn = "sqlite:" PATH "data/db.sq3"
|
||||
;usr = null
|
||||
;pwd = null
|
||||
;opt[12] = true ; PDO::ATTR_PERSISTENT
|
||||
;opt[12] = true ; PDO::ATTR_PERSISTENT
|
||||
|
|
|
@ -41,10 +41,10 @@ class configuration
|
|||
'sizelimit' => 2097152,
|
||||
'template' => 'bootstrap',
|
||||
'notice' => '',
|
||||
'base64version' => '2.1.9',
|
||||
'languageselection' => false,
|
||||
'languagedefault' => '',
|
||||
'urlshortener' => '',
|
||||
'zerobincompatibility' => false,
|
||||
),
|
||||
'expire' => array(
|
||||
'default' => '1week',
|
||||
|
|
|
@ -27,7 +27,7 @@ class model_paste extends model_abstract
|
|||
public function get()
|
||||
{
|
||||
$this->_data = $this->_store->read($this->getId());
|
||||
// See if paste has expired and delete it if neccessary.
|
||||
// check if paste has expired and delete it if neccessary.
|
||||
if (property_exists($this->_data->meta, 'expire_date'))
|
||||
{
|
||||
if ($this->_data->meta->expire_date < time())
|
||||
|
@ -52,6 +52,12 @@ class model_paste extends model_abstract
|
|||
$this->_data->meta->formatter = $this->_conf->getKey('defaultformatter');
|
||||
}
|
||||
}
|
||||
|
||||
// support old paste format with server wide salt
|
||||
if (!property_exists($this->_data->meta, 'salt'))
|
||||
{
|
||||
$this->_data->meta->salt = serversalt::get();
|
||||
}
|
||||
$this->_data->comments = array_values($this->getComments());
|
||||
$this->_data->comment_count = count($this->_data->comments);
|
||||
$this->_data->comment_offset = 0;
|
||||
|
@ -73,6 +79,7 @@ class model_paste extends model_abstract
|
|||
throw new Exception('You are unlucky. Try again.', 75);
|
||||
|
||||
$this->_data->meta->postdate = time();
|
||||
$this->_data->meta->salt = serversalt::generate();
|
||||
|
||||
// store paste
|
||||
if (
|
||||
|
@ -151,7 +158,12 @@ class model_paste extends model_abstract
|
|||
*/
|
||||
public function getDeleteToken()
|
||||
{
|
||||
return hash_hmac('sha1', $this->getId(), serversalt::get());
|
||||
if (!property_exists($this->_data->meta, 'salt')) $this->get();
|
||||
return hash_hmac(
|
||||
$this->_conf->getKey('zerobincompatibility') ? 'sha1' : 'sha256',
|
||||
$this->getId(),
|
||||
$this->_data->meta->salt
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -327,7 +327,6 @@ class zerobin
|
|||
else
|
||||
{
|
||||
// Make sure the token is valid.
|
||||
serversalt::setPath($this->_conf->getKey('dir', 'traffic'));
|
||||
if (filter::slow_equals($deletetoken, $paste->getDeleteToken()))
|
||||
{
|
||||
// Paste exists and deletion token is valid: Delete the paste.
|
||||
|
@ -364,6 +363,7 @@ class zerobin
|
|||
{
|
||||
$data = $paste->get();
|
||||
$this->_doesExpire = property_exists($data, 'meta') && property_exists($data->meta, 'expire_date');
|
||||
if (property_exists($data->meta, 'salt')) unset($data->meta->salt);
|
||||
$this->_data = json_encode($data);
|
||||
}
|
||||
else
|
||||
|
@ -439,7 +439,7 @@ class zerobin
|
|||
$page->assign('BURNAFTERREADINGSELECTED', $this->_conf->getKey('burnafterreadingselected'));
|
||||
$page->assign('PASSWORD', $this->_conf->getKey('password'));
|
||||
$page->assign('FILEUPLOAD', $this->_conf->getKey('fileupload'));
|
||||
$page->assign('BASE64JSVERSION', $this->_conf->getKey('base64version'));
|
||||
$page->assign('BASE64JSVERSION', $this->_conf->getKey('zerobincompatibility') ? '1.7' : '2.1.9');
|
||||
$page->assign('LANGUAGESELECTION', $languageselection);
|
||||
$page->assign('LANGUAGES', i18n::getLanguageLabels(i18n::getAvailableLanguages()));
|
||||
$page->assign('EXPIRE', $expire);
|
||||
|
|
|
@ -85,6 +85,7 @@ class helper
|
|||
public static function getPasteWithAttachment($meta = array())
|
||||
{
|
||||
$example = self::$paste;
|
||||
$example['meta']['salt'] = serversalt::generate();
|
||||
$example['meta'] = array_merge($example['meta'], $meta);
|
||||
return $example;
|
||||
}
|
||||
|
@ -97,6 +98,8 @@ class helper
|
|||
public static function getPasteAsJson($meta = array())
|
||||
{
|
||||
$example = self::getPaste();
|
||||
// the JSON shouldn't contain the salt
|
||||
unset($example['meta']['salt']);
|
||||
if (count($meta))
|
||||
$example['meta'] = $meta;
|
||||
$example['comments'] = array();
|
||||
|
|
|
@ -13,10 +13,10 @@ class configurationTest extends PHPUnit_Framework_TestCase
|
|||
'sizelimit' => 2097152,
|
||||
'template' => 'bootstrap',
|
||||
'notice' => '',
|
||||
'base64version' => '2.1.9',
|
||||
'languageselection' => false,
|
||||
'languagedefault' => '',
|
||||
'urlshortener' => '',
|
||||
'zerobincompatibility' => false,
|
||||
),
|
||||
'expire' => array(
|
||||
'default' => '1week',
|
||||
|
|
|
@ -46,13 +46,14 @@ class jsonApiTest extends PHPUnit_Framework_TestCase
|
|||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||
$this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
$paste = $this->_model->read($response['id']);
|
||||
$this->assertEquals(
|
||||
hash_hmac('sha1', $response['id'], serversalt::get()),
|
||||
hash_hmac('sha256', $response['id'], $paste->meta->salt),
|
||||
$response['deletetoken'],
|
||||
'outputs valid delete token'
|
||||
);
|
||||
$this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,13 +81,14 @@ class jsonApiTest extends PHPUnit_Framework_TestCase
|
|||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||
$this->assertEquals(helper::getPasteId(), $response['id'], 'outputted paste ID matches input');
|
||||
$this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
$paste = $this->_model->read($response['id']);
|
||||
$this->assertEquals(
|
||||
hash_hmac('sha1', $response['id'], serversalt::get()),
|
||||
hash_hmac('sha256', $response['id'], $paste->meta->salt),
|
||||
$response['deletetoken'],
|
||||
'outputs valid delete token'
|
||||
);
|
||||
$this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -97,9 +99,10 @@ class jsonApiTest extends PHPUnit_Framework_TestCase
|
|||
$this->reset();
|
||||
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists before deleting data');
|
||||
$paste = $this->_model->read(helper::getPasteId());
|
||||
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||
file_put_contents($file, http_build_query(array(
|
||||
'deletetoken' => hash_hmac('sha1', helper::getPasteId(), serversalt::get()),
|
||||
'deletetoken' => hash_hmac('sha256', helper::getPasteId(), $paste->meta->salt),
|
||||
)));
|
||||
request::setInputStream($file);
|
||||
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||
|
@ -121,9 +124,10 @@ class jsonApiTest extends PHPUnit_Framework_TestCase
|
|||
$this->reset();
|
||||
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists before deleting data');
|
||||
$paste = $this->_model->read(helper::getPasteId());
|
||||
$_POST = array(
|
||||
'action' => 'delete',
|
||||
'deletetoken' => hash_hmac('sha1', helper::getPasteId(), serversalt::get()),
|
||||
'deletetoken' => hash_hmac('sha256', helper::getPasteId(), $paste->meta->salt),
|
||||
);
|
||||
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
|
@ -254,4 +258,4 @@ class jsonApiTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals('{}', $content, 'does not output nasty data');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -169,12 +169,13 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
$paste = $this->_model->read($response['id']);
|
||||
$this->assertEquals(
|
||||
hash_hmac('sha1', $response['id'], serversalt::get()),
|
||||
hash_hmac('sha256', $response['id'], $paste->meta->salt),
|
||||
$response['deletetoken'],
|
||||
'outputs valid delete token'
|
||||
);
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -288,13 +289,13 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
$paste = $this->_model->read($response['id']);
|
||||
$this->assertEquals(
|
||||
hash_hmac('sha1', $response['id'], serversalt::get()),
|
||||
hash_hmac('sha256', $response['id'], $paste->meta->salt),
|
||||
$response['deletetoken'],
|
||||
'outputs valid delete token'
|
||||
);
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
$paste = $this->_model->read($response['id']);
|
||||
$this->assertGreaterThanOrEqual($time + 300, $paste->meta->expire_date, 'time is set correctly');
|
||||
}
|
||||
|
||||
|
@ -320,13 +321,13 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
$paste = $this->_model->read($response['id']);
|
||||
$this->assertEquals(
|
||||
hash_hmac('sha1', $response['id'], serversalt::get()),
|
||||
hash_hmac('sha256', $response['id'], $paste->meta->salt),
|
||||
$response['deletetoken'],
|
||||
'outputs valid delete token'
|
||||
);
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
$paste = $this->_model->read($response['id']);
|
||||
$this->assertGreaterThanOrEqual($time + 300, $paste->meta->expire_date, 'time is set correctly');
|
||||
$this->assertEquals(1, $paste->meta->opendiscussion, 'discussion is enabled');
|
||||
}
|
||||
|
@ -351,12 +352,13 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
$paste = $this->_model->read($response['id']);
|
||||
$this->assertEquals(
|
||||
hash_hmac('sha1', $response['id'], serversalt::get()),
|
||||
hash_hmac('sha256', $response['id'], $paste->meta->salt),
|
||||
$response['deletetoken'],
|
||||
'outputs valid delete token'
|
||||
);
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -426,17 +428,17 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||
$this->assertEquals(
|
||||
hash_hmac('sha1', $response['id'], serversalt::get()),
|
||||
$response['deletetoken'],
|
||||
'outputs valid delete token'
|
||||
);
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
$original = json_decode(json_encode($_POST));
|
||||
$stored = $this->_model->read($response['id']);
|
||||
foreach (array('data', 'attachment', 'attachmentname') as $key) {
|
||||
$this->assertEquals($original->$key, $stored->$key);
|
||||
}
|
||||
$this->assertEquals(
|
||||
hash_hmac('sha256', $response['id'], $stored->meta->salt),
|
||||
$response['deletetoken'],
|
||||
'outputs valid delete token'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -459,12 +461,13 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
$paste = $this->_model->read($response['id']);
|
||||
$this->assertEquals(
|
||||
hash_hmac('sha1', $response['id'], serversalt::get()),
|
||||
hash_hmac('sha256', $response['id'], $paste->meta->salt),
|
||||
$response['deletetoken'],
|
||||
'outputs valid delete token'
|
||||
);
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -705,6 +708,7 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
unset($burnPaste['meta']['salt']);
|
||||
$this->assertContains(
|
||||
'<div id="cipherdata" class="hidden">' .
|
||||
htmlspecialchars(helper::getPasteAsJson($burnPaste['meta']), ENT_NOQUOTES) .
|
||||
|
@ -796,6 +800,7 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$oldPaste['meta']['formatter'] = 'plaintext';
|
||||
unset($oldPaste['meta']['salt']);
|
||||
$this->assertContains(
|
||||
'<div id="cipherdata" class="hidden">' .
|
||||
htmlspecialchars(helper::getPasteAsJson($oldPaste['meta']), ENT_NOQUOTES) .
|
||||
|
@ -813,8 +818,9 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||
$this->reset();
|
||||
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists before deleting data');
|
||||
$paste = $this->_model->read(helper::getPasteId());
|
||||
$_GET['pasteid'] = helper::getPasteId();
|
||||
$_GET['deletetoken'] = hash_hmac('sha1', helper::getPasteId(), serversalt::get());
|
||||
$_GET['deletetoken'] = hash_hmac('sha256', helper::getPasteId(), $paste->meta->salt);
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
|
@ -947,4 +953,27 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||
);
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste successfully deleted');
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testDeleteMissingPerPasteSalt()
|
||||
{
|
||||
$this->reset();
|
||||
$paste = helper::getPaste();
|
||||
unset($paste['meta']['salt']);
|
||||
$this->_model->create(helper::getPasteId(), $paste);
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists before deleting data');
|
||||
$_GET['pasteid'] = helper::getPasteId();
|
||||
$_GET['deletetoken'] = hash_hmac('sha256', helper::getPasteId(), serversalt::get());
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$this->assertRegExp(
|
||||
'#<div[^>]*id="status"[^>]*>.*Paste was properly deleted[^<]*</div>#s',
|
||||
$content,
|
||||
'outputs deleted status correctly'
|
||||
);
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste successfully deleted');
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue