Opened 52 minutes ago
#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'sfrom_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
subjectandmessagewithout 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.]