mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-29 18:22:41 +01:00
Disallow email addresses which will overflow MySQL storage
Summary: Via HackerOne. An attacker can bypass `auth.email-domains` by registering with an email like: aaaaa...aaaaa@evil.com@company.com We'll validate the full string, then insert it into the database where it will be truncated, removing the `@company.com` part. Then we'll send an email to `@evil.com`. Instead, reject email addresses which won't fit in the table. `STRICT_ALL_TABLES` stops this attack, I'm going to add a setup warning encouraging it. Test Plan: - Set `auth.email-domains` to `@company.com`. - Registered with `aaa...aaa@evil.com@company.com`. Previously this worked, now it is rejected. - Did a valid registration. - Tried to add `aaa...aaaa@evil.com@company.com` as an email address. Previously this worked, now it is rejected. - Did a valid email add. - Added and executed unit tests. Reviewers: btrahan, arice Reviewed By: arice CC: aran, chad Differential Revision: https://secure.phabricator.com/D8308
This commit is contained in:
parent
a4d4bf8196
commit
7cf0358dda
6 changed files with 110 additions and 2 deletions
|
@ -2175,6 +2175,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorUserCustomFieldStringIndex' => 'applications/people/storage/PhabricatorUserCustomFieldStringIndex.php',
|
'PhabricatorUserCustomFieldStringIndex' => 'applications/people/storage/PhabricatorUserCustomFieldStringIndex.php',
|
||||||
'PhabricatorUserDAO' => 'applications/people/storage/PhabricatorUserDAO.php',
|
'PhabricatorUserDAO' => 'applications/people/storage/PhabricatorUserDAO.php',
|
||||||
'PhabricatorUserEditor' => 'applications/people/editor/PhabricatorUserEditor.php',
|
'PhabricatorUserEditor' => 'applications/people/editor/PhabricatorUserEditor.php',
|
||||||
|
'PhabricatorUserEditorTestCase' => 'applications/people/editor/__tests__/PhabricatorUserEditorTestCase.php',
|
||||||
'PhabricatorUserEmail' => 'applications/people/storage/PhabricatorUserEmail.php',
|
'PhabricatorUserEmail' => 'applications/people/storage/PhabricatorUserEmail.php',
|
||||||
'PhabricatorUserLog' => 'applications/people/storage/PhabricatorUserLog.php',
|
'PhabricatorUserLog' => 'applications/people/storage/PhabricatorUserLog.php',
|
||||||
'PhabricatorUserPreferences' => 'applications/settings/storage/PhabricatorUserPreferences.php',
|
'PhabricatorUserPreferences' => 'applications/settings/storage/PhabricatorUserPreferences.php',
|
||||||
|
@ -4997,6 +4998,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorUserCustomFieldStringIndex' => 'PhabricatorCustomFieldStringIndexStorage',
|
'PhabricatorUserCustomFieldStringIndex' => 'PhabricatorCustomFieldStringIndexStorage',
|
||||||
'PhabricatorUserDAO' => 'PhabricatorLiskDAO',
|
'PhabricatorUserDAO' => 'PhabricatorLiskDAO',
|
||||||
'PhabricatorUserEditor' => 'PhabricatorEditor',
|
'PhabricatorUserEditor' => 'PhabricatorEditor',
|
||||||
|
'PhabricatorUserEditorTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorUserEmail' => 'PhabricatorUserDAO',
|
'PhabricatorUserEmail' => 'PhabricatorUserDAO',
|
||||||
'PhabricatorUserLog' => 'PhabricatorUserDAO',
|
'PhabricatorUserLog' => 'PhabricatorUserDAO',
|
||||||
'PhabricatorUserPreferences' => 'PhabricatorUserDAO',
|
'PhabricatorUserPreferences' => 'PhabricatorUserDAO',
|
||||||
|
|
|
@ -200,8 +200,11 @@ final class PhabricatorAuthRegisterController
|
||||||
if (!strlen($value_email)) {
|
if (!strlen($value_email)) {
|
||||||
$e_email = pht('Required');
|
$e_email = pht('Required');
|
||||||
$errors[] = pht('Email is required.');
|
$errors[] = pht('Email is required.');
|
||||||
} else if (!PhabricatorUserEmail::isAllowedAddress($value_email)) {
|
} else if (!PhabricatorUserEmail::isValidAddress($value_email)) {
|
||||||
$e_email = pht('Invalid');
|
$e_email = pht('Invalid');
|
||||||
|
$errors[] = PhabricatorUserEmail::describeValidAddresses();
|
||||||
|
} else if (!PhabricatorUserEmail::isAllowedAddress($value_email)) {
|
||||||
|
$e_email = pht('Disallowed');
|
||||||
$errors[] = PhabricatorUserEmail::describeAllowedAddresses();
|
$errors[] = PhabricatorUserEmail::describeAllowedAddresses();
|
||||||
} else {
|
} else {
|
||||||
$e_email = null;
|
$e_email = null;
|
||||||
|
|
|
@ -570,6 +570,10 @@ final class PhabricatorUserEditor extends PhabricatorEditor {
|
||||||
// user friendly errors for us, but we omit the courtesy checks on some
|
// user friendly errors for us, but we omit the courtesy checks on some
|
||||||
// pathways like administrative scripts for simplicity.
|
// pathways like administrative scripts for simplicity.
|
||||||
|
|
||||||
|
if (!PhabricatorUserEmail::isValidAddress($email->getAddress())) {
|
||||||
|
throw new Exception(PhabricatorUserEmail::describeValidAddresses());
|
||||||
|
}
|
||||||
|
|
||||||
if (!PhabricatorUserEmail::isAllowedAddress($email->getAddress())) {
|
if (!PhabricatorUserEmail::isAllowedAddress($email->getAddress())) {
|
||||||
throw new Exception(PhabricatorUserEmail::describeAllowedAddresses());
|
throw new Exception(PhabricatorUserEmail::describeAllowedAddresses());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorUserEditorTestCase extends PhabricatorTestCase {
|
||||||
|
|
||||||
|
protected function getPhabricatorTestCaseConfiguration() {
|
||||||
|
return array(
|
||||||
|
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRegistrationEmailOK() {
|
||||||
|
$env = PhabricatorEnv::beginScopedEnv();
|
||||||
|
$env->overrideEnvConfig('auth.email-domains', array('example.com'));
|
||||||
|
|
||||||
|
$this->registerUser(
|
||||||
|
'PhabricatorUserEditorTestCaseOK',
|
||||||
|
'PhabricatorUserEditorTestCase@example.com');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRegistrationEmailInvalid() {
|
||||||
|
$env = PhabricatorEnv::beginScopedEnv();
|
||||||
|
$env->overrideEnvConfig('auth.email-domains', array('example.com'));
|
||||||
|
|
||||||
|
$prefix = str_repeat('a', PhabricatorUserEmail::MAX_ADDRESS_LENGTH);
|
||||||
|
$email = $prefix.'@evil.com@example.com';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->registerUser(
|
||||||
|
'PhabricatorUserEditorTestCaseInvalid',
|
||||||
|
$email);
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$caught = $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertEqual(true, ($caught instanceof Exception));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRegistrationEmailDomain() {
|
||||||
|
$env = PhabricatorEnv::beginScopedEnv();
|
||||||
|
$env->overrideEnvConfig('auth.email-domains', array('example.com'));
|
||||||
|
|
||||||
|
$caught = null;
|
||||||
|
try {
|
||||||
|
$this->registerUser(
|
||||||
|
'PhabricatorUserEditorTestCaseDomain',
|
||||||
|
'PhabricatorUserEditorTestCase@whitehouse.gov');
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$caught = $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertEqual(true, ($caught instanceof Exception));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function registerUser($username, $email) {
|
||||||
|
$user = id(new PhabricatorUser())
|
||||||
|
->setUsername($username)
|
||||||
|
->setRealname($username);
|
||||||
|
|
||||||
|
$email = id(new PhabricatorUserEmail())
|
||||||
|
->setAddress($email)
|
||||||
|
->setIsVerified(0);
|
||||||
|
|
||||||
|
id(new PhabricatorUserEditor())
|
||||||
|
->setActor($user)
|
||||||
|
->createNewUser($user, $email);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -12,6 +12,8 @@ final class PhabricatorUserEmail extends PhabricatorUserDAO {
|
||||||
protected $isPrimary;
|
protected $isPrimary;
|
||||||
protected $verificationCode;
|
protected $verificationCode;
|
||||||
|
|
||||||
|
const MAX_ADDRESS_LENGTH = 128;
|
||||||
|
|
||||||
public function getVerificationURI() {
|
public function getVerificationURI() {
|
||||||
return '/emailverify/'.$this->getVerificationCode().'/';
|
return '/emailverify/'.$this->getVerificationCode().'/';
|
||||||
}
|
}
|
||||||
|
@ -27,10 +29,36 @@ final class PhabricatorUserEmail extends PhabricatorUserDAO {
|
||||||
/* -( Domain Restrictions )------------------------------------------------ */
|
/* -( Domain Restrictions )------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task restrictions
|
||||||
|
*/
|
||||||
|
public static function isValidAddress($address) {
|
||||||
|
if (strlen($address) > self::MAX_ADDRESS_LENGTH) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task restrictions
|
||||||
|
*/
|
||||||
|
public static function describeValidAddresses() {
|
||||||
|
return pht(
|
||||||
|
'The maximum length of an email address is %d character(s).',
|
||||||
|
new PhutilNumber(self::MAX_ADDRESS_LENGTH));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @task restrictions
|
* @task restrictions
|
||||||
*/
|
*/
|
||||||
public static function isAllowedAddress($address) {
|
public static function isAllowedAddress($address) {
|
||||||
|
if (!self::isValidAddress($address)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$allowed_domains = PhabricatorEnv::getEnvConfig('auth.email-domains');
|
$allowed_domains = PhabricatorEnv::getEnvConfig('auth.email-domains');
|
||||||
if (!$allowed_domains) {
|
if (!$allowed_domains) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -173,8 +173,11 @@ final class PhabricatorSettingsPanelEmailAddresses
|
||||||
if (!strlen($email)) {
|
if (!strlen($email)) {
|
||||||
$e_email = pht('Required');
|
$e_email = pht('Required');
|
||||||
$errors[] = pht('Email is required.');
|
$errors[] = pht('Email is required.');
|
||||||
} else if (!PhabricatorUserEmail::isAllowedAddress($email)) {
|
} else if (!PhabricatorUserEmail::isValidAddress($email)) {
|
||||||
$e_email = pht('Invalid');
|
$e_email = pht('Invalid');
|
||||||
|
$errors[] = PhabricatorUserEmail::describeValidAddresses();
|
||||||
|
} else if (!PhabricatorUserEmail::isAllowedAddress($email)) {
|
||||||
|
$e_email = pht('Disallowed');
|
||||||
$errors[] = PhabricatorUserEmail::describeAllowedAddresses();
|
$errors[] = PhabricatorUserEmail::describeAllowedAddresses();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue