CGI for sending emails

jozef on 2008-11-01T22:41:37

Today I've created a CGI (below) to send feedback emails from submitted form.

I would like to use (that's why I've created it of course) but also to give it as an example.

Now I'm wondering 1. is it a good example and 2. if it's "secure" enough.

Any suggestions?

---cut---

#!/usr/bin/perl -T

=head1 NAME

send-feedback.cgi - CGI that sends email from posted feedback form

=head1 SYNOPSIS

in html:



in Apache virtualhost:

AddHandler cgi-script .cgi Options +ExecCGI

test from commandline:

send-feedback.cgi message=test name=jozef subject=subj email=devnull@ba.pm.org

=head1 DESCRIPTION

=cut

use strict; use warnings;

use CGI; use MIME::Lite; use Email::Address;

my $EMAIL_TO = 'feedback@bratislava.pm.org'; my $REDIRECT_OK = '/cgi/email-ok.html'; my $REDIRECT_FAILED = '/cgi/email-fail.html';

my $EMAIL_ADDRESS_RE = $Email::Address::mailbox;

exit main();

sub main { my $q = CGI->new(); # default redirection is to _FAILED my $REDIRECT = $REDIRECT_FAILED; # get parameters my $name = $q->param('name') || ''; my $from = $q->param('email') || 'devnull@ba.pm.org'; my $subject = $q->param('subject') || ''; my $message = $q->param('message'); $from = "$name <$from>" if ($name);

# input check my $input_ok = 1; $input_ok = 0 if ($from =~ m/\r|\n/m); $input_ok = 0 if ($subject =~ m/\r|\n/m); $input_ok = 0 if ($from !~ m/^$EMAIL_ADDRESS_RE$/xms); # only message paramter is mandatory if ($message and $input_ok) { # create message my $msg = MIME::Lite->new( From => $from, To => $EMAIL_TO, Subject => $subject, Type => 'text/plain; charset=UTF-8', Encoding => '8bit', Data => $message, ); # send email and redirect to _OK if sucessfull $REDIRECT = $REDIRECT_OK if eval { $msg->send('smtp', 'localhost'); }; } # redirect print $q->redirect( -uri => 'http://'.$q->virtual_host.$REDIRECT, -status => 302 ); return 0; }


Seems secure

autarch on 2008-11-01T23:01:05

At first glance, it seems secure. The biggest issue with a script that sends mail from web input is if you allow the "To" address to be controlled by the form submission. Obviously, allowing that is a recipe for spam.

You're generating the email using a module, rather than by hand, so that should help rule out other vulnerabilities that could come if you built the email by hand.

There might be an issue with the message body, because you're not restricting it to 7-bit ASCII. I'm not sure if MIME::Lite will do encoding if needed.

Re:Seems secure

jozef on 2008-11-02T07:19:14

the "To:" is fixed to one address, the purpose is just to collect feedback, so that people can leave notes for us.

the encoding was a good point. The page is in utf-8, so I changed:

        my $msg = MIME::Lite->new(
            From     => $from,
            To       => $EMAIL_TO,
            Subject  => $subject,
            Type     => 'text/plain; charset=UTF-8',
            Encoding => '8bit',
            Data     => $message,
        );

The HTML

dorward on 2008-11-02T09:27:30

Other people have already commented on the Perl, so I'll mention the HTML.

If it is trying to be HTML, then its invalid. If it is trying to be XHTML, then it is probably flawed in ways you don't want. I'm going to assume the latter.

It's "post" not "POST"

You have no block level container between the form element and its inline content. This means you can't be using valid XHTML 1.0 Strict (or anything based on it). Strict is the modern approach, it is generally wise to avoid Transitional.

You don't have elements for your label text.

You are missing spaces before / characters when you use self-closing element syntax. This violates the recommendations of Appendix C.

For your content-less textarea element, you use empty element syntax, but the element is not defined as being empty. This is another violation of Appendix C. I've never checked with textarea, but when you make that mistake with script, Internet Explorer doesn't do what you want.

You have no submit button.

Re:The HTML

jozef on 2008-11-02T10:09:50

You are right! The SYNOPSIS is bad :-(

    <form action="/cgi-bin/send-feedback.cgi" method="POST">
        Your Name:  <input type="text" name="name" size="20" /><br/>
        Your Email: <input type="text" name="from" size="20" /><br/>
        Subject: <input type="text" name="subject" size="20" /><br/>
        Message: <textarea name="message" cols="80" rows="20" />
    </form>

For the real page I generate it by FormFu and it looks like this:

<form action="/cgi/send-feedback.cgi" id="feedback_form" method="post">
    <fieldset>
        <span class="text label">
            <label for="name">Your name</label>
            <input name="name" type="text" id="name" size="25" />
        </span>
        <span class="text label">
            <label for="email">Tour email</label>
            <input name="email" type="text" id="email" size="25" />
        </span>
        <span class="text label">
            <label for="subject">Subject</label>
            <input name="subject" type="text" id="subject" size="25" />
        </span>
        <span class="textarea label">
            <label for="message">Message</label>
            <textarea name="message" cols="40" id="message" rows="5"></textarea>
        </span>
        <span class="submit">
            <input name="submit" type="submit" value="Submit" />
        </span>
    </fieldset>
</form>

Much better, I'll put that one instead. Thank you!

Re:The HTML

andy.sh on 2008-11-11T10:21:12

It's still weird: placing textarea inside span element is not an option you should use :-)