WillyXoft

Software, Servicios, Seguridad y más…

Envío por SMTP con autenticación

with 6 comments


Envío de correo electrónico por SMTP con autenticación

Compartir

Por Willy Mejía
Publicado: Septiembre 2004
Actualizado: Febrero 2006

Este artículo muestra como podemos enviar correo electrónico por medio de servidores SMTP que requieren credenciales de autenticación.

-

Envío de correo electrónico en .NET

Para que una aplicación .NET pueda realizar envío de correo electrónico con el .NET Framework 1.x se emplea el espacio de nombres System.Web.Mail, que contiene las clases que permiten construir y enviar mensajes mediante el componente COM de mensajería conocido como CDO (Collaboration Data Objects) formalmente llamado “Microsoft CDO for Windows 2000 Library” que se incluye en Windows 2000 y posteriores. El mensaje de correo se entrega por medio del servicio de correo SMTP (Simple Mail Transport Protocol) ya sea con el servidor incluido con IIS o bien mediante algún otro servidor SMTP externo. Las clases de System.Web.Mail se pueden utilizar desde cualquier aplicación administrada ya sea Web (ASP.NET) o Windows.

Imports System.Web.Mail
Module Module1
  Sub Main()
    Dim oMsg As MailMessage = New MailMessage()
    oMsg.From = "sender@somewhere.com"
    oMsg.To = "recipient@somewhere.com"
    oMsg.Subject = "Send using Web Mail"
    oMsg.BodyFormat = MailFormat.Html
    oMsg.Body = "Hello World!"
    SmtpMail.SmtpServer = "smtp.domain.com"
    SmtpMail.Send(oMsg)
  End Sub
End Module

Lo anterior lo podemos emplear también en el .NET Framework 2.0, no obstante éste introduce una serie clases mejoradas para la comunicación por red, incluyendo aquellas relacionadas con el envío de correo electrónico. Dichas clases se encuentran bajo el Namespace System.Net.Mail y ya no son un wrapper para CDO.

Imports System.Net.Mail
Module Module1
  Sub Main()
    Dim oMsg As MailMessage = New MailMessage()
    oMsg.From = "sender@somewhere.com"
    oMsg.To = "recipient@somewhere.com"
    oMsg.Subject = "Send using Web Mail"
    oMsg.IsBodyHtml = True
    oMsg.Body = "Hello World!"
    Dim oClient As SmtpClient = New SmtpClient("smtp.domain.com")
    oClient.Send(oMsg)
  End Sub
End Module

En este caso MailMessage es una nueva clase que pertenece al Namespace System.Net.Mail, por lo que no es la misma clase del Namespace System.Web.Mail aun presente por compatibilidad. Se observa también que ahora se tiene que crear una instancia de la nueva clase SmtpClient para realizar el envío de correo electrónico.

El Problema: El servicio SMTP requiere autenticación

Con el fin de evitar el envío de correo electrónico con remitente falso y reducir el spam, algunos servidores de correo electrónico SMTP requieren que el usuario se autentifique antes de enviar un mensaje. Si bajo estas circunstancias se envía un correo electrónico sin las credenciales de autenticación se recibe un mensaje error. 

Para resolver el problema del envío de correo electrónico mediante un servidor SMTP que requiere credenciales autenticación tenemos tres posibles soluciones:

 

NOTA: ¿Cómo determinar si el error es a causa de falta de autenticación?

Cuando el método SmtpMail.Send de System.Web.Mail no puede completar el envío del mensaje de correo electrónico, se dispara una excepción con un mensaje parecido a:

No se puede obtener acceso al objeto ‘CDO.Message’.

Lamentablemente dicho mensaje no brinda mayor información sobre la causa real del problema. Por ello debemos utilizar InnerException para conocer la excepción interna que dió origen a la excepción actual. Como en el ejemplo siguiente, donde EscribeLog es una función para escribir el Log en un archivo, una base de datos, o al EventLog:

    Try
        System.Web.Mail.SmtpMail.Send(Correo)
    Catch ex As System.Exception
        EscribeLog("Excepción: " & ex.Message)
        Do Until (ex.InnerException Is Nothing)
            EscribeLog("Excepción interna: " & _
                ex.InnerException.ToString())
            ex = ex.InnerException
        Loop
    End Try

De este modo podemos determinar la causa por la qué no se pudo enviar el mensaje. Si la causa tiene que ver con la falta de autenticación entonces continue leyendo.

 

Estableciendo las Credenciales de Autenticación en CDO.

La configuración del componente CDO se establece a través de un conjunto de propiedades en forma de pares de nombre/valor llamados CDO Fields. Dichos Campos se implementan en una Colección de tipo ADODB.Fields llamada a su vez Fields. Esta implementación esta asociada al objeto Configuration de CDO quien a su vez esta asociado a los distintos objetos de CDO. Es entonces que debemos emplear el objeto Configuration para definir parámetros de configuración específicos al objeto Message de CDO a través de su colección Fields para así establecer los valores de los Campos necesarios para establecer las credenciales de autenticación.

Todos los campos que necesitamos establecer se encuentran bajo el Espacio de Nombres de CDO (no confundir con los Namespaces de .NET) “http://schemas.microsoft.com/cdo/configuration/” que se encuentran agrupadas en la Biblioteca de CDO bajo el Módulo CdoConfiguration, y que están representados por constantes que equivalen al nombre completo del Campo, esto es: Espacio de nombres + Nombre del campo. Así por ejemplo, la constante cdoSMTPAuthenticate equivale al Campo smtpauthenticate cuyo nombre completo es: “http://schemas.microsoft.com/cdo/configuration/smtpauthenticate“.

Mediante el campo smtpauthenticate podemos indicar el mecanismo de autenticación requerida para enviar los mensajes, los valores permitidos se listan en el Enumerado CdoProtocolsAuthentication, que alberga a tres constantes: cdoAnonymous (valor numérico 0), cdoBasic (valor numérico 1) y cdoNTLM (valor numérico 2).

Para que el campo smtpauthenticate sea tomado en cuenta debe establecerse a su vez el campo sendusing (“http://schemas.microsoft.com/cdo/configuration/sendusing”), al valor de la constante cdoSendUsingPort (valor numérico 2) del Enumerado CdoSendUsing, este campo define si se debe enviar el mensaje utilizando el servicio local SMTP (si es que el host local tuviese el servicio SMTP instalado) o un servicio SMTP externo sobre la red de trabajo local, que es el que debemos establecer.

También debe establecerse el valor del campo smtpserver (“http://schemas.microsoft.com/cdo/configuration/smtpserver”) con la dirección IP específica o el nombre DNS del host que alberga el servicio SMTP, y en caso de ser necesario, debe establecerse el valor numérico del puerto del mismo en el campo smtpserverport (“http://schemas.microsoft.com/cdo/configuration/smtpserverport”).

Por último y mas importante, para establecer las credenciales de autenticación requeridas para la conexión con el servicio SMTP, deben establecerse los valores de los campos sendusername (“http://schemas.microsoft.com/cdo/configuration/sendusername”) y sendpassword (“http://schemas.microsoft.com/cdo/configuration/sendpassword”) correspondientes a el nombre de usuario (login) y la contraseña de usuario. Estos dos campos junto con el campo smtpauthenticate son los indispensables para establecer las credenciales de autenticación para el servidor SMTP.

En resumen, los Campos CDO a emplear y cuyos valores debemos establecer son:

  • smtpserver: Nombre del servidor SMTP.
  • smtpserverport: Puerto del servidor SMTP (25).
  • sendusing: Método de envío, establecido a cdoSendUsingPort (2) para el uso de la red de trabajo.
  • smtpauthenticate: Mecanismo de autenticación hacia el servicio SMTP sobre la red de trabajo. Establecido a cdoBasic (1).
  • sendusername: Nombre de usuario
  • sendpassword: Contraseña

 

Utilizando CDO (cdosys.dll) para el envío de mensajes con autenticación.

Este primer método no emplea el Namespace System.Web.Mail, sino que emplea directamente la Biblioteca de tipos de CDO a través de Interoperabilidad COM. Para ello lo primero que tenemos que hacer es crear nuestro Ensamblado de Interoperabilidad. Desde su IDE puede hacerlo agregando la referencia COM de dicho componente, o bien, desde la línea de comandos lo obtenemos escribiendo:

>tlbimp cdosys.dll /out:Interop.CDO.dll

Podemos ahora elaborar un ejemplo con VB.NET, para ello creamos un Proyecto de Consola y agregamos la referencia de CDO, y creamos un archivo de nombre Module1.vb. Después, introducimos el código siguiente:

Option Explicit On 
Module Module1 
   Sub Main()
    Const ConfigNamespace As String = _
    "http://schemas.microsoft.com/cdo/configuration/" 
    Dim oMsg As New CDO.Message() 
    Dim iConfig As New CDO.Configuration() 
    Dim Flds As ADODB.Fields = iConfig.Fields
    With Flds 
      .Item(ConfigNamespace & "smtpserver").Value = "smtp.mail.dominio"
      .Item(ConfigNamespace & "smtpserverport").Value = 25
      .Item(ConfigNamespace & "sendusing").Value = _
            CDO.CdoSendUsing.cdoSendUsingPort
      .Item(ConfigNamespace & "sendusername").Value = "usuario"
      .Item(ConfigNamespace & "sendpassword").Value = "contraseña"
      .Item(ConfigNamespace & "smtpauthenticate").Value = _
            CDO.CdoProtocolsAuthentication.cdoBasic
      .Update()
    End With 
    With oMsg
      .Configuration = iConfig
      .From = "usuario@dominio"
      .To = "alguien@dominio"
      .Subject = "Test con CDO"
      .TextBody = "Hola Mundo"
      .Send()
    End With 
    oMsg = Nothing
    iConfig = Nothing
  End Sub
End Module

Nota: Debe cambiar los valores de los campos y propiedades por aquellos que correspondan a su caso particular. Si el servidor SMTP además requiere una conexión SSL, se debe agregar el campo “smtpusessl” con valor establecido a “true”.

Compilamos desde nuestro IDE, o bien, para compilar desde la línea de comandos:

>vbc Module1.vb /r:Interop.CDO.dll,adodb.dll

Nota: El ensamblado ADODB.DLL es el PIA (Primary Interop Assembly) de ADO (ActiveX Data Objects) que por lo general se encuentra en: %programfiles%\Microsoft.NET\Primary Interop Assemblies\ En caso contrario deberá crearse manualmente con tlbimp.

Esta es la única solución para el envío de correo electrónico con autenticación para la versión 1.0 del .NET Framework, mas puede funcionar también con las versiones 1.1 y 2.0.

Utilizando MailMessage.Fields para el envío de mensajes con autenticación.

En la documentación del .Net Framework versión 1.1 se observa que la clase System.Web.Mail.MailMessage tiene un nuevo miembro llamado Fields —mismo que hasta el día de hoy no esta del todo documentado— con el cual podemos establecer los valores de la colección Fields de la biblioteca CDO.

Para probarlo modificaremos el código anterior cambiando los objetos CDO por sus equivalentes en System.Web.Mail, empleando un System.Collections.IDictionary en lugar de ADODB.Fields, así como cambiando las nombres de las constantes CDO por sus valores numéricos correspondientes para evitar cualquier referencia a CDO, teniendo como resultado el siguiente código:

Option Explicit On 
Module Module1 
  Sub Main()
    Const ConfigNamespace As String = _
    "http://schemas.microsoft.com/cdo/configuration/" 
    Dim oMsg As New System.Web.Mail.MailMessage 
    Dim Flds As System.Collections.IDictionary 
    Flds = oMsg.Fields 
    With Flds 
      .Add(ConfigNamespace & "smtpserver", "smtp.mail.dominio") 
      .Add(ConfigNamespace & "smtpserverport", 25) 
      .Add(ConfigNamespace & "sendusing", 2) 
      .Add(ConfigNamespace & "sendusername", "usuario") 
      .Add(ConfigNamespace & "sendpassword", "contraseña") 
      .Add(ConfigNamespace & "smtpauthenticate", 1) 
    End With 
    With oMsg 
      .From = "usuario@dominio" 
      .To = "alguien@dominio" 
      .Subject = "Test con MailMessage.Fields" 
      .Body = "Hola Mundo" 
    End With 
    System.Web.Mail.SmtpMail.Send(oMsg) 
  End Sub 
End Module

Nota: Debe cambiar los valores de los campos y propiedades por aquellos que correspondan a su caso particular. Si el servidor SMTP además requiere una conexión SSL, se debe agregar el campo “smtpusessl” con valor establecido a “true”.

Observe que dado a que el código se especificó el Host del servicio SMTP mediante el campo “smtpserver” ya no es necesario hacerlo mediante la propiedad SmtpServer. Por otro lado, si se emplea la propiedad SmtpServer se pueden omitir los campos “smtpserver”, “smtpserverport” y “sendusing” y sólo necesitaríamos especificar los tres campos restantes: “sendusername”, “sendpassword” y “smtpauthenticate”.

De este modo podemos enviar mensajes con autenticación sin necesidad de emplear la Biblioteca COM de CDO mediante Interoperabilidad COM gracias a la propiedad Fields de MailMessage presente a partir de la versión 1.1 del .Net Framework. Por ende esta solución no funciona en la versión 1.0 pero sí en la versión 2.0 del .NET Framework.

Utilizando SmtpClient para el envío de mensajes con autenticación.

Dado que las nuevas clases bajo el Namespace System.Net.Mail del .NET Framework 2.0 ya no emplean CDO, las credenciales de autenticación ya no se especifican mediante Campos CDO de ningún tipo. En lugar de ello se utilizan o especifican las Credenciales de Red a través de la instancia de la clase SmtpClient creada para el envío de correo electrónico.

Si se desea autenticar con las credenciales del usuario actual debemos especificarlo utilizando la propiedad UseDefaultCredentials de SmtpClient y establecer su valor a True. Tal y como se muestra en la siguiente sentencia:

mySmtpClient.UseDefaultCredentials = True

Para especificar credenciales distintas deberá utilizarse la propiedad Credentials de SmtpClient que es de tipo ICredentialsByHost. Por lo que podemos asignar una instancia de la clase System.Net.NetworkCredential que albergará las credenciales a utilizar. Por ejemplo, para establecer las cadenas de usuario y contraseña (empleando Autenticación Básica) podemos utilizar la siguiente sentencia:

mySmtpClient.Credentials = _
New System.Net.NetworkCredential("usuario", "contraseña")

Ahora modifiquemos el ejemplo visto en los casos anteriores para utilizar las nuevas clases del .NET Framework 2.0.

Option Explicit On
Module Module1
  Sub Main()
    Dim oMsg As New System.Net.Mail.MailMessage
    With oMsg
      .From = "usuario@dominio"
      .To = "alguien@dominio"
      .Subject = "Test con SmtpClient"
      .Body = "Hola Mundo"
    End With
    Dim oClient As New System.Net.Mail.SmtpClient("smtp.mail.dominio")
    oClient.UseDefaultCredentials = False
    oClient.Credentials = _
    New System.Net.NetworkCredential("usuario", "contraseña")
    oClient.Send(oMsg)
  End Sub 
End Module

Nota: Debe cambiar los valores de los constructores y propiedades por aquellos que correspondan a su caso particular.

Si el servidor SMTP además requiere una conexión SSL, se debe utilizar la propiedad EnableSsl del SmtpClient con valor establecido a “true”.

oClient.EnableSsl = True

Tal y como se aprecia, el código necesario se ha simplificado y reducido considerablemente. Se puede reducir aun mas si se establece la configuración del envío de correo electrónico mediante el archivo de configuración del sitio web (web.config). Tal y como se muestra a continuación.

<configuration>
 <system.net>

   <mailSettings>
     <smtp deliveryMethod="Network">
       <network host="smtp.mail.dominio" defaultCredentials="false"
        userName="usuario" password="contraseña" />
     </smtp>
   </mailSettings>

 </system.net> 
</configuration>

Nota: La configuración SMTP también puede establecerse mediante la Herramienta Administración de sitios Web => ficha “Aplicación”.

De manera que el código queda reducido a:

Option Explicit On
Module Module1
    Sub Main()
    Dim oMsg As New System.Net.Mail.MailMessage
    With oMsg
      .From = "usuario@dominio"
      .To = "alguien@dominio"
      .Subject = "Test con SmtpClient"
      .Body = "Hola Mundo"
    End With
    Dim oClient As New System.Net.Mail.SmtpClient()
    oClient.Send(oMsg)
  End Sub 
End Module

Utilizar el archivo de configuración permite que el código sea reutilizable y la configuración fácil de personalizar si las condiciones han cambiado. Por ejemplo, si en lugar de un servidor SMTP externo ahora se desea utilizar el servicio SMTP de IIS, sólo tendremos que cambiar la configuración como se muestra a continuación:

<configuration>
  <system.net>

   <mailSettings>
     <smtp deliveryMethod="PickupDirectoryFromIis" />
   </mailSettings>

 </system.net>
</configuration>

Aunque también es posible establecer el método de envío (deliveryMethod) y otros parámetros mediante código -tal y como lo hemos visto- es mejor establecerlo mediante el archivo de configuración.

 

Aviso de seguridad

La utilización de Autenticación Básica implica enviar los valores de userName y password al servidor sin encriptar. Cualquier persona que este monitoreando el tráfico de la red puede ver las credenciales y utilizarlas para conectarse al servidor. Por ello se recomienda utilizar mecanismos de autenticación más seguros, tales como Kerberos o NTLM. Dichos protocolos se utilizan si se emplea defaultCredentials con el valor establecido a true (aplicable con el .NET Framework 2.0), siempre y cuando el servidor los soporte.

 

Conclusión

En el presente artículo se han presentado tres formas, una para cada versión del .NET Framework, para el envío de correo electrónico mediante servidores SMTP que requieren autenticación. Las dos primeras consisten en manipular los Campos de Configuración de CDO: “smtpserver”, “smtpserverport” y “sendusing” para establecer los valores necesarios, siendo la única diferencia que la primero utiliza CDO mediante Interoperabilidad COM, y la segundo emplea la propiedad Fields de System.Web.Mail.MailMessage presente a partir del .NET Framework 1.1. La última consiste en utilizar las propiedades de las nuevas clases del .NET Framework 2.0 agrupadas bajo el Namespace System.Net.Mail, así como utilizando los archivos de configuración.

Written by Willy Mejia

7 agosto, 2008 at 11:58

6 comentarios

Subscribe to comments with RSS.

  1. hola que tal.. valiosa informacion… yo pude enviar correo pero noto que lo hace cuando el outlook esta abierto y pasa un tiempo y deja de mandar correo desde mi site… justo abro outlook y otra vez funciona, pasa un tiempo y deja de funcionar… estoy pensando que es autentificacion como dicen aqui por que parece ser que outlook abre la sesion de la cuenta osea la autentifica cosa que en asp no hago… me sirvio mucho esta explicacion… un salu2

    Alejandro hdz.

    22 marzo, 2009 at 21:17

  2. Muy buena explicacion pero aun no lologro con mi server je jeje, saludos, pero eso si ke funciona!!
    ;-)

    Iori Yagami

    6 septiembre, 2009 at 01:23

  3. Hola, muy completo el artículo y de mucha utiliadad.. aunque ya es antiguo, lástima un poco de más información referente al encriptado de la autenticación, sin embargo por sí solo ya es información muy valiosa. Saludos!

    mouse

    28 abril, 2010 at 16:00

    • Como a que tipo de información te refieres???

      El cifrado en este caso queda en manos del servicio de correo, que a lo sumo solo podrá ofrecer una conexión SSL, y es a lo cual me refiero en el artículo con:

      >Si el servidor SMTP además requiere una conexión SSL,
      >se debe utilizar la propiedad EnableSsl del SmtpClient
      >con valor establecido a “true”.
      >
      >oClient.EnableSsl = True

      …como en el caso de Google u otros de pago.

      Ahora que sobre el cifrado de información de autenticación en aplicaciones web, es otra cosa, para lo cual puedes leer por ejemplo

      http://willyxoft.wordpress.com/2006/01/10/cifrado-datos-autenticacion-aspnet/

      También un poco viejo, pero sirve…

      Saludos,
      Willy Mejía

      Willy Mejía

      28 abril, 2010 at 23:47

  4. Estimado, buscando encontré esto. En referencia a la información, que la encontré muy profesional y bien descriptiva, ahora si es posible saber como puedo agregar al cuerpo del mensaje “Avances de Linea”, y que no se vea todo en sola linea.

    Letscan

    17 agosto, 2012 at 20:06

    • Es una cuestión de formato y manipulación de texto, más que del envío de correo en sí.

      Dado que los avances de línea van en el cuerpo del mensaje, bastaría con colocar un retorno y nueva línea (\r\n) si el mensaje es de texto:
      msg.Body = string.Format("Primera línea{0}{0}segunda…", "\r\n");
      …o bien el tag correspondiente <br /> si es con HTML:
      msg.Body = "Primera línea<br />segunda…";

      Saludos.

      Willy Mejia

      18 agosto, 2012 at 13:10


Deja un comentario

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 196 seguidores

A %d blogueros les gusta esto: