#37162 assigned Cleanup/optimization

Docs ContactForm example demonstrates poor practices

Reported by: Mike Edmunds Owned by: Mike Edmunds
Component: Documentation Version: 6.0
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

The ContactForm used as a running example starting in the More on fields forms topic demonstrates poor practices for implementing a contact form that sends email:

  • It uses the contact's email address (sender) as the message's from_email. At best, this won't work (the message will be rejected by the outgoing server or silently ignored as spam at the receiving end). At worst, it creates vulnerabilities.
  • It passes through subject and message without any indication they originated in a web contact form. This can allow high-fidelity phishing attacks against the organization deploying the form (particularly when combined with an arbitrary sender address).

I realize the example needs to be kept simple, but Django's docs probably shouldn't be illustrating insecure approaches.

I'd suggest changing the current:

from django.core.mail import send_mail

if form.is_valid():
    subject = form.cleaned_data["subject"]
    message = form.cleaned_data["message"]
    sender = form.cleaned_data["sender"]
    cc_myself = form.cleaned_data["cc_myself"]

    recipients = ["info@example.com"]
    if cc_myself:
        recipients.append(sender)

    send_mail(subject, message, sender, recipients)
    return HttpResponseRedirect("/thanks/")

to something like (also renaming the sender form field to email throughout the page):

    from django.core.mail import EmailMessage

    if form.is_valid():
        subject = form.cleaned_data["subject"]
        message = form.cleaned_data["message"]
        email = form.cleaned_data["email"]
        cc_myself = form.cleaned_data["cc_myself"]

        # Send the message, directing replies to the contact's email.
        EmailMessage(
            subject=f"[via contact form] {subject}",
            body=f"Contact email: {email}\n\n{message}",
            to=["info@example.com"],
            cc=[email] if cc_myself else None,
            reply_to=[email],
        ).send()

        return HttpResponseRedirect("/thanks/")

Note that the cc handling there (and in the original) is also not ideal. Maybe we could come up with some other reason to include a boolean field in the form? ("Urgent"? "No reply necessary"?)

Or if something like that seems too complicated, maybe we should consider rewriting the page to use something other than a contact form.

[Noticed this while I was working on #34753. I was considering removing the contact-form-like example from the Email header injection section and replacing it with a ref to this actual-contact-form example.]

Change History (0)

Note: See TracTickets for help on using tickets.
Back to Top