diff --git a/checks/categories.py b/checks/categories.py index d4a2829b0..e78d7dfcd 100644 --- a/checks/categories.py +++ b/checks/categories.py @@ -222,6 +222,7 @@ def __init__(self, name="mail-auth"): MailAuthDkim, MailAuthSpf, MailAuthSpfPolicy, + MailAuthTlsRptExists, ] super().__init__(name, subtests) @@ -2182,8 +2183,8 @@ def __init__(self): label="detail mail auth spf label", explanation="detail mail auth spf exp", tech_string="detail mail auth spf tech table", - worst_status=scoring.MAIL_AUTH_SPF_WORST_STATUS, - full_score=scoring.MAIL_AUTH_SPF_PASS, + worst_status=scoring.MAIL_AUTH_TLSRPT_WORST_STATUS, + full_score=scoring.MAIL_AUTH_TLSRPT_PASS, model_score_field="spf_score", ) # Fix for one line, one value data. @@ -2250,6 +2251,36 @@ def result_bad_redirect(self, tech_data): self.tech_data = tech_data +class MailAuthTlsRptExists(Subtest): + def __init__(self): + super().__init__( + name="tlsrpt", + label="detail mail auth tlsrpt label", + explanation="detail mail auth tlsrpt exp", + tech_string="detail mail auth tlsrpt tech table", + worst_status=scoring.MAIL_AUTH_SPF_WORST_STATUS, + full_score=scoring.MAIL_AUTH_SPF_PASS, + model_score_field="tlsrpt_score", + ) + # Fix for one line, one value data. + self.tech_data = [[self.tech_data]] + + def result_good(self, tech_data): + self._status(STATUS_SUCCESS) + self.verdict = "detail mail auth tlsrpt verdict good" + self.tech_data = [[tech_data]] + + def result_bad(self, tech_data): + self._status(STATUS_NOTICE) + self.verdict = "detail mail auth tlsrpt verdict bad" + if tech_data: + # More than one spf record. Show the records. + self.tech_data = [[tech_data]] + else: + self.tech_data = "" + self.tech_type = "" + + # --- APPSECPRIV class WebAppsecprivHttpXFrame(Subtest): def __init__(self): diff --git a/checks/migrations/0016_add_tlsrpt.py b/checks/migrations/0016_add_tlsrpt.py new file mode 100644 index 000000000..20740c46a --- /dev/null +++ b/checks/migrations/0016_add_tlsrpt.py @@ -0,0 +1,28 @@ +# Created by Uwe Kamper with Django 3.2.23 on 2024-02-12 13:30 + +import checks.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("checks", "0015_auto_20240212_1616"), + ] + + operations = [ + migrations.AddField( + model_name="mailtestauth", + name="tlsrpt_score", + field=models.IntegerField(null=True), + ), + migrations.AddField( + model_name="mailtestauth", + name="tlsrpt_available", + field=models.BooleanField(default=False, null=True), + ), + migrations.AddField( + model_name="mailtestauth", + name="tlsrpt_record", + field=checks.models.ListField(default=[]), + ), + ] diff --git a/checks/models.py b/checks/models.py index 4eb9ca19a..f5a4e0677 100644 --- a/checks/models.py +++ b/checks/models.py @@ -1010,6 +1010,9 @@ class MailTestAuth(BaseTestModel): spf_policy_status = EnumIntegerField(SpfPolicyStatus, null=True) spf_policy_score = models.IntegerField(null=True) spf_policy_records = ListField(null=True) + tlsrpt_score = models.IntegerField(null=True) + tlsrpt_available = models.BooleanField(null=True, default=False) + tlsrpt_record = ListField(default=[]) score = models.IntegerField(null=True) max_score = models.IntegerField(null=True) @@ -1032,6 +1035,9 @@ def __dir__(self): "spf_policy_status", "spf_policy_score", "spf_policy_records", + "tlsrpt_score", + "tlsrpt_available", + "tlsrpt_record", "score", "max_score", ] diff --git a/checks/scoring.py b/checks/scoring.py index e716656c8..e168fdcbd 100644 --- a/checks/scoring.py +++ b/checks/scoring.py @@ -235,6 +235,11 @@ MAIL_AUTH_SPF_ERROR = NO_POINTS MAIL_AUTH_SPF_WORST_STATUS = STATUS_FAIL +MAIL_AUTH_TLSRPT_PASS = NO_POINTS +MAIL_AUTH_TLSRPT_FAIL = NO_POINTS # TLS-RPT fail does not give a points penalty +MAIL_AUTH_TLSRPT_ERROR = NO_POINTS +MAIL_AUTH_TLSRPT_WORST_STATUS = STATUS_FAIL + MAIL_AUTH_SPF_POLICY_PASS = FULL_WEIGHT_POINTS MAIL_AUTH_SPF_POLICY_PARTIAL = LESS_WEIGHT_POINTS MAIL_AUTH_SPF_POLICY_FAIL = NO_POINTS diff --git a/checks/tasks/mail.py b/checks/tasks/mail.py index 2e76c850b..9c123c5e2 100644 --- a/checks/tasks/mail.py +++ b/checks/tasks/mail.py @@ -17,6 +17,7 @@ from checks.tasks.dispatcher import check_registry, post_callback_hook from checks.tasks.dmarc_parser import parse as dmarc_parse from checks.tasks.spf_parser import parse as spf_parse +from checks.tasks import tlsrpt_parsing from interface import batch, batch_shared_task, redis_id from internetnl import log @@ -114,6 +115,17 @@ def batch_spf(self, url, *args, **kwargs): return do_spf(self, url, *args, **kwargs) +@mail_registered +@shared_task( + bind=True, + soft_time_limit=settings.SHARED_TASK_SOFT_TIME_LIMIT_LOW, + time_limit=settings.SHARED_TASK_TIME_LIMIT_LOW, + base=SetupUnboundContext, +) +def tlsrpt(self, url, *args, **kwargs): + return do_tlsrpt(self, url, *args, **kwargs) + + def skip_dkim_for_non_sending_domain(mtauth): """ If there is no DKIM, check if DMARC and SPF are hinting for a non email @@ -214,10 +226,23 @@ def callback(results, addr, category): subtests["spf_policy"].result_bad_include(spf_records) elif spf_policy_status == SpfPolicyStatus.invalid_redirect: subtests["spf_policy"].result_bad_redirect(spf_records) - else: subtests["spf"].result_bad(spf_record) + elif testname == "tlsrpt": + tlsrpt_available = result.get("available") + tlsrpt_record = result.get("record") + tlsrpt_score = result.get("score") + + # Pass results to mtauth and subtests + mtauth.tlsrpt_available = tlsrpt_available + mtauth.tlsrpt_record = tlsrpt_record + mtauth.tlsrpt_score = tlsrpt_score + log.debug(f"subtests: {subtests.keys()}") + if tlsrpt_available: + subtests["tlsrpt"].result_good(tlsrpt_record) + else: + subtests["tlsrpt"].result_bad(tlsrpt_record) if skip_dkim_for_non_sending_domain(mtauth): mtauth.dkim_score = scoring.MAIL_AUTH_DKIM_PASS subtests["dkim"].result_no_email() @@ -824,3 +849,76 @@ def dmarc_get_public_suffix_list(): public_suffix_list = dmarc_get_public_suffix_list() return public_suffix_list + + +def tlsrpt_callback(data, status, r): + data["score"] = scoring.MAIL_AUTH_TLSRPT_FAIL + data["available"] = False + data["record"] = [] + if status == 0: + record = [] + available = False + if r.rcode == unbound.RCODE_NOERROR and r.havedata == 1: + # TXT record(s) for _smtp._tls.{domain} found, start looking for TLSRPT + score = scoring.MAIL_AUTH_TLSRPT_FAIL + for d in r.data.data: + txt = as_txt(d) + log.debug(f"tlsrpt: found record '{txt.lower()}'") + if txt.lower().startswith("v=tlsrptv1"): + record.append(txt) + if tlsrpt_parsing.parse_silent(txt) is None: + # A parsing error has occured + available = False + score = scoring.MAIL_AUTH_TLSRPT_FAIL + break + if available: + # We see more than one TLSRPT record. Fail the test. + available = False + score = scoring.MAIL_AUTH_TLSRPT_FAIL + break + else: + available = True + score = scoring.MAIL_AUTH_TLSRPT_PASS + elif r.rcode == unbound.RCODE_NXDOMAIN or (r.rcode == unbound.RCODE_NOERROR and r.havedata == 0): + # we know for sure there is no TLSRPT record + score = scoring.MAIL_AUTH_TLSRPT_FAIL + else: + # resolving problems, servfail probably + score = scoring.MAIL_AUTH_TLSRPT_ERROR + + data["score"] = score + data["available"] = available + data["record"] = record + data["done"] = True + + +def resolve_tlsrpt_record(url, task): + # Make sure, url does not start with a dot, then add "_smtp._tls." in front + # of the domain name. + tls_rpt_url = f'_smtp._tls.{url.lstrip(".")}' + return task.async_resolv(tls_rpt_url, unbound.RR_TYPE_TXT, callback=tlsrpt_callback) + + +def do_tlsrpt(self, url, *args, **kwargs): + try: + cb_data = resolve_tlsrpt_record(url, self) + available = "available" in cb_data and cb_data["available"] + score = cb_data["score"] + record = cb_data["record"] + + result = dict( + available=available, + score=score, + record=record, + ) + + # KeyError is due to score missing, happens in case of timeout on non resolving domain + except (SoftTimeLimitExceeded, KeyError) as specific_exception: + log.debug("Soft time limit exceeded: %s", specific_exception) + result = dict( + available=False, + score=scoring.MAIL_AUTH_TLSRPT_FAIL, + record=[], + ) + # return a tuple containing ("testname", result) + return ("tlsrpt", result) diff --git a/checks/tasks/tlsrpt_parsing.py b/checks/tasks/tlsrpt_parsing.py new file mode 100644 index 000000000..6616bc290 --- /dev/null +++ b/checks/tasks/tlsrpt_parsing.py @@ -0,0 +1,63 @@ +# Copyright: 2022-2024, ECP, NLnet Labs, the Internet.nl contributors and SYS4 AG. +# SPDX-License-Identifier: Apache-2.0 + +""" +SMTP TLS Reporting policy parser as defined by: + + RFC 8460, Section "3. Reporting Policy", see: + https://datatracker.ietf.org/doc/html/rfc8460#section-3 +""" + +from pyparsing import ( + Literal, + CaselessLiteral, + Combine, + ParseException, + Regex, + White, + Word, + ZeroOrMore, + alphanums, + pyparsing_common, + delimitedList, +) + + +WSP = White(ws=" ", exact=1).suppress() # Whitespace + +field_delim = ZeroOrMore(WSP) + Literal(";") + ZeroOrMore(WSP) # Fields are semicolon-delimited +ura_delim = ZeroOrMore(WSP) + Literal(",") + ZeroOrMore(WSP) # multiple RUAs are comma-delimited + +tlsrpt_ext_name = Word(alphanums, alphanums + "_-.", max=32) +tlsrpt_ext_value = Word(alphanums, alphanums + "_-.") +tlsrpt_extension = ZeroOrMore(tlsrpt_ext_name + Literal("=") + tlsrpt_ext_value) + +# RegEx for parsing email. +regex_tld = r"(?:[a-zA-Z]{2,63}|xn--[a-zA-Z0-9]+)" +regex_mailaddr = ( + r"(?P([a-zA-Z0-9]{0,61}@)?([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+" r"" + regex_tld + ")" +) +mail_uri = Combine(CaselessLiteral("mailto:") + Regex(regex_mailaddr)) +tlsrpt_rua = Literal("rua=") + delimitedList(mail_uri | pyparsing_common.url, delim=",").setResultsName("tlsrpt_uri") + +tlsrpt_field = tlsrpt_rua + ZeroOrMore(field_delim + tlsrpt_extension) + +# Literal will match the version string as required by the ABNF in the RFC: +# tlsrpt-version = %s"v=TLSRPTv1" +version = Literal("v=TLSRPTv1").setResultsName("tlsrpt_version") + +record = version + field_delim + tlsrpt_field + + +def parse_silent(tlsrpt_record): + """ + Will return None if there was a parsing error and a ParseResult object otherwise. + """ + try: + parsed = record.parseString(tlsrpt_record) + except ParseException: + parsed = None + except Exception as e: + print(f"{e.__class__.__name__}: {e}") + parsed = None + return parsed diff --git a/checks/test/test_tlsrpt_parsing.py b/checks/test/test_tlsrpt_parsing.py new file mode 100644 index 000000000..84cc21bc5 --- /dev/null +++ b/checks/test/test_tlsrpt_parsing.py @@ -0,0 +1,39 @@ +from checks.tasks import tlsrpt_parsing + + +def test_record_parse_simple_mailto(): + TXT_RECORD = "v=TLSRPTv1; rua=mailto:reports@example.com" + parsed = tlsrpt_parsing.record.parseString(TXT_RECORD) + assert parsed.tlsrpt_version == "v=TLSRPTv1" + assert parsed.tlsrpt_uri[0] == "mailto:reports@example.com" + + +def test_record_parse_multiple_mailto(): + TXT_RECORD = "v=TLSRPTv1;rua=mailto:reports@example.com,mailto:postmaster@example.com" + parsed = tlsrpt_parsing.record.parseString(TXT_RECORD) + assert parsed.tlsrpt_version == "v=TLSRPTv1" + assert parsed.tlsrpt_uri[0] == "mailto:reports@example.com" + assert parsed.tlsrpt_uri[1] == "mailto:postmaster@example.com" + + +def test_record_parse_simple_https(): + TXT_RECORD = "v=TLSRPTv1; rua=https://reporting.example.com/v1/tlsrpt" + parsed = tlsrpt_parsing.record.parseString(TXT_RECORD) + assert parsed.tlsrpt_version == "v=TLSRPTv1" + assert parsed.tlsrpt_uri[0] == "https://reporting.example.com/v1/tlsrpt" + + +def test_record_parse_with_extension(): + TXT_RECORD = "v=TLSRPTv1; rua=https://reporting.example.com/v1/tlsrpt; ext=extvalue" + parsed = tlsrpt_parsing.record.parseString(TXT_RECORD) + assert parsed.tlsrpt_version == "v=TLSRPTv1" + + +def test_parse_silent(): + """ + Check that parse_silent does not throw a ParseException but instead returns + None if the TLSRPT policy record is malformed. + """ + TXT_RECORD = "v=TLSRPTv1; rua=!!" # broken TLSRPT + parsed = tlsrpt_parsing.parse_silent(TXT_RECORD) + assert parsed is None diff --git a/interface/templates/mail-results.html b/interface/templates/mail-results.html index 33232c673..af0ab9846 100644 --- a/interface/templates/mail-results.html +++ b/interface/templates/mail-results.html @@ -48,7 +48,10 @@

{% include "details-test-item.html" with testitem=details.spf %} {% include "details-test-item.html" with testitem=details.spf_policy %} - +
+ {% include "string.html" with name="results mail auth tlsrpt label" %} +
+ {% include "details-test-item.html" with testitem=details.tlsrpt %} {% elif probe.name == 'mailtls' %}
{% include "string.html" with name="results mail tls starttls label" %} diff --git a/translations/en/main.po b/translations/en/main.po index 7a4039e44..c060feadd 100644 --- a/translations/en/main.po +++ b/translations/en/main.po @@ -183,7 +183,7 @@ msgstr "" "\n" "* [IPv6](/faqs/ipv6/): reachable via modern internet address?\n" "* [DNSSEC](/faqs/dnssec/): domain names signed?\n" -"* [DMARC, DKIM and SPF](/faqs/mailauth/): authenticity marks against email phishing?\n" +"* [DMARC, DKIM, SPF, and TLSRPT](/faqs/mailauth/): authenticity marks against email phishing?\n" "* [STARTTLS and DANE](/faqs/starttls): secure mail server connection?\n" "* [RPKI](/faqs/rpki/): route authorisation?\n" "\n" @@ -588,6 +588,26 @@ msgstr "" "your SPF record points to an insufficiently strict SPF policy or a non-" "existing SPF record." +# TLSRPT +msgid "detail mail auth tlsrpt exp" +msgstr "" +"We check if your domain defines a policy for SMTP TLS Reporting – also know as TLSRPT for short. " +"A receiving mail server can use the address in your TLSRPT record to send to you reports about " +"possible attacks or misconfigurations.\n" +"\n" + +msgid "detail mail auth tlsrpt label" +msgstr "TLSRPT existence" + +msgid "detail mail auth tlsrpt tech table" +msgstr "TLSRPT record" + +msgid "detail mail auth tlsrpt verdict bad" +msgstr "Your domain does not have a TLSRPT record or the record is invalid." + +msgid "detail mail auth tlsrpt verdict good" +msgstr "Your domain has a TLSRPT record." + msgid "detail mail dnssec exists exp" msgstr "" "We check if your domain, more specifically its SOA record, is DNSSEC signed. With a DNSSEC signature senders who validate domain signatures can verify the authenticity of the DNS reply that contains your mail server domains (MX). This prevents an attacker from manipulating the DNS answer in order to redirect mails sent to you to the attacker's mailserver domain.\n" @@ -3816,11 +3836,12 @@ msgstr "" "## Specifications\n" "* [RFC 7489: Domain-based Message Authentication, Reporting, and Conformance (DMARC)](https://www.rfc-editor.org/rfc/rfc7489)\n" "* [RFC 6376: DomainKeys Identified Mail (DKIM) Signatures](https://www.rfc-editor.org/rfc/rfc6376)\n" -"* [RFC 7208: Sender Policy Framework (SPF) for Authorizing Use of Domains in Email, Version 1](https://www.rfc-editor.org/rfc/rfc7208)" +"* [RFC 7208: Sender Policy Framework (SPF) for Authorizing Use of Domains in Email, Version 1](https://www.rfc-editor.org/rfc/rfc7208)\n" +"* [RFC 8460: SMTP TLS Reporting (TLSRPT)](https://www.rfc-editor.org/rfc/rfc8460)" #, md-format msgid "faqs mailauth title" -msgstr "Protection against email phishing (DMARC, DKIM and SPF)" +msgstr "Protection against email phishing (DMARC, DKIM, SPF, and TLSRPT)" #, md-format msgid "faqs report score content" @@ -4382,6 +4403,9 @@ msgstr "DMARC" msgid "results mail auth spf label" msgstr "SPF" +msgid "results mail auth tlsrpt label" +msgstr "TLSRPT" + msgid "results mail dnssec domain label" msgstr "Email address domain" @@ -4559,14 +4583,14 @@ msgstr "*Error* during test execution!" msgid "test mailauth failed description" msgstr "" "Too bad! Your domain does *not* contain all authenticity marks against email\n" -" forgery ([DMARC, DKIM and SPF](/faqs/mailauth/)). Therefore receivers are *not* able to reliably separate phishing \n" +" forgery ([DMARC, DKIM, SPF and TLSRPT](/faqs/mailauth/)). Therefore receivers are *not* able to reliably separate phishing \n" "and spam emails abusing your domain in their sender address from your \n" -"authentic emails. You should ask your mail provider to activate DMARC, DKIM \n" -"and SPF." +"authentic emails. You should ask your mail provider to activate DMARC, DKIM, \n" +"SPF and TLSRPT" msgid "test mailauth failed summary" msgstr "" -"*Not* all authenticity marks against email phishing (DMARC, DKIM and SPF)" +"*Not* all authenticity marks against email phishing (DMARC, DKIM, SPF and TLSRPT)" msgid "test mailauth info description" msgstr "" @@ -4578,10 +4602,10 @@ msgstr "" msgid "test mailauth info summary" msgstr "" -"*Not* all authenticity marks against email phishing (DMARC, DKIM and SPF)" +"*Not* all authenticity marks against email phishing (DMARC, DKIM, SPF and TLSRPT)" msgid "test mailauth label" -msgstr "Authenticity marks against phishing (DMARC, DKIM and SPF)" +msgstr "Authenticity marks against phishing (DMARC, DKIM, SPF and TLSRPT)" msgid "test mailauth passed description" msgstr "" diff --git a/translations/nl/main.po b/translations/nl/main.po index 96d4406d2..0fc05468d 100644 --- a/translations/nl/main.po +++ b/translations/nl/main.po @@ -182,7 +182,7 @@ msgstr "" "\n" "* [IPv6](/faqs/ipv6/): bereikbaar via modern internetadres?\n" "* [DNSSEC](/faqs/dnssec/): domeinnamen ondertekend?\n" -"* [DMARC, DKIM en SPF](/faqs/mailauth/): echtheidswaarmerken tegen e-mailphishing?\n" +"* [DMARC, DKIM, SPF en TLSRPT](/faqs/mailauth/): echtheidswaarmerken tegen e-mailphishing?\n" "* [STARTTLS en DANE](/faqs/starttls/): beveiligde mailserver-verbinding?\n" "* [RPKI](/faqs/rpki/): route-autorisatie?\n" "\n" @@ -592,6 +592,26 @@ msgstr "" "record verwijst naar een onvoldoende strikte SPF-policy of naar een niet-" "bestaand SPF-record." +# TLSRPT +msgid "detail mail auth tlsrpt exp" +msgstr "" +"We controleren of je domein een beleid definieert voor SMTP TLS-rapportage - ook bekend als TLSRPT. " +"Een ontvangende mailserver kan het adres in uw TLSRPT record gebruiken om u rapporten te sturen over " +"mogelijke aanvallen of misconfiguraties.\n" +"\n" + +msgid "detail mail auth tlsrpt label" +msgstr "TLSRPT aanwezigheid" + +msgid "detail mail auth tlsrpt tech table" +msgstr "TLSRPT-record" + +msgid "detail mail auth tlsrpt verdict bad" +msgstr "Je domein heeft geen TLSRPT-record of het record is ongeldig." + +msgid "detail mail auth tlsrpt verdict good" +msgstr "Je domein heeft een TLSRPT-record." + msgid "detail mail dnssec exists exp" msgstr "" "We testen of de domeinnaam in je e-mailadres, meer specifiek het SOA-record, ondertekend is met DNSSEC. De handtekening zorgt ervoor dat een verzender, die domeinhandtekeningen controleert, op een betrouwbare wijze jouw mailserverdomeinen (MX) kan opvragen. Dit voorkomt dat een aanvaller het antwoord manipuleert en aan jou gerichte mail omleidt naar een mailserverdomein dat onder controle is van de aanvaller.\n" @@ -3839,11 +3859,12 @@ msgstr "" "## Specificaties\n" "* [RFC 7489: Domain-based Message Authentication, Reporting, and Conformance (DMARC)](https://www.rfc-editor.org/rfc/rfc7489)\n" "* [RFC 6376: DomainKeys Identified Mail (DKIM) Signatures](https://www.rfc-editor.org/rfc/rfc6376)\n" -"* [RFC 7208: Sender Policy Framework (SPF) for Authorizing Use of Domains in Email, Version 1](https://www.rfc-editor.org/rfc/rfc7208)" +"* [RFC 7208: Sender Policy Framework (SPF) for Authorizing Use of Domains in Email, Version 1](https://www.rfc-editor.org/rfc/rfc7208)\n" +"* [RFC 8460: SMTP TLS Reporting (TLSRPT)](https://www.rfc-editor.org/rfc/rfc8460)" #, md-format msgid "faqs mailauth title" -msgstr "Protectie tegen e-mailphishing (DMARC, DKIM en SPF)" +msgstr "Protectie tegen e-mailphishing (DMARC, DKIM, SPF en TLSRPT)" #, md-format msgid "faqs report score content" @@ -4577,29 +4598,29 @@ msgstr "*Fout* tijdens uitvoering van test!" msgid "test mailauth failed description" msgstr "" "Helaas! Je domein bevat *niet* alle echtheidswaarmerken tegen \n" -"e-mailvervalsing ([DMARC, DKIM en SPF](/faqs/mailauth/)). Ontvangers kunnen daardoor *niet* betrouwbaar phishing- of\n" +"e-mailvervalsing ([DMARC, DKIM, SPF en TLSRPT](/faqs/mailauth/)). Ontvangers kunnen daardoor *niet* betrouwbaar phishing- of\n" " spammails die jouw domeinnaam in hun afzenderadres misbruiken, scheiden van\n" -" jouw echte e-mails. Vraag je mailprovider om DMARC, DKIM en SPF te \n" +" jouw echte e-mails. Vraag je mailprovider om DMARC, DKIM, SPF an TLSRPT te \n" "activeren." msgid "test mailauth failed summary" msgstr "" -"*Niet* alle echtheidswaarmerken tegen e-mailphishing (DMARC, DKIM en SPF)" +"*Niet* alle echtheidswaarmerken tegen e-mailphishing (DMARC, DKIM, SPF en TLSRPT)" msgid "test mailauth info description" msgstr "" "Helaas! Je domein bevat *niet* alle echtheidswaarmerken tegen \n" "e-mailvervalsing ([DMARC, DKIM en SPF](/faqs/mailauth/)). Ontvangers kunnen daardoor *niet* betrouwbaar phishing- of\n" " spammails die jouw domeinnaam in hun afzenderadres misbruiken, scheiden van\n" -" jouw echte e-mails. Vraag je mailprovider om DMARC, DKIM en SPF te \n" +" jouw echte e-mails. Vraag je mailprovider om DMARC, DKIM, SPF en TLSRPT te \n" "activeren." msgid "test mailauth info summary" msgstr "" -"*Niet* alle echtheidswaarmerken tegen e-mailphishing (DMARC, DKIM en SPF)" +"*Niet* alle echtheidswaarmerken tegen e-mailphishing (DMARC, DKIM, SPF en TLSRPT)" msgid "test mailauth label" -msgstr "Echtheidswaarmerken tegen phishing (DMARC, DKIM en SPF)" +msgstr "Echtheidswaarmerken tegen phishing (DMARC, DKIM, SPF en TLSRPT)" msgid "test mailauth passed description" msgstr ""