Search code examples
gosmtp-auth

Send email through unencrypted connection


I have a SMTP account that does not use encrypted connection. I can use the same account to send emails from C# and Python without problems but with Go I get the error: unencrypted connection

This is the code I am using:

package main

import (
        "log"
        "net/smtp"
)

func main() {
        // Set up authentication information.
        auth := smtp.PlainAuth(
                "",
                "[email protected]",
                "password",
                "mail.example.com",
        )
        // Connect to the server, authenticate, set the sender and recipient,
        // and send the email all in one step.
        err := smtp.SendMail(
                "mail.example.com:25",
                auth,
                "[email protected]",
                []string{"[email protected]"},
                []byte("This is the email body."),
        )
        if err != nil {
                log.Fatal(err)
        }
}

Solution

  • The issue here is that smtp.PlainAuth refuses to send your password over an unencrypted connection. This is for your own protection. Something like smtp.CRAMMD5Auth would be a much better choice. When using CRAM-MD5, even over an unencrypted connection, your password is not exposed.

    If you want to use plain authentication anyways, you would need to make your own version of smtp.PlainAuth. Luckily, this is a very easy thing to do. Just copy the 20 lines or so from the standard library and remove:

    if !server.TLS {
        return "", nil, errors.New("unencrypted connection")
    }
    

    http://golang.org/src/pkg/net/smtp/auth.go?s=1820:1882#L41 contains the code.

    If you do not wish to copy code, you can reuse the standard library implementation by wrapping the smtp.Auth returned by the function in your own type. This way you intercept the *smtp.ServerInfo and trick the actual Auth mechanism (from the standard library) that there is an encrypted connection. Make sure to heavily comment to make it clear why you are doing what you are doing. Something like this (untested):

    type unencryptedAuth struct {
        smtp.Auth
    }
    
    func (a unencryptedAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
        s := *server
        s.TLS = true
        return a.Auth.Start(&s)
    }
    
    auth := unencryptedAuth {
        smtp.PlainAuth(
            "",
            "[email protected]",
            "password",
            "mail.example.com",
        ),
    }