mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-26 23:40:57 +01:00
Prevent inbound processing of the "void/placeholder" address and other reserved addresses
Summary: Depends on D19952. Ref T13222. Never process mail targets if they match: - The "default" address which we send mail "From". - The "void" address which we use as a placholder "To" when we only have "CC" addresses. - Any address from a list of reserved/administrative names. The first two prevent loops. The third one prevents abuse. There's a reasonably well-annotated list of reservations and reasons here: https://webmasters.stackexchange.com/questions/104811/is-there-any-list-of-email-addresses-reserved-because-of-security-concerns-for-a Stuff like `support@` seems fine; stuff like `ssladmin@` might let you get SSL certs issued for a domain you don't control. Also, forbid users from creating application emails with these reserved addresses. Finally, build the default and void addresses somewhat more cleverly. Test Plan: Added unit tests, tried to configured reserved addresses, hit the default/void cases manually with `bin/mail receive-test`. Reviewers: amckinley Reviewed By: amckinley Subscribers: olexiy.myronenko Maniphest Tasks: T13222 Differential Revision: https://secure.phabricator.com/D19953
This commit is contained in:
parent
e3aa043a02
commit
a37b28ef79
6 changed files with 112 additions and 9 deletions
|
@ -191,10 +191,7 @@ EODOC
|
|||
$this->newOption('cluster.mailers', 'cluster.mailers', array())
|
||||
->setHidden(true)
|
||||
->setDescription($mailers_description),
|
||||
$this->newOption(
|
||||
'metamta.default-address',
|
||||
'string',
|
||||
'noreply@phabricator.example.com')
|
||||
$this->newOption('metamta.default-address', 'string', null)
|
||||
->setDescription(pht('Default "From" address.')),
|
||||
$this->newOption(
|
||||
'metamta.one-mail-per-recipient',
|
||||
|
|
|
@ -104,6 +104,16 @@ final class PhabricatorMetaMTAApplicationEmailEditor
|
|||
pht('Invalid'),
|
||||
pht('Email address is not formatted properly.'));
|
||||
}
|
||||
|
||||
$address = new PhutilEmailAddress($email);
|
||||
if (PhabricatorMailUtil::isReservedAddress($address)) {
|
||||
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Reserved'),
|
||||
pht(
|
||||
'This email address is reserved. Choose a different '.
|
||||
'address.'));
|
||||
}
|
||||
}
|
||||
|
||||
$missing = $this->validateIsEmptyTextField(
|
||||
|
|
|
@ -41,4 +41,30 @@ final class PhabricatorMailReceiverTestCase extends PhabricatorTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public function testReservedAddresses() {
|
||||
$default_address = id(new PhabricatorMetaMTAMail())
|
||||
->newDefaultEmailAddress();
|
||||
|
||||
$void_address = id(new PhabricatorMetaMTAMail())
|
||||
->newVoidEmailAddress();
|
||||
|
||||
$map = array(
|
||||
'alincoln@example.com' => false,
|
||||
'sysadmin@example.com' => true,
|
||||
'hostmaster@example.com' => true,
|
||||
'"Walter Ebmaster" <webmaster@example.com>' => true,
|
||||
(string)$default_address => true,
|
||||
(string)$void_address => true,
|
||||
);
|
||||
|
||||
foreach ($map as $raw_address => $expect) {
|
||||
$address = new PhutilEmailAddress($raw_address);
|
||||
|
||||
$this->assertEqual(
|
||||
$expect,
|
||||
PhabricatorMailUtil::isReservedAddress($address),
|
||||
pht('Reserved: %s', $raw_address));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -713,7 +713,7 @@ final class PhabricatorMetaMTAMail
|
|||
$actors = $this->loadAllActors();
|
||||
$deliverable_actors = $this->filterDeliverableActors($actors);
|
||||
|
||||
$default_from = PhabricatorEnv::getEnvConfig('metamta.default-address');
|
||||
$default_from = (string)$this->newDefaultEmailAddress();
|
||||
if (empty($params['from'])) {
|
||||
$mailer->setFrom($default_from);
|
||||
}
|
||||
|
@ -1463,18 +1463,33 @@ final class PhabricatorMetaMTAMail
|
|||
}
|
||||
|
||||
private function newMailDomain() {
|
||||
$domain = PhabricatorEnv::getEnvConfig('metamta.reply-handler-domain');
|
||||
if (strlen($domain)) {
|
||||
return $domain;
|
||||
}
|
||||
|
||||
$install_uri = PhabricatorEnv::getURI('/');
|
||||
$install_uri = new PhutilURI($install_uri);
|
||||
|
||||
return $install_uri->getDomain();
|
||||
}
|
||||
|
||||
public function newVoidEmailAddress() {
|
||||
public function newDefaultEmailAddress() {
|
||||
$raw_address = PhabricatorEnv::getEnvConfig('metamta.default-address');
|
||||
if (strlen($raw_address)) {
|
||||
return new PhutilEmailAddress($raw_address);
|
||||
}
|
||||
|
||||
$domain = $this->newMailDomain();
|
||||
$address = "void-recipient@{$domain}";
|
||||
$address = "noreply@{$domain}";
|
||||
|
||||
return new PhutilEmailAddress($address);
|
||||
}
|
||||
|
||||
public function newVoidEmailAddress() {
|
||||
return $this->newDefaultEmailAddress();
|
||||
}
|
||||
|
||||
|
||||
/* -( Routing )------------------------------------------------------------ */
|
||||
|
||||
|
|
|
@ -161,10 +161,19 @@ final class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO {
|
|||
->setFilterMethod('isEnabled')
|
||||
->execute();
|
||||
|
||||
$targets = $this->newTargetAddresses();
|
||||
foreach ($targets as $key => $target) {
|
||||
// Never accept any reserved address as a mail target. This prevents
|
||||
// security issues around "hostmaster@" and bad behavior with
|
||||
// "noreply@".
|
||||
if (PhabricatorMailUtil::isReservedAddress($target)) {
|
||||
unset($targets[$key]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$any_accepted = false;
|
||||
$receiver_exception = null;
|
||||
|
||||
$targets = $this->newTargetAddresses();
|
||||
foreach ($receivers as $receiver) {
|
||||
$receiver = id(clone $receiver)
|
||||
->setViewer($viewer);
|
||||
|
|
|
@ -62,4 +62,50 @@ final class PhabricatorMailUtil
|
|||
return ($u->getAddress() === $v->getAddress());
|
||||
}
|
||||
|
||||
public static function isReservedAddress(PhutilEmailAddress $address) {
|
||||
$address = self::normalizeAddress($address);
|
||||
$local = $address->getLocalPart();
|
||||
|
||||
$reserved = array(
|
||||
'admin',
|
||||
'administrator',
|
||||
'hostmaster',
|
||||
'list',
|
||||
'list-request',
|
||||
'majordomo',
|
||||
'postmaster',
|
||||
'root',
|
||||
'ssl-admin',
|
||||
'ssladmin',
|
||||
'ssladministrator',
|
||||
'sslwebmaster',
|
||||
'sysadmin',
|
||||
'uucp',
|
||||
'webmaster',
|
||||
|
||||
'noreply',
|
||||
'no-reply',
|
||||
);
|
||||
|
||||
$reserved = array_fuse($reserved);
|
||||
|
||||
if (isset($reserved[$local])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$default_address = id(new PhabricatorMetaMTAMail())
|
||||
->newDefaultEmailAddress();
|
||||
if (self::matchAddresses($address, $default_address)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$void_address = id(new PhabricatorMetaMTAMail())
|
||||
->newVoidEmailAddress();
|
||||
if (self::matchAddresses($address, $void_address)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue