Email Security

What Is SPF, DKIM, and DMARC?

Three complementary email authentication standards that prevent email spoofing. SPF (Sender Policy Framework) specifies which servers can send email for your domain. DKIM (DomainKeys Identified Mail) adds a cryptographic signature to verify emails were not tampered with. DMARC (Domain-based Message Authentication, Reporting & Conformance) tells receiving servers how to handle emails that fail SPF/DKIM checks.

How It Works

SPF, DKIM, and DMARC form a three-layer email authentication system. Each layer adds a different type of verification, and DMARC ties them together with enforcement policy.

SPF (Sender Policy Framework) — When a mail server receives an email from admin@yourapp.com, it extracts the envelope sender domain (yourapp.com) and performs a DNS TXT lookup: dig TXT yourapp.com. The SPF record specifies which mail servers are authorized to send email for that domain: v=spf1 include:mailgun.org -all. The receiving server checks whether the connecting mail server's IP address is listed in the authorized set. -all means fail any IP not explicitly listed; ~all means soft fail (deliver to spam); +all means pass any IP (do not use).

DKIM (DomainKeys Identified Mail) — The sending mail server (Mailgun, SendGrid, Postmark, SES) signs the email's headers and body with a private key. The signature is attached as a DKIM-Signature header. The receiving server fetches the corresponding public key from DNS: dig TXT mg._domainkey.yourapp.com (the selector prefix varies by provider). It uses this public key to verify the signature. A valid signature proves: (1) the email was sent by someone with the private key (your mail provider), and (2) the headers and body were not modified in transit.

DMARC (Domain-based Message Authentication, Reporting, and Conformance) — DMARC adds alignment and enforcement. The receiving server checks _dmarc.yourapp.com TXT for the policy. DMARC requires that either SPF or DKIM must pass AND the authenticated domain must align with the From: header (what users see). This prevents attackers from using a legitimate sending domain that is different from the From address. The p=reject policy instructs receiving servers to discard failing emails. The rua tag specifies where to send aggregate reports of authentication failures — rua=mailto:dmarc@yourapp.com.

Types of SPF, DKIM, and DMARC

Direct Domain Spoofing

Attacker sends email with From: admin@yourapp.com from an unauthorized server — blocked by SPF + DMARC alignment when both are correctly configured with enforcement.

# Without DMARC p=reject, this email may be delivered:
# From: admin@yourapp.com
# (Sent from attacker's server, not authorized by your SPF)
# DMARC p=reject causes receiving servers to discard this email

Subdomain Spoofing

Attacker uses a subdomain not covered by SPF/DMARC to send emails that appear to come from your organization.

# Email sent from billing.yourapp.com
# If _dmarc.billing.yourapp.com is not configured:
# DMARC falls back to yourapp.com policy (if aspf/adkim relaxed)
# Add DMARC coverage for each subdomain that sends email

Look-Alike Domain

Attacker registers a visually similar domain (yourapp-security.com, yourapp.co) and sends phishing emails from it — SPF/DKIM/DMARC on yourapp.com does not protect against look-alike domains.

# Not preventable with SPF/DKIM/DMARC:
# From: security@yourapp-security.com (different domain — looks similar)
# Defense: brand monitoring, user education, MFA on your actual app

In Laravel Applications

Laravel applications that send email (password resets, notifications, marketing) need proper SPF, DKIM, and DMARC records. Without them, your emails may be marked as spam, and attackers can spoof emails from your domain to phish your users.

Code Examples

DNS Records: Missing vs. Complete Email Authentication

Vulnerable

; No SPF, DKIM, or DMARC configured
; Anyone can send email as @yourapp.com
; dig TXT yourapp.com — returns nothing security-relevant
yourapp.com.   3600  IN  A    203.0.113.1
; Missing: SPF, DKIM, DMARC
; Result: phishing emails appear to legitimately come from yourapp.com

Secure

; Complete email authentication setup (example: Mailgun)
; Step 1: SPF — authorize Mailgun to send for your domain
yourapp.com.   3600  IN  TXT  "v=spf1 include:mailgun.org ~all"

; Step 2: DKIM — add Mailgun's public key (from Mailgun dashboard)
; Selector name varies: "mg" for Mailgun, "s1" for SendGrid, etc.
mg._domainkey.yourapp.com.  3600  IN  TXT  "v=DKIM1; k=rsa; p=MIGfMA0GCS..."

; Step 3: DMARC — quarantine failing emails, receive reports
_dmarc.yourapp.com.  3600  IN  TXT  "v=DMARC1; p=quarantine; sp=reject; rua=mailto:dmarc@yourapp.com; adkim=s; aspf=s"
; p=quarantine → move to spam (step toward p=reject)
; sp=reject → reject spoofed subdomain emails
; rua → receive XML reports of authentication results

Laravel Mail Config Requiring DKIM Alignment

Vulnerable

# .env — sending email directly via SMTP without provider DKIM signing
# Email passes SPF (if server IP is authorized) but fails DKIM (no signature)
MAIL_MAILER=smtp
MAIL_HOST=smtp.yourapp.com
MAIL_PORT=587
MAIL_USERNAME=admin@yourapp.com
MAIL_PASSWORD=password
# Sent email has no DKIM-Signature header — DMARC may fail alignment

Secure

# .env — use a dedicated transactional email provider with DKIM
# Mailgun, Postmark, SendGrid, or SES all sign emails with DKIM automatically
MAIL_MAILER=mailgun
MAILGUN_DOMAIN=yourapp.com
MAILGUN_SECRET=key-...your-mailgun-api-key...
# Also add Mailgun's DKIM DNS record (from Mailgun dashboard → Sending → Domains)
# This ensures sent emails pass both DKIM and SPF alignment for DMARC

Real-World Example

Without DMARC, an attacker sends emails from admin@yourapp.com to your users asking them to reset their password on a fake login page. The emails appear legitimate because there is no policy telling receivers to reject them.

Why It Matters

Email is the primary channel for social engineering attacks against your users. If attackers can send emails that appear to come from your domain — password reset requests, account alerts, promotional offers — they can manipulate users into revealing credentials or performing harmful actions. SPF, DKIM, and DMARC together make this impersonation significantly harder.

Laravel applications send transactional email via config/mail.php using services like Mailgun, Postmark, SendGrid, or SES. Each of these services requires that you add their sending infrastructure to your SPF record and configure DKIM signing to authenticate their emails as legitimate. This configuration must be updated whenever you switch email providers.

DMARC is the enforcement layer. Without DMARC, SPF and DKIM records exist but there is no instruction to receiving mail servers about what to do when authentication fails. A DMARC record with p=reject tells receivers to block emails that fail authentication — the strongest protection. p=quarantine sends failures to spam, and p=none monitors only.

Start with p=none to collect DMARC reports (using the rua and ruf tags to receive XML and forensic reports) and understand your email ecosystem before moving to p=quarantine and finally p=reject. Moving to p=reject too quickly can block legitimate email from forgotten sending sources.

Common Misconceptions

Myth: We use a reputable email service (SendGrid/Mailgun), so our email authentication is handled.

Reality: Email services provide DKIM signing infrastructure, but you must add their DKIM public key as a DNS record on your domain and add their sending IPs to your SPF record. This does not happen automatically.

Myth: SPF alone prevents email spoofing.

Reality: SPF only authenticates the sending server's IP — it does not prevent spoofing of the From header (the address users see). DKIM and DMARC are required to prevent the visible From address from being spoofed.

Myth: Having DMARC with `p=none` protects our users.

Reality: `p=none` is a monitoring-only mode — it has no enforcement effect. Emails that fail authentication are still delivered. `p=none` is a first step to understanding your email ecosystem, not a final protection.

How to Detect This

Run `dig TXT yourdomain.com` and look for an SPF record starting with `v=spf1`. Check DMARC with `dig TXT _dmarc.yourdomain.com` — the record should contain `p=quarantine` or `p=reject` for enforcement to be active. For DKIM, check `dig TXT selector._domainkey.yourdomain.com` for your email provider's selector (Mailgun, SendGrid, Postmark, and SES each have specific selector names). Use mail-tester.com or mxtoolbox.com to send a test email and receive a comprehensive SPF/DKIM/DMARC analysis report. StackShield checks SPF, DKIM, and DMARC configuration as part of its DNS security monitoring and alerts when records are missing, malformed, or when DMARC enforcement is set to `p=none`.

How to Prevent This in Laravel

  1. 1

    Add an SPF record (`v=spf1 include:mailgun.org ~all`) to your domain — replace `mailgun.org` with your mail provider's SPF include (`sendgrid.net`, `amazonses.com`) as shown in their documentation.

  2. 2

    Configure DKIM signing via your email provider's dashboard (Mailgun → Sending → Domains → Add DKIM record) and add the resulting TXT record to your DNS.

  3. 3

    Set DMARC with at minimum `p=none; rua=mailto:dmarc@yourapp.com` to start receiving reports, then upgrade to `p=quarantine` after reviewing reports for 2 weeks, then `p=reject` for full enforcement.

  4. 4

    Configure Laravel to use a dedicated transactional email provider (`MAIL_MAILER=mailgun|ses|postmark`) rather than SMTP — these providers sign emails with DKIM automatically.

  5. 5

    Send a test email to mail-tester.com/10minutemail.com and review the DMARC/DKIM/SPF analysis in the results to verify all three authentication checks pass.

Frequently Asked Questions

How do I configure SPF for Laravel when using Mailgun, SendGrid, or SES?

Add a TXT record to your DNS: `v=spf1 include:mailgun.org -all` for Mailgun, `v=spf1 include:sendgrid.net -all` for SendGrid, or `v=spf1 include:amazonses.com -all` for SES. The `include:` directive tells receiving mail servers to also check the provider's SPF record, which lists their sending IPs. If you send from multiple providers, chain them: `v=spf1 include:mailgun.org include:sendgrid.net ~all`. Do not exceed 10 DNS lookups in a single SPF record (SPF lookup limit).

Does DMARC completely prevent phishing of my users?

DMARC `p=reject` prevents phishing emails that use your exact domain in the `From:` header. It does not prevent phishing that uses look-alike domains (`yourapp-security.com`), display name spoofing (legitimate address but misleading display name), or compromised accounts sending from within your own domain. DMARC is essential but should be combined with MFA (to limit damage from compromised accounts) and subdomain monitoring (to prevent subdomain-based phishing).

How do I read DMARC aggregate reports (RUA)?

DMARC aggregate reports are XML files sent to the `rua` email address, reporting how many emails passed/failed SPF and DKIM authentication for your domain each day. For human-readable reports, use a free service like DMARC Analyzer, Postmark's DMARC monitoring, or dmarcian — they parse the XML and present it as dashboards. Review reports weekly during the `p=none` phase to identify all legitimate sending sources before moving to `p=quarantine`.

Will adding DMARC break my legitimate outgoing email?

Only if you switch to `p=reject` without first verifying all your sending sources pass authentication. Start with `p=none` (monitoring only) and collect reports for 2-4 weeks. Identify all services sending email on your domain's behalf (transactional email, marketing email, CRM notifications, automated systems). Ensure each is listed in SPF and has DKIM configured. Only after all legitimate senders pass both SPF and DKIM should you move to `p=quarantine` then `p=reject`.

Related Terms

Related Articles

Related Fix Guides

Monitor Your Laravel Application's Security

StackShield continuously checks your Laravel application from the outside, catching security issues before attackers find them.

Start Free Trial