lunes, 12 de marzo de 2012

Cómo proteger tu servidor MailEnable en Internet

Últimamente estoy que lo tiro con los posts relativos a la seguridad en un servidor VPS en Internet. Si recuerdas, he escrito ya los siguientes posts:

Después de hablar de SQL Server, RDP y el FileZilla (servicio FTP), hoy toca hablar del servicio SMTP y del servidor MailEnable en su versión estándar (gratuita).

Te prometo que estos posts no me gustan en exceso pero, la única forma de acordarme de los pasos necesarios para asegurar mi servidor de nuevo en caso de desastre, es que los cuente en el blog y así perduren aunque mi servidor sea atacado por una legión de hackers malhumorados.

Cómo configurar el servidor MailEnable no te lo voy a contar porque, además de no ser ningún experto, está perfectamente expuesto en los siguientes enlaces: Installation guideQuick Start guide.

Sin embargo, lo que si voy a contar en este post es cómo asegurarnos de que, en un principio, nuestro servidor cumple con las normas de seguridad básicas que, en mi opinión, deberían estar activas.

STMP Relay

  • Sólo permitir envío de correo para usuarios autenticados.
  • Permitir envío para un rango de IPs privilegiadas (por defecto, sólo está activa la dirección local 127.0.0.1)

image

Ten en cuenta que esta configuración es la que hace posible que a los 5 minutos de levantar tu servidor de correo en Internet, no seas origen de spam y seas agregado a una lista negra de spammers como por arte de magia.

Mensaje de bienvenida

Ya puestos, cuantas menos pistas mejor, así que pasamos del mensaje de bienvenido por defecto a uno bastante más neutro que no da información sobre el servidor ni su versión.

Con el mensaje por defecto:

image

Con un mensaje personalizado:

image

image

No permitir correo anónimo para direcciones locales

Básicamente, si el destinatario del correo es una cuenta local al dominio de correo, no es necesario suministrar credenciales de seguridad, por lo que es posible enviar un correo “fake” del estilo cualquier_direccion@cualquier_dominio.algo a cuenta_local@tudominio.algo.

Si no te lo crees sigue los sencillos pasos expuestos en este blog antes de “cortar” el grifo.

Asumiendo que no queremos dar la oportunidad de enviar correo anónimo a nuestras direcciones de correo locales, es necesario activar la siguiente configuración:

image

Desactivar los comandos VRFY y EXPN

Para una lista completa de los comandos disponibles para STMP, visita el siguiente enlace: STMP Commands.

Básicamente VRFY y EXPN permiten comprobar la existencia de nombres de cuentas de correo o incluso devolver todas las cuentas de una lista. Más información en Whatever happened to VRFY?

Para desactivar los comandos, de nuevo en la pestaña “Advanced STMP” del conector de SMTP, accede a “Allowed STMP Commands”, desmárcalos y listo.

Cabe mencionar que con la configuración actual de nuestro servidor SMTP, no nos será posible por código .NET averiguar si una dirección de correo existe o no sin enviar un correo. Es decir, fallarán métodos como How to check if email address exists without sending an email? y componentes de terceros como Email Validador .NET.

Por último, sólo quiero comentarte que no soy ningún experto en MailEnable ni el servicio SMTP, así que es probable que me haya dejado muchas cosas “obvias” en el tintero, pero por algo se empieza.

Un saludo!

domingo, 4 de marzo de 2012

Visualizador de relaciones y restricciones en un DataSet

Quizás hoy en día un método extensor de DataSet y DataTable no sea muy popular porque es una tecnología de la que cualquier Geek de pro huye como la peste.

De hecho, hoy en día no tiene mucho sentido seguir utilizando DataSets puesto que tenemos disponibles otras tecnologías de accedo a datos mucho más modernas y mejor preparadas para la batalla, como son cualquier ORM del estilo de Entity Framework e incluso Linq To Sql (también en desuso pero al fin y al cabo también un ORM).

En cualquier caso, el otro día un amigo me comentaba en el correo que conocer la base de ADO.NET nos ayudará a entender mejor todo las nuevas propuestas de acceso a datos de Microsoft que, al final de los finales, siguen tirando por debajo código ADO.NET (es cierto que EF no utiliza DataSets pero al final y al cabo un DataSet es ADO.NET Core, un must-know).

En esta situación y no pudiendo olvidar que tarde o temprano tendremos que volver a tocar código viejo o heredado que utiliza DataSets, he creído oportuno realizar unos métodos extensores que me ayudarán a ver las relaciones y restricciones existentes en un DataSet a través de la ventana de inspección de Visual Studio.

El código de los métodos extensores es el siguiente (perdona que no sea C#, pero estoy seguro de que sabrás encontrar algún traductor en línea para pasar este código de VB a C#).

Module MyDataSetExtensions
    Private Sub DumpUniqueConstraintToHtml(ByVal uc As UniqueConstraint, ByVal dump As StringBuilder)
        dump.Append(String.Format("<p><b>UniqueConstraint {0}</b></p>", uc.ConstraintName))
        dump.Append(String.Format("<p>IsPrimaryKey {0}</p>", uc.IsPrimaryKey))
        dump.Append("<p>Columns</p>")
        dump.Append("<ul>")
        For Each col As DataColumn In uc.Columns
            dump.Append(String.Format("<li>{0}</li>", col.ColumnName))
        Next
        dump.Append("</ul>")
    End Sub
    Private Sub DumpForeignKeyConstraintToHtml(ByVal fkc As ForeignKeyConstraint, ByVal dump As StringBuilder)
        dump.Append(String.Format("<p><b>ForeignKeyConstraint: {0}</b></p>", fkc.ConstraintName))
        dump.Append(String.Format("<p>UpdateRule {0}</p>", fkc.UpdateRule.ToString()))
        dump.Append(String.Format("<p>DeleteRule {0}</p>", fkc.DeleteRule.ToString()))
        dump.Append(String.Format("<p>AcceptRejectRule {0}</p>", fkc.AcceptRejectRule.ToString()))
        dump.Append(String.Format("<p>RelatedTable {0}</p>", fkc.RelatedTable.TableName))
        dump.Append("<p>RelatedColumns</p>")
        dump.Append("<ul>")
        For Each col As DataColumn In fkc.RelatedColumns
            dump.Append(String.Format("<li>{0}</li>", col.ColumnName))
        Next
        dump.Append("</ul>")
        dump.Append("<p>Columns</p>")
        dump.Append("<ul>")
        For Each col As DataColumn In fkc.Columns
            dump.Append(String.Format("<li>{0}</li>", col.ColumnName))
        Next
        dump.Append("</ul>")
    End Sub
    Private Function GetHtmlStyle() As String
        Return "p, ul, li { margin-bottom: 2px; margin-top: 2px; }"
    End Function
    <System.Runtime.CompilerServices.Extension()> _
    Public Function DumpConstraintsToHtml(ByVal table As DataTable) As String
        Dim dump As New StringBuilder
        dump.Append(String.Format("<style>{0}</style>", GetHtmlStyle()))
        dump.Append(String.Format("<p>TableName {0}</p>", table.TableName))
        dump.Append(String.Format("<p>Constraints {0}</p>", table.Constraints.Count))
        For Each c As Constraint In table.Constraints
            If TypeOf c Is UniqueConstraint Then
                Dim uc As UniqueConstraint = DirectCast(c, UniqueConstraint)
                DumpUniqueConstraintToHtml(uc, dump)
            ElseIf TypeOf c Is ForeignKeyConstraint Then
                Dim fkc As ForeignKeyConstraint = DirectCast(c, ForeignKeyConstraint)
                DumpForeignKeyConstraintToHtml(fkc, dump)
            End If
        Next
        Return dump.ToString()
    End Function
    <System.Runtime.CompilerServices.Extension()> _
    Public Function DumpRelationsToHtml(ByVal dataSet As DataSet) As String
        Dim dump As New StringBuilder
        dump.Append(String.Format("<style>{0}</style>", GetHtmlStyle()))
        dump.Append(String.Format("<p>DataSetName {0}</p>", dataSet.DataSetName))
        dump.Append(String.Format("<p>Relations {0}</p>", dataSet.Relations.Count))
        For Each dr As DataRelation In dataSet.Relations
            dump.Append(String.Format("<p><b>RelationName {0}</b></p>", dr.RelationName))
            dump.Append(String.Format("<p>ParentTable {0}</p>", dr.ParentTable.TableName))
            dump.Append(String.Format("<p>ChildTable {0}</p>", dr.ChildTable.TableName))
            dump.Append(String.Format("<p>Nested {0}</p>", dr.Nested))
            dump.Append("<p>ParentColumns</p>")
            dump.Append("<ul>")
            For Each col As DataColumn In dr.ParentColumns
                dump.Append(String.Format("<li>{0}</li>", col.ColumnName))
            Next
            dump.Append("</ul>")
            If Not dr.ParentKeyConstraint Is Nothing Then
                dump.Append(String.Format("<p><b>ParentKeyConstraint</b></li>", dr.ParentKeyConstraint.ConstraintName))
                DumpUniqueConstraintToHtml(dr.ParentKeyConstraint, dump)
            End If
            dump.Append("<p>ChildColumns</p>")
            dump.Append("<ul>")
            For Each col As DataColumn In dr.ChildColumns
                dump.Append(String.Format("<li>{0}</li>", col.ColumnName))
            Next
            dump.Append("</ul>")
            If Not dr.ChildKeyConstraint Is Nothing Then
                dump.Append(String.Format("<p><b>ChildKeyConstraint</b></li>", dr.ChildKeyConstraint.ConstraintName))
                DumpForeignKeyConstraintToHtml(dr.ChildKeyConstraint, dump)
            End If
        Next
        Return dump.ToString()
    End Function
End Module

Teniendo disponible el código en nuestro proyecto podríamos agregar a la ventana inspección algo como esto:



  • MiDataTable.DumpConstraintsToHtml()

  • MiDataSet.DumpRelationsToHtml()

Y con el visualizador de HTML veríamos lo siguiente (fíjate que incluso en el código de los métodos extensores hemos agregado la etiqueta style de HTML para poder ajustar un poco el resultado):


image


image


No quiero acabar este post sin revelarte el amigo que sin él saberlo me impulsó a escribir este post. Estoy hablando de Óscar Osotorrio… y por supuesto no dejes de visitar su blog que te será muy útil seguro http://oscarsotorrio.com/ 


Un saludo!