validate domain of email address in registration form, fixes #284
- via DNS (MX, A/AAAA records) - against blacklist with regexes This is implemented because without it, postmasters get tons of mailer daemon emails each day, just because emails are invalid, domain has no mx, ...
This commit is contained in:
parent
90e59c7a89
commit
7dcf5a22bb
75
nsupdate/accounts/registration_form.py
Normal file
75
nsupdate/accounts/registration_form.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from django.conf import settings
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from registration.forms import RegistrationForm
|
||||||
|
|
||||||
|
import dns.resolver
|
||||||
|
import dns.name
|
||||||
|
|
||||||
|
|
||||||
|
resolver = dns.resolver.Resolver()
|
||||||
|
resolver.search = [dns.name.root, ]
|
||||||
|
resolver.lifetime = 5.0
|
||||||
|
resolver.nameservers = settings.NAMESERVERS
|
||||||
|
|
||||||
|
|
||||||
|
maildomain_blacklist = settings.MAILDOMAIN_BLACKLIST.strip().splitlines()
|
||||||
|
|
||||||
|
|
||||||
|
def check_mx(domain):
|
||||||
|
valid = False
|
||||||
|
try:
|
||||||
|
mx_answers = resolver.query(domain, 'MX')
|
||||||
|
# domain exists in DNS, domain has MX
|
||||||
|
mx_entries = sorted([(mx_rdata.preference, mx_rdata.exchange) for mx_rdata in mx_answers])
|
||||||
|
for preference, mx in mx_entries:
|
||||||
|
try:
|
||||||
|
addr_answers = resolver.query(mx, 'A')
|
||||||
|
except dns.resolver.NoAnswer:
|
||||||
|
addr_answers = resolver.query(mx, 'AAAA')
|
||||||
|
# MX has IP addr
|
||||||
|
mx_addrs = [addr_rdata.address for addr_rdata in addr_answers]
|
||||||
|
for mx_addr in mx_addrs:
|
||||||
|
if mx_addr not in (u'127.0.0.1', u'::1', u'0.0.0.0', ):
|
||||||
|
valid = True
|
||||||
|
break
|
||||||
|
if valid:
|
||||||
|
break
|
||||||
|
except (dns.resolver.Timeout, dns.resolver.NoAnswer, dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, ):
|
||||||
|
# expected exceptions (e.g. due to non-existing or misconfigured crap domains)
|
||||||
|
pass
|
||||||
|
return valid
|
||||||
|
|
||||||
|
|
||||||
|
def check_blacklist(domain):
|
||||||
|
for blacklisted_re in maildomain_blacklist:
|
||||||
|
if re.search(blacklisted_re, domain):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class RegistrationFormValidateEmail(RegistrationForm):
|
||||||
|
def clean_email(self):
|
||||||
|
"""
|
||||||
|
Validate the supplied email address to avoid undeliverable email and mailer daemon spam.
|
||||||
|
"""
|
||||||
|
email = self.cleaned_data.get('email')
|
||||||
|
valid_mx = False
|
||||||
|
try:
|
||||||
|
domain = email.split('@')[1]
|
||||||
|
valid_mx = check_mx(domain)
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception('RegistrationFormValidateEmail raised an exception:')
|
||||||
|
not_blacklisted = check_blacklist(domain)
|
||||||
|
if valid_mx and not_blacklisted:
|
||||||
|
return email
|
||||||
|
logger.info('RegistrationFormValidateEmail: rejecting email address %r' % email)
|
||||||
|
raise forms.ValidationError(_("Enter a valid email address."))
|
@ -51,6 +51,21 @@ BAD_AGENTS = set([]) # list can have str elements
|
|||||||
from netaddr import IPSet, IPAddress, IPNetwork
|
from netaddr import IPSet, IPAddress, IPNetwork
|
||||||
BAD_IPS_HOST = IPSet([]) # inner list can have IPAddress and IPNetwork elements
|
BAD_IPS_HOST = IPSet([]) # inner list can have IPAddress and IPNetwork elements
|
||||||
|
|
||||||
|
# nameservers used e.g. for MX lookups in the registration email validation.
|
||||||
|
# google / cloudflare DNS IPs are only given as example / fallback -
|
||||||
|
# please configure your own nameservers in your local settings file.
|
||||||
|
NAMESERVERS = ['8.8.8.8', '1.1.1.1', ]
|
||||||
|
|
||||||
|
# registration email validation: disallow specific email domains,
|
||||||
|
# e.g. domains that have a non-working mx / that are frequently abused.
|
||||||
|
# we use a multiline string here with one regex per line (used with re.search).
|
||||||
|
# the domains given below are just examples, please configure your own
|
||||||
|
# regexes in your local settings file.
|
||||||
|
MAILDOMAIN_BLACKLIST = """
|
||||||
|
mailcatch\.com$
|
||||||
|
mailspam\.xyz$
|
||||||
|
"""
|
||||||
|
|
||||||
# Local time zone for this installation. Choices can be found here:
|
# Local time zone for this installation. Choices can be found here:
|
||||||
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
||||||
# although not all choices may be available on all operating systems.
|
# although not all choices may be available on all operating systems.
|
||||||
@ -245,6 +260,7 @@ SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
|||||||
|
|
||||||
ACCOUNT_ACTIVATION_DAYS = 7
|
ACCOUNT_ACTIVATION_DAYS = 7
|
||||||
REGISTRATION_EMAIL_HTML = False # we override the text, but not the html email template
|
REGISTRATION_EMAIL_HTML = False # we override the text, but not the html email template
|
||||||
|
REGISTRATION_FORM = 'nsupdate.accounts.registration_form.RegistrationFormValidateEmail'
|
||||||
|
|
||||||
LOGIN_REDIRECT_URL = '/overview/'
|
LOGIN_REDIRECT_URL = '/overview/'
|
||||||
LOGOUT_REDIRECT_URL = '/'
|
LOGOUT_REDIRECT_URL = '/'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user