Skip to content

Network Security - DNS Domains with SPF configured

Description

The percentage of DNS domains with email configured that has an SPF record created in the DNS zone.

How we measure it

Find all DNS zones in DNSimple and Cloudflare. For every zone with an MX record, check that an SPF record has been created. Zones without an MX record are assumed not to send email.

Meta Data

Attribute Value
Metric id ns_domains_with_spf
Category Network Security
SLO 95.00% - 99.00%
Weight 0.5
Type risk

References

Framework Ref Domain Control
ISO 27001:2022 A.8.20 8 Technological controls Networks security
CIS 8.1 9.2 Email and Web Browser Protections Use DNS Filtering Services
CIS 8.1 9.3 Email and Web Browser Protections Maintain and Enforce Network-Based URL Filters
CIS 8.1 12.6 Network Infrastructure Management Use of Secure Network Management and Communication Protocols
NIST CSF v2.0 PR.DS-01 Data Security (PR.DS) PR.DS-01: The confidentiality, integrity, and availability of data-at-rest are protected

Code

SELECT
  zone AS resource,
  'domain' AS resource_type,
  CASE
    WHEN count(*) FILTER (WHERE type = 'MX' AND name = '') = 0 THEN 1
    WHEN count(*) FILTER (WHERE type = 'TXT' AND name = '' AND content ILIKE '%v=spf1%') > 0 THEN 1
    ELSE 0
  END AS compliance,
  CASE
    WHEN count(*) FILTER (WHERE type = 'MX' AND name = '') = 0 THEN 'No MX record found - assumed no email'
    WHEN count(*) FILTER (WHERE type = 'TXT' AND name = '' AND content ILIKE '%v=spf1%') > 0 THEN 'SPF record found'
    ELSE 'MX record found, but no SPF record'
  END AS detail
FROM {{ ref('dnsimple_zone_records') }}
GROUP BY zone
SELECT
  zones.name AS resource,
  'domain' AS resource_type,
  CASE
    WHEN count(records.id) FILTER (WHERE records.type = 'MX' AND records.name = zones.name) = 0 THEN 1
    WHEN count(records.id) FILTER (WHERE records.type = 'TXT' AND records.name = zones.name AND records.content ILIKE '%v=spf1%') > 0 THEN 1
    ELSE 0
  END AS compliance,
  CASE
    WHEN count(records.id) FILTER (WHERE records.type = 'MX' AND records.name = zones.name) = 0 THEN 'No MX record found - assumed no email'
    WHEN count(records.id) FILTER (WHERE records.type = 'TXT' AND records.name = zones.name AND records.content ILIKE '%v=spf1%') > 0 THEN 'SPF record found'
    ELSE 'MX record found, but no SPF record'
  END AS detail
FROM {{ ref('cloudflare_zones') }} AS zones
LEFT JOIN {{ ref('cloudflare_dns_records') }} AS records ON records.zone_id = zones.id
GROUP BY zones.name