Skip to content
Snippets Groups Projects
Commit 64129d21 authored by Martin Mareš's avatar Martin Mareš
Browse files

Mail: Přísnější kontroly mailových adres

Funkce email_check_domain je nyní přísnější:

  •  Odmítáme adresy, které mají display name. Není jasné, jestli
     nějaké takové byly předtím akceptované, protože jsme odmítali
     adresy s mezerami.

  •  S parametrem check_existence=True se ptáme DNS, zda doménová
     část adresy existuje. Vyžadujeme buď MX nebo A záznam. Dotazy
     nicméně mají krátký timeout (aby kontrola moc nebrzdila UI)
     a po něm adresu uznáme jako použitelnou.

  •  Domény "nomail" a "test" považujeme za korektní.

  •  Zaveden blacklist domén, zatím v něm je jenom obvyklý chyták
     gmail.cz. Možná ho chceme časem přesunout do konfigurace.
parent 29a912e9
Branches
No related tags found
1 merge request!130Přísnější kontroly mailových adres
This commit is part of merge request !130. Comments created here will be created in the context of that merge request.
......@@ -3,6 +3,8 @@
import bcrypt
import datetime
import dateutil.tz
import dns.exception
import dns.resolver
import email.errors
import email.headerregistry
import re
......@@ -246,7 +248,44 @@ def find_or_create_participation(user: db.User, contest: db.Contest, place: Opti
return pion, is_new
def normalize_email(addr: str) -> str:
def email_is_fake(addr: str) -> bool:
return addr.endswith('@nomail') or addr.endswith('@test')
bad_domains = {
'gmail.cz',
}
def email_check_domain(domain: str):
# Některé domény rovnou odmítáme
if domain in bad_domains:
logger.info(f'DNS: Doména <{domain}> na blacklistu')
raise mo.CheckError('Doména {domain} nepřijímá poštu')
for record in ['MX', 'A', 'AAAA']:
try:
dns.resolver.resolve(domain, record, lifetime=2, search=False)
except dns.exception.Timeout:
# Kontrola je konzervativní, při timeoutu adresu raději schválíme
logger.info(f'DNS: Timeout při kontrole domény <{domain}>')
return
except dns.resolver.NoAnswer:
logger.debug(f'DNS: Doména <{domain}> neobsahuje {record}')
except dns.resolver.NXDOMAIN:
logger.info(f'DNS: Doména <{domain}> neexistuje')
raise mo.CheckError('Adresa obsahuje neexistující doménu {domain}')
except dns.exception.DNSException as e:
logger.warn(f'DNS: Záznam <{domain}>/{record} nejde resolvovat: {e}')
return
logger.debug(f'DNS: Doména <{domain}> OK')
return
logger.info(f'DNS: Doména <{domain}> nepřijímá poštu')
raise mo.CheckError('Doména {domain} nepřijímá poštu')
def normalize_email(addr: str, check_existence: bool = False) -> str:
if not re.fullmatch(r'.+@.+', addr):
raise mo.CheckError('V e-mailové adrese chybí zavináč')
......@@ -263,10 +302,16 @@ def normalize_email(addr: str) -> str:
try:
# Tady úmyslně používáme knihovnu jen ke kontrole a ne k normalizaci,
# protože nechceme riskovat, že se normalizovaný tvar časem změní.
email.headerregistry.Address(addr_spec=addr)
addr_obj = email.headerregistry.Address(addr_spec=addr)
except (email.errors.HeaderParseError, ValueError):
raise mo.CheckError('Chybná syntaxe mailové adresy')
if addr_obj.display_name != "":
raise mo.CheckError('Chybná syntaxe mailové adresy')
if check_existence and not email_is_fake(addr):
email_check_domain(addr_obj.domain)
# XXX: Striktně vzato, tohle není korektní, protože některé domény mohou
# mít case-sensitive levou stranu adresy. Ale i na nich se prakticky nevyskytují
# levé strany s velkými písmeny, zatímco uživatelé při psaní adres běžně velká
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment