lunes, 30 de abril de 2012

Hello World en Windows Phone 7

Este podría ser el típico post de tres líneas donde explico de una manera rápida y sencilla como hacer el clásico Hello World! u ¡Hola mundo! en Windows Phone 7. Pero (siempre hay un pero) cuando las cosas no quieren salir no salen y lo que empezó siendo algo de 5 minutos terminó siendo una pequeña batalla de un par de horas.

Comencemos. Lo primero que hice fue buscar el SDK para descargarlo. Esta búsqueda me llevo a está página en donde te puedes descargar el instalador web para el SDK. Como no me gustan los instaladores web, busqué el enlace a la ISO (está en la parte final de la página) y me lo descargué.

Aquí cometí mi primer error. Tras descargarme e instalar el SDK, al arrancar el Visual Studio 2010 Professional no veía por ningún lado las plantillas para crear una aplicación para Windows Phone 7. Si ejecutaba el Visual Studio 2010 Express que instala el SDK no tenía problemas, pero en el Professional que ya tenía instalado no había manera. Tras buscar por google di con la respuesta en un foro de la MSDN donde decía que si tu Visual Studio 2010 estaba en inglés el SDK a instalar debía ser el inglés, sino, las plantillas podía no instalarse correctamente (me hizo gracia el comentario de "podían"). Y yo me había descargado el SDK en español, bueno, realmente ni me había fijado. Mientras me descargaba la ISO inglesa del SDK desinstalé el SDK español. Tras lanzar de nuevo la instalación me tocó desinstalar a mano todo lo relacionado con el Expression Blend, así que otros minutos divertidos delante del ordenador viendo barras de progreso. Bien, con todo esto superado, y tras instalar el SDK en inglés, las plantillas para crear una aplicación Windows Phone 7 estaban disponibles en mi Visual Studio.

Tras crear un proyecto y simplemente cambiar el texto de un label, ejecuté la aplicación. Pensaba que ya nada más podría ir mal, pero me equivoqué. El Visual Studio me dio el siguiente error

Error 1 Connection failed because of invalid command-line arguments. 0 0

Tocaba otra vez, lanzarme a buscar la solución en google. En diversos foros y páginas web encontré las siguiente soluciones, que desde ya advierto que no funcionan (por lo menos a mi)
  • Cerrar la solución y borrar todos los ficheros .user y .suo. 
  • Lanzar el Visual Studio con privilegios de administrador.
Desesperado iba a abandonar, pero revisando lo que se me había instalado con el SDK vi que estaba el emulador de Windows Phone. Ya que no podía ver la aplicación funcionando lo mismo podría entretenerme viendo el emulador. Al lanzar el emulador, y como no podía ser de otra manera me dio un error.


El programa no puede iniciarse porque falta 'MfPlat.DLL" en el equipo. Intente reinstalar el programa para corregir este programa.

¿Algo más podía ir mal? Una vez más tocada tirar de google. La mayoría de las soluciones pasaban por descargar la DLL de algun repositorio de DLLs e irla copiando por diversas carpetas del sistema. Solución que por supuesto descarte totalmente. En un foro de MSDN vi que si tu sistema operativo era de la serie N había que instalar el Media Feature Pack for Windows 7 N desde aquí. Y como mi Windows es serie 7N, me tocó descargar el paquete de 300MB e instalarlo.

Tras varios minutos de descarga e instalación, volví a abrir la solución. Y tras un par de segundos de compilación vi como se abría el emulador y un momento después mi primera ¿aplicación?  para Windows Phone 7 veía la luz...



viernes, 27 de abril de 2012

Introducción al Sql Injection en MySql

Después de leer este comentario "Eso con MySql no pasaría. Esas cosas solo le pasan al Sql Server porque es de Microsoft..." en mi artículo sobre Introducción al Sql Injection en Sql Server no me he quedado más remedio que realizar el mismo experimento pero esta vez sobre MySql. Voy a dejar fuera del artículo como instalar las cosas que se necesitan para probar el código pero básicamente son
También nos lo podemos bajar todo junto de aquí.

Bien, con todo montado y funcionando lo primero que haremos será crear una tabla User similar a la anterior e insertar un registro. Podemos hacerlo usando las siguientes sentencias SQL
CREATE  TABLE `test`.`User` (
  `IdUser` INT NOT NULL AUTO_INCREMENT ,
  `Username` VARCHAR(50) NOT NULL ,
  `Password` VARCHAR(200) NOT NULL ,
  `Name` VARCHAR(50) NOT NULL ,
  PRIMARY KEY (`IdUser`) ,
  UNIQUE INDEX `IX_Username` (`Username` ASC) );

INSERT INTO `test`.`User` (`Username`, `Password`, `Name`) VALUES ('admin', 'admin', 'Administrador');
Y montaremos una aplicación con el mismo aspecto que la que usamos en el anterior artículo.


Tras esto pondremos este código en el manejador del evento Click del botón
try
{
 string connectionString = ConfigurationManager.ConnectionStrings["Foo"].ConnectionString;
 using (MySqlConnection connection = new MySqlConnection(connectionString))
 {
  connection.Open();
  string cmdText = "SELECT * FROM User WHERE Username='" + textUsername.Text + "' AND Password='" + textPassword.Text + "'";
  MySqlCommand command = new MySqlCommand(cmdText, connection);
  MySqlDataReader dataReader = command.ExecuteReader();
  if (dataReader.HasRows)
  {
   dataReader.Read();
   string name = dataReader["Name"].ToString();
   labelStatus.Text = "Bienvenido al sistema " + name;
  }
  else
   labelStatus.Text = "Nombre de usuario o contraseña incorrectos";
 }
}
catch (Exception ex)
{
 labelStatus.Text = ex.Message;
}
Bien, ahora es el momento de hacer las mismas pruebas que hicimos con el Sql Server.
Si ponemos el usuario y la contraseña de manera correcta


Y si la ponemos de manera incorrecta


Ahora toca hacer el Sql Injection, pero como no uso normalmente MySql no me queda más remedio que buscar como comentar una linea a partir de un carácter (o caracteres). La solución está aquí y es usando el carácter #. Con esto usaremos ' OR 1=1 #  como nombre de usuario y el resultado es el siguiente


Como vemos, hemos accedido al sistema simplemente conociendo un nombre de usuario, al igual que ocurría con el Sql Server. Para corregir este fallo de seguridad usaremos parámetros tal y como hicimos en el anterior artículo
try
{
    string connectionString = ConfigurationManager.ConnectionStrings["Foo"].ConnectionString;
    using (MySqlConnection connection = new MySqlConnection(connectionString))
    {
        connection.Open();
        string cmdText = "SELECT * FROM User WHERE Username=?Username AND Password=?Password";
        MySqlCommand command = new MySqlCommand(cmdText, connection);
        command.Parameters.Add(new MySqlParameter("?Username", textUsername.Text));
        command.Parameters.Add(new MySqlParameter("?Password", textPassword.Text));
        MySqlDataReader dataReader = command.ExecuteReader();
        if (dataReader.HasRows)
        {
            dataReader.Read();
            string name = dataReader["Name"].ToString();
            labelStatus.Text = "Bienvenido al sistema " + name;
        }
        else
            labelStatus.Text = "Nombre de usuario o contraseña incorrectos";
    }
}
catch (Exception ex)
{
    labelStatus.Text = ex.Message;
}
En este caso si usáramos el mismo nombre de usuario obtendríamos el siguiente resultado


Como vemos del Sql Injection no se escapa "casi nadie" sobre todo si somos nosotros mismos los que hacemos las cosas mal y no validamos la información introducida por el usuario.

miércoles, 25 de abril de 2012

Entity Framework Code First DbMigration (II)

En el anterior artículo sobre las migraciones automáticas en Entity Framework vimos como lanzar las migraciones desde la Package Manager Console de Visual Studio. La pregunta ahora sería, ¿podemos lanzar las migraciones desde el código de nuestra aplicación?, y la respuesta como no cabía esperar de otra manera es que si.

Para crear los ficheros de migración seguiremos usando la consola siguiendo los pasos descritos en el artículo anterior, pero para actualizar la base de datos haremos lo siguiente:
var config = new Migrations.Configuration();
var dbMigrator = new DbMigrator(config);
dbMigrator.Update();
Esto lanzará las migraciones pendientes. En caso que queramos lanzar una migración en concreto tendremos que indicárselo al método Update de la siguiente manera
var config = new Migrations.Configuration();
var dbMigrator = new DbMigrator(config);
dbMigrator.Update("201204191333459_InitialCreate");
Como vemos es bastante fácil lanzar nuestras actualizaciones desde código, pero cuidado con lo que hacemos sobre todo si la base de datos está en producción no sea que nos llevemos sorpresas desagradables.

Happy coding!

Mas info | Entity Framework Code First DbMigration (I)

martes, 24 de abril de 2012

CS0012: El tipo 'System.Data.Objects.DataClasses.EntityObject' está definido en un ensamblado al que no se hace referencia...

Hoy, haciendo una prueba me he tropezado el siguiente error al intentar usar un objecto definido en el modelo del Entity Framework

Mensaje de error del compilador: CS0012: El tipo 'System.Data.Objects.DataClasses.EntityObject' está definido en un ensamblado al que no se hace referencia. Debe agregar una referencia al ensamblado 'System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.

Lo que estaba haciendo en la vista era lo siguiente
@model Model.Persona
@{
    ViewBag.Title = "Detalle";
    Layout = "~/Views/Shared/_Public.cshtml";
}
<h2>@Model.Nombre</h2>
Vamos algo relativamente normal. Lo curioso del error era que mi proyecto ASP.NET MVC si tenía agregada la referencia a System.Data.Entity como se puede ver en la imagen


La solución pasa por añadir la referencia a System.Data.Entity en el web.config de la manera siguiente
<system.web>
  <compilation debug="true" targetFramework="4.0">
    <assemblies>
      <add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    </assemblies>
  </compilation>
</system.web>

Happy coding!

lunes, 23 de abril de 2012

Instalar el widget de karmacracy en blogger

Recientemente me he dado de alta en Karmacracy, un fenomenal servicio para compartir contenidos que va más alla de los clásicos acortadores de url como bit.ly, goo.glhootsuite. El principal problema que he tenido ha sido configurar su widget al final de cada post, ya que cada vez que insertaba el código en la plantilla que me da blogger obtenía el siguiente error 
Error parsing XML, line 1366, column 125: The reference to entity "type" must end with the ';' delimiter.
Al final lo he conseguido, y los pasos a seguir son  los siguientes
  1. Ir a la sección Templates
  2. Ir a Edit HTML
  3. En la pregunta contestar Proceed
  4. Marcar la opción Expand Widget Templates
  5. Buscar class='post-footer'
  6. Insertar en ese bloque el código del widget obtenido desde la página de Karmacracy sustituyendo todos los & por su codificación html tal y como se muestra en la imagen


Con esto ya tendremos el widget de Karmacracy funcionando correctamente al final de cada post de nuestro blog. Ahora sólo queda esperar a que nuestros lectores compartan el contenido de nuestro blog en su círculo.



Mas info | Karmacracy

Entity Framework Code First DbMigration (I)

Siguiendo con mi línea de aprendizaje de Entity Framework esta vez le ha tocado el turno a una de las novedades introducidas en la versión 4.3, las migraciones automáticas o DbMigration. Creo que su nombre deja claro para que sirve, pero básicamente nos da soporte para realizar actualizaciones de nuestra base de datos. ¿Y qué podemos actualizar?, pues básicamente podremos crear, renombrar y borrar tanto tablas como columnas, así como crear índices, claves ajenas o incluso ejecutar sentencias SQL para actualizar registros. ¿Impresionado? ¿No? Pues deberías, y sino sigue leyendo para que veas lo que podrás hacer.

Bien, para poder hacer uso de las migraciones deberás tener instalado la versión 4.3 de Entity Framework como mínimo, en caso que no tengas estas versión o no estés seguro de que versión estás usando te recomiendo una lectura de mi artículo sobre Entity Framework Fluent Api, donde en la primera parte cuento como actualizar el Entity Framework a través del Package Manager Console de Visual Studio.

Lo primero que haremos será crear nuestras clases POCO, y nuestro DataContext (esto también está explicado en el artículo anteriormente mencionado).
Primero las clases POCO
public class Blog
{
 public int IdBlog { get; set; }
 public string Title { get; set; }
 public string Url { get; set; }

 // Navigation properties
 public virtual ICollection<Post> Posts { get; set; }
}

public class Post
{
 public int IdPost { get; set; }
 public string Title { get; set; }
 public string Body { get; set; }  

 // Foreign key
 public int IdBlog { get; set; }

 // Navigation properties
 public virtual Blog Blog { get; set; }
 public virtual Category Category { get; set; }
 public virtual ICollection<Tag> Tags { get; set; }
}

public class Category
{
 public int IdCategory { get; set; }
 public string Name { get; set; }

 // Navigation properties
 public virtual ICollection<Post> Posts { get; set; }
}

public class Tag
{
 public int IdTag { get; set; }
 public string Name { get; set; }

 // Navigation properties
 public virtual ICollection<Post> Posts { get; set; }
}

Y ahora nuestro DbContext
public class DataContext : DbContext
{
 public DataContext() : base("DataContext") {
            
 }

 public DataContext(string nameOrConnectionString) : base(nameOrConnectionString)
 {

 }

 public DbSet<Blog> Blog { get; set; }
 public DbSet<Post> Post { get; set; }
 public DbSet<Category> Category { get; set; }
 public DbSet<Tag> Tag { get; set; }

 protected override void OnModelCreating(DbModelBuilder modelBuilder)
 {
  modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
  
  // Primary Keys
  modelBuilder.Entity<Blog>().HasKey(c => c.IdBlog);
  modelBuilder.Entity<Post>().HasKey(c => c.IdPost);
  modelBuilder.Entity<Category>().HasKey(c => c.IdCategory);
  modelBuilder.Entity<Tag>().HasKey(c => c.IdTag);

  // Properties
  modelBuilder.Entity<Blog>().Property(p => p.Title).IsRequired();
  modelBuilder.Entity<Blog>().Property(p => p.Title).HasMaxLength(50);

  modelBuilder.Entity<Post>().Property(p => p.Title).IsRequired();
  modelBuilder.Entity<Post>().Property(p => p.Title).HasMaxLength(50);
 }
}
Llegados a este punto ya podemos empezar a usar las migraciones propiamente dichas. Lo primero que haremos será habilitar las migraciones ejecutando lo siguiente en la consola
PM> Enable-Migrations
Si todo va bien deberemos ver algo parecido a esto
Detected database created with a database initializer. Scaffolded migration '201204191333459_InitialCreate' corresponding to current database schema. To use an automatic migration instead, delete the Migrations folder and re-run Enable-Migrations specifying the -EnableAutomaticMigrations parameter.
Code First Migrations enabled for project EntityFramework.CodeFirst.DbMigration.
¿Qué mas ha pasado? Pues se habrá creado una carpeta en nuestro proyecto con dos ficheros
  • *_InitialCreate.cs: Donde tendremos el script de creación de nuestra base de datos, en base a nuestro modelo de clase POCO.
  • Configuration.cs: Donde podremos configurar la migración y sobreescribiendo el método Seed llenar de datos nuestra base de datos.
¿Y ahora? Pues simple, actualizaremos nuestra base de datos con el script que hemos creado. Para esto usaremos el siguiente comando
PM> Update-Database
Y si todo ha ido bien veremos algo así
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Applying explicit migrations: [201204191333459_InitialCreate].
Applying explicit migration: 201204191333459_InitialCreate.
Si vamos a la base de datos veremos que se ha creado la base de datos y dentro de la tablas del sistema se ha creado __MigrationHistory. Esta tabla es usada por el Entity Framework para llevar un control de las versiones.

Ahora supongamos que queremos añadir un campo a nuestra tabla blog, por ejemplo el campo Author, que deberá ser de tipo varchar, obligatorio y tener una longitud máxima de 250 caracteres. Lo primero será añadir este campo al objeto Blog como propiedad
public string Author { get; set; }
Lo siguiente será crear un nuevo script de migración de la siguiente forma
PM> Add-Migration BlogAddAuthor
Y si todo va bien veremos lo siguiente
Scaffolding migration 'BlogAddAuthor'.
The Designer Code for this migration file includes a snapshot of your current Code First model. This snapshot is used to calculate the changes to your model when you scaffold the next migration. If you make additional changes to your model that you want to include in this migration, then you can re-scaffold it by running 'Add-Migration 201204191351431_BlogAddAuthor' again.
También veremos que se ha creado un fichero *_BlogAddAuthor.cs, con esta estructura
public partial class BlogAddAuthor : DbMigration
{
    public override void Up()
    {
        AddColumn("Blog", "Author", c => c.String());
    }
        
    public override void Down()
    {
        DropColumn("Blog", "Author");
    }
}
Primero decir, que el método Up se llama cuando actualizamos (update) la base de datos y el método Down se llama cuando retrocedemos a una versión (downgrade). Aunque podemos modificar esto para ajustarnos a nuestras necesidades
public override void Up()
    {
        AddColumn("Blog", "Author", c => c.String(nullable: false, maxLength: 250));
    }
¿Qué cosas podemos hacer en estos métodos? Pues tenemos a nuestro alcance los siguiente métodos a través de la clase DbMigration
  • AddColumn
  • AddForeignKey
  • AlterColumn
  • CreateIndex
  • CreateTable
  • DropColumn
  • DropForeignKey
  • DropIndex
  • DropPrimaryKey
  • DropTable
  • MoveTable
  • RenameColumn
  • RenameTable Sql
Si no hubiéramos añadido la propiedad a la clase, obtendríamos este fichero vacio y tendríamos que añadir las sentencias AddColumn y DropColumn nosotros mismos (no siempre todos lo campos de la base de datos estarán en nuestras clases). Para actualizar nuestro modelo nuevamente usaremos Update-Database aunque esta vez usaremos el modificador -Verbose para ver un log de lo se está haciendo
PM> Update-Database -Verbose
Using NuGet project 'EntityFramework.CodeFirst.DbMigration'.
Using StartUp project 'EntityFramework.CodeFirst.DbMigration'.
Target database is: 'DbMigration' (DataSource: .\sql2k8r2, Provider: System.Data.SqlClient, Origin: Configuration).
Applying explicit migrations: [201204191358443_BlogAddAuthor].
Applying explicit migration: 201204191358443_BlogAddAuthor.
ALTER TABLE [Blog] ADD [Author] [nvarchar](250) NOT NULL DEFAULT ''
[Inserting migration history record]


¿Podemos hacer más? Pues si, por ejemplo volver a un versión. En este caso volveremos a la versión inicial ejecutando esto
PM> Update-Database –TargetMigration:$InitialDatabase –Force
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Reverting migrations: [201204191358443_BlogAddAuthor, 201204191333459_InitialCreate].
Reverting explicit migration: 201204191358443_BlogAddAuthor.
Reverting explicit migration: 201204191333459_InitialCreate.
Si queremos ir a nuestra versión en concreto ejecutaremos
‘Update-Database –TargetMigration:"NombreDeLaVersion" –Force’
Para obtener más ayuda sobre los distintos comandos que tenemos (Enable-Migrations: Enables Code First Migrations in a project. Add-Migration: Scaffolds a migration script for any pending model changes. Update-Database: Applies any pending migrations to the database. Get-Migrations) tan solo debemos escribir lo siguiente en la Package Manager Console
PM> get-help Update-Database
Y obtendremos una ayuda detallada del comando.

Como vemos tenemos en nuestras manos una excelente herramienta para llevar una sincronización perfecta de nuestras base de datos

Happy coding!

viernes, 20 de abril de 2012

Introducción al Sql Injection en Sql Server

Desde hace algún tiempo intento participar lo más que puedo en los foros en español de la MSDN. Es un buen punto de encuentro para resolver dudas y la verdad es que de algunas respuestas se aprende muchísimo, e intentando ayudar a la gente refrescas muchos conocimientos que podrías tener en el baúl de los recuerdos.

Una de las cosas que me más sorprende es que en el foro de ADO.NET hay muchísimas preguntas en las que viendo el código se ve como la gente sigue trabajando con DataSet o DataTable. Las causas pueden ser muchas y este post no es el lugar más adecuado para discutir si este sistema de acceso a datos es mejor o peor que otros. Aunque como digo me sorprende, sobre todo si son aplicaciones nuevas teniendo en cuenta que hay alternativas que desde mi punto son mejores e incluso más fáciles de usar como Entity Framework, NHibernate o LLBLGen.

Otras cosas que se ven muchísimo, y es el motivo por el que escribo este post, es que se siguen concatenando cadenas para formar las consultas sql que luego se ejecutan a través de SqlCommand. Esto es un error desde muchos puntos de vista. El principal problema es que quedamos expuestos a ataques de Sql Injection, sin tener en cuenta que mantener este tipo de consultas es complicado.

 Este artículo no pretende ser una explicación completa y rigurosa sobre Sql Injection ya que existe gran cantidad de información al respecto en Internet pero si pretende ser una pequeña introducción sobre este tema.

Para este artículo creamos la sigiuente table en nuestro Sql Server
CREATE TABLE dbo.[User]
 (
 IdUser int NOT NULL IDENTITY (1, 1),
 Username varchar(50) NOT NULL,
 Password varchar(200) NOT NULL,
  Name varchar(50) NOT NULL
 )  ON [PRIMARY]
GO
ALTER TABLE dbo.[User] ADD CONSTRAINT
 PK_User PRIMARY KEY CLUSTERED 
 (
 IdUser
 ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO
CREATE UNIQUE NONCLUSTERED INDEX IX_Username ON dbo.[User]
 (
 Username
 ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ALTER TABLE dbo.[User] SET (LOCK_ESCALATION = TABLE)
GO
COMMIT

Y añadimos un registro con la siguiente sentencia
INSERT INTO [User] ([Username], [Password], [Name])
VALUES ('admin', 'admin', 'Administrador')
Como vemos es una tabla muy simple que podemos usar para el inicio de sesión en nuestra aplicación. También crearemos un programa muy simple que tendrá el siguiente aspecto


Para seguir con la demostración y tomándome alguna licencia sobre la arquitectura de aplicaciones usaremos el siguiente código en el botón de iniciar sesión
private void buttonLogin_Click(object sender, EventArgs e)
{
 try {
  string connectionString = ConfigurationManager.ConnectionStrings["Foo"].ConnectionString;
  using (SqlConnection connection = new SqlConnection(connectionString))
  {
   connection.Open();
   string cmdText = "SELECT * FROM [User] WHERE [Username]='" + textUsername.Text + "' AND [Password]='" + textPassword.Text + "'";
   SqlCommand command = new SqlCommand(cmdText, connection);    
   SqlDataReader dataReader = command.ExecuteReader();
   if (dataReader.HasRows)
   {
    dataReader.Read();
    string name = dataReader["Name"].ToString();
    labelStatus.Text = "Bienvenido al sistema " + name;
   }
   else
    labelStatus.Text = "Nombre de usuario o contraseña incorrectos";
  }
  }
  catch (Exception ex)
  {
   labelStatus.Text = ex.Message;
  }
}
Como vemos el código es muy simple. Obtenemos la cadena de conexión del fichero de configuración y creamos una conexión y un comando. A este comando, le asignamos la consulta que creamos y la ejecutamos. Si nos devuelve un registro, mostramos el nombre del usuario y si no hay registros mostramos el clásico error de inicio de sesión incorrecto. En caso de excepción, mostramos el mensaje.

Si nos ponemos a hacer pruebas, veremos que el sistema se comporta como esperamos. Si usamos admin/admin el sistema nos da la bienvenida


Pero si usamos otra combinación el sistema nos devuelve un error


Hasta aquí todo bien. Pero ahora vamos a ponernos un poco juguetones, y vamos a suponer que sabemos que en el sistema hay un usuario admin (¿en que sistema no hay un usuario admin, administrador, administrator o root?) pero no sabemos sabemos la contraseña (como es lógico, sino esto no tendría sentido). Si ponemos admin' AND 1=1 --' como nombre de usuario sin importar lo que pongamos como contraseña obtenemos lo siguiente


¿Sorprendido? ¿No? Pues deberías. El problema está en la concatenación de la consulta que usamos para autenticar al usuario. Al usar ese nombre de usuario la consulta que se ejecuta es la siguiente
SELECT * FROM [User] WHERE [Username]='admin' AND 1=1 --'' AND [Password]='loquesea'
Y está consulta siempre nos devolverá el usuario admin sin necesidad de la contraseña. Es verdad que hemos usado algo de información, ya que conocíamos la existencia de un usuario admin, pero nos sirve de demostración de los peligros que corremos si no hacemos nuestras consultas de manera correcta.

Supongo que a estas alturas te estarás preguntando como podemos hacer para evitar esto. Pues existen dos formas
  1. Usar parámetros.
  2. Usar un procedimiento almacenado.
Para usar parámetros tan sólo debemos modificar nuestro código anterior de la siguiente forma
try
{
 string connectionString = ConfigurationManager.ConnectionStrings["Foo"].ConnectionString;
 using (SqlConnection connection = new SqlConnection(connectionString))
 {
  connection.Open();
  string cmdText = "SELECT * FROM [User] WHERE [Username]=@Username AND [Password]=@Password";
  SqlCommand command = new SqlCommand(cmdText, connection);
  command.Parameters.Add(new SqlParameter("@Username", textUsername.Text));
  command.Parameters.Add(new SqlParameter("@Password", textPassword.Text));
  SqlDataReader dataReader = command.ExecuteReader();
  if (dataReader.HasRows)
  {
   dataReader.Read();
   string name = dataReader["Name"].ToString();
   labelStatus.Text = "Bienvenido al sistema " + name;
  }
  else
   labelStatus.Text = "Nombre de usuario o contraseña incorrectos";
 }
}
catch (Exception ex)
{
 labelStatus.Text = ex.Message;
}
Y si ejecutamos nuestra aplicación con el usuario "tramposo" vemos que el sistema se comporta de la manera correcta.


El uso de los parámetros hace que la comparación se haga de manera literal y no se interprete.
[Username] = admin' AND 1=1 --'
No se interpretan ni las comillas simples ni el doble guion que sirve para poner comentarios.
Para usar la opción de un procedimiento almacenado, tendremos que crear éste, en la base de datos de la siguiente manera
CREATE PROCEDURE SP_Login
 @Username VARCHAR(50),
 @Password VARCHAR(200)
AS
BEGIN
 SELECT * FROM [User] WHERE [Username]=@Username AND [Password]=@Password
END
GO
Y para usarlo tendremos que modificar nuestro código, quedando éste así
try
{
 string connectionString = ConfigurationManager.ConnectionStrings["Foo"].ConnectionString;
 using (SqlConnection connection = new SqlConnection(connectionString))
 {
  connection.Open();
  string cmdText = "SP_LOGIN";
  SqlCommand command = new SqlCommand(cmdText, connection);
  command.CommandType = CommandType.StoredProcedure;
  command.Parameters.Add(new SqlParameter("@Username", textUsername.Text));
  command.Parameters.Add(new SqlParameter("@Password", textPassword.Text));
  SqlDataReader dataReader = command.ExecuteReader();
  if (dataReader.HasRows)
  {
   dataReader.Read();
   string name = dataReader["Name"].ToString();
   labelStatus.Text = "Bienvenido al sistema " + name;
  }
  else
   labelStatus.Text = "Nombre de usuario o contraseña incorrectos";
 }
}
catch (Exception ex)
{
 labelStatus.Text = ex.Message;
}
Y vemos que una vez más el sistema se comporta de la manera esperada.


El alcance de este tipo de ataques no solo se reduce a poder acceder al sistema conociendo un nombre de usuario, sino a cosas mucho más peligrosas, como poder incluso a borrar una tabla, aunque para esto hacen falta que se cumplan más requisitos como conocer el nombre de la tabla en nuestra base datos (que también se puede obtener via Sql Injection). Simplemente imaginemos que usamos como nombre de usuario admin' admin AND 1=1; DROP TABLE [User] --.

Como vemos se pueden correr muchos muchísimos peligros si hacemos las cosas sin pensar, y hacer las cosas bien no cuesta tanto.

Happy coding!

jueves, 19 de abril de 2012

Como crear un proyecto para servicios web en Visual Studio 2010

Hoy en el foro de la MSDN ha surgido la duda de como crear un servicio web en Visual Studio 2010. La verdad es que está un poco rebuscado y por eso he decidido crear un pequeño artículo de como se debe hacer.
  • En nuestra solución añadimos un nuevo proyecto
  • Buscamos el desplegable donde estará marcado ".NET Framework 4"
  • Seleccionamos ".NET Framework 3.5", y vamos a "Visual C#" > "Web" para C# u "Otros lenguajes" > "Visual Basic" > "Web" para VB.NET y seleccionamos "Aplicación de servicio Web de ASP.NET"

  • Veremos que se añade un nuevo proyecto a nuestra solución
  • Marcamos el proyecto como proyecto de inicio y ejecutamos nuestra solución. Veremos que nuestro WebService se ejecuta sin problemas

NOTA: En caso de estar haciendo un proyecto nuevo recomiendo una lectura sobre Windows Communication Foundation

martes, 17 de abril de 2012

Entity Framework Code First Fluent API (I)

Code First es un nuevo enfoque de trabajo que aparece en el Entity Framework a partir de las versión 4.1, que nos permite crear nuestro modelo mediante clases POCO (Plain Old CLR Object), a partir de las cuales se generará nuestro modelo de base de datos. Otra característica es que en este modelo no existe el fichero edmx de definición del modelo (conceptual y de datos).

La principal ventaja de usar clases POCO es que el modelo de clases es más limpio ya que las clases no tiene ninguna dependencia con el Entity Framework (clase EntityObject). Por defecto se utiliza el nombre de nuestras clases y correspondientes propiedades para generar los nombre de tablas y campos. Esto puede ser más que suficiente para muchos proyectos, sobre todo si están empezando de cero, pero, ¿que ocurre si   queremos hacer modificaciones en nuestro modelo de datos? Para eso tenemos dos alternativas
  • Data Annotations
  • Fluent Api
Como ya he comentado al principio, para poder trabajar con CodeFirst es necesario usar la versión 4.1 (o superior) del Entity Framework. Para descargar e instalar esta versión la mejor alternativa es utilizar NuGet Gallery. Para eso en nuestro Visual Studio vamos a Herramientas > Library Package Manager > Package Manager Console. Con esto se nos abrirá una consola


donde escribiremos
Install-Package EntityFramework -Version 4.3.1
Si todo ha ido bien veremos algo parecido a esto


Ya tenemos la última versión del Entity Framework instalada en nuestro proyecto, así que, ¿ahora qué? Para demostrar el uso de Fluent Api para definir nuestro modelo de datos, voy a usar la estructura que podría tener un blog (muy simplificado). Para estos tendremos
  • Un blog con su identificador y título.
  • Una lista de post asociada a nuestro blog. Cada post tendrá su identificador, titulo, un título en html y un texto.
  • Cada post pertenecerá a una categoría y podrá tener una lista de etiquetas (tags). Tanto las categorías como las etiquetas, tendrán tanto un identificador como un nombre.
Con este esquema tan simple crearemos nuestras clases de la siguiente manera.
public class Blog
{
    public int IdBlog { get; set; }
    public string Title { get; set; }

    // Navigation properties
    public virtual ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int IdPost { get; set; }
    public string Title { get; set; }
    public string HtmlTitle { get; set; }
    public string Body { get; set; }

    // Foreign key
    public int IdBlog { get; set; }

    // Navigation properties
    public virtual Blog Blog { get; set; }
    public virtual Category Category { get; set; }
    public virtual ICollection<Tag> Tags { get; set; }
}

public class Category
{
    public int IdCategory { get; set; }
    public string Name { get; set; }

    // Navigation properties
    public virtual ICollection<Post< Posts { get; set; }
}

public class Tag
{
    public int IdTag { get; set; }
    public string Name { get; set; }

    // Navigation properties
    public virtual ICollection<Post> Posts { get; set; }
}


Una vez hecho esto empezaremos a definir nuestro contexto de la siguiente manera
public class DataContext : DbContext { }
Ahora definiremos un constructor donde especificaremos el nombre de la cadena de conexión y el conjunto de entidades que serán mapeadas (en nuestro caso todas)
public class DataContext : DbContext
{
    public DataContext() : base("DataContext") { }

    public DbSet<Blog> Blog { get; set; }
    public DbSet<Post> Post { get; set; }
    public DbSet<Category> Category { get; set; }
    public DbSet<Tag> Tag { get; set; }
}
Lo siguiente que debemos hacer es definir nuestro modelo de datos, por ejemplo, definiendo cuales son las claves de nuestras tablas. Para estas definiciones usaremos la sintaxis que nos brinda Fluent Api, así que, sobreescribiremos el método OnModelCreating de nuestro DataContext y escribiremos lo siguiente para definir nuestras claves primarias
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // Primary Keys
    modelBuilder.Entity<Blog>().HasKey(c => c.IdBlog);
    modelBuilder.Entity<Post>().HasKey(c => c.IdPost);
    modelBuilder.Entity<Category>().HasKey(c => c.IdCategory);
    modelBuilder.Entity<Tag>().HasKey(c => c.IdTag);           
}
Con este código "casi" ya podríamos crear nuestro primer modelo de datos, y ver como funciona Code First con Fluent Api. Tan solo deberíamos añadir este código a nuestro programa (para este ejemplo yo lo estoy poniendo en el constructor de un formulario)
public Main()
{
    InitializeComponent();

    Database.SetInitializer<DataContext>(new DropCreateDatabaseIfModelChanges<DataContext>());

    using (DataContext context = new DataContext()) {
        Blog b = context.Blog.FirstOrDefault();
    }
}
Bien este código tiene dos partes importantes
  • En la primera parte indicaremos cual es la estrategia de incialización de nuestro modelo. Entity Framework Code First incluye 3 inicializadores (ojo con estos inicializadores cuando pasemos nuestra aplicación a producción porque podemos perder todos nuestros datos): 
    1. CreateDatabaseIfNotExist
    2. DropCreateDatabaseAlways
    3. DropCreateDatabaseIfModelChanges
  • En la segunda parte hacemos una simple consulta (obtenemos el primer blog) a nuestro contexto porque si no realizamso una consulta no se ejecutaría nuestro inicializador (si nunca accedemos a nuestros datos, para que se va a perder el tiempo en crear el modelo de datos).
Si todo va bien veremos que el siguiente modelo de datos se ha creado

Vemos que con el modelo conceptual (clases POCO) y la definición de las claves primarias Entity Framework ha creado un esquena de base de datos que se ajusta perfectamente a nuestra definición. Inlcluso a creado una tabla intermedia (TagPost) para poder gestionar la relación muchos a muchos que hay entre los Posts y los Tags.

A partir de aquí podemos modificar nuestro modelo de datos a través de Fluent Api añadiendo estas lineas al método OnModeloCreating. Por ejemplo:

Evitar que la clave primaria sea un identity en la tabla Categoria
modelBuilder.Entity<Category>()
  .Property(p => p.IdCategory) 
  .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Forzar a que un campo sea obligatorio (NOT NULL) y tenga una longitud máxima de 50 caracteres
modelBuilder.Entity<Blog>().Property(p => p.Title).IsRequired();
modelBuilder.Entity<Blog>().Property(p => p.Title).HasMaxLength(50);
Evitar que una propiedad de nuestro modelo conceptual esté en en la base de datos
modelBuilder.Entity<Post>().Ignore(p => p.HtmlTitle);
Si volvemos a ejecutar nuestra aplicación y dado que nuestra estrategia de inicialización era borrar la base de datos si el modelo ha cambiado, y éste ha cambiado, veremos que que nuestros cambios se ha realizado de la forma esperada.
  • El identificador de la tabla Category no es un identity
  • Se han modificado la características del campo Title de la tabla Blog

  • El campo HtmlTitle no se mapea en la tabla Post

Como vemos, tenemos total libertad para ir refinando nuestro modelo de datos a partir de nuestro modelo conceptual.

Happy coding !

lunes, 16 de abril de 2012

Registrando los cambios de nuestros objectos en Entity Framework (II)

En mi anterior artículo sobre como registrar cambios de nuestros objetos en el Entity Framework dejé sin terminar una parte. En concreto, la parte en la que obtendríamos el IdResource en nuestro TrackFactory.

La primera alternativa que utilice fue la siguiente
private Track TrackFactory(ObjectStateEntry entry)
{
 Track track = new Track();
 track.IdTrack = Guid.NewGuid();
 track.TableName = entry.EntitySet.Name;
 track.FC = DateTime.Now;
 track.UC = string.Empty;

 if (entry.State == EntityState.Added)
 {
  // Insert (I)
  track.IdResource = entry.EntityKey.EntityKeyValues[0].Value.ToString();  
  track.NewData = this.GetXml(entry, false);
  track.Action = TrackActionsEnum.I.ToString();
 }
 else if (entry.State == EntityState.Deleted)
 {
  // Update (U)
  track.IdResource = entry.EntityKey.EntityKeyValues[0].Value.ToString();
  track.OldData = this.GetXml(entry, true);
  track.NewData = this.GetXml(entry, false);
  track.Action = TrackActionsEnum.U.ToString();
 }
 else
 {
  // Delete (D)
  track.IdResource = entry.EntityKey.EntityKeyValues[0].Value.ToString();
  track.OldData = this.GetXml(entry, true);
  track.Action = TrackActionsEnum.D.ToString();
 }

 return track;
}
Esta solución hubiera sido fantástica salvo porque no funciona a la hora de obtener el identificador en la inserción de un registro, ya que EntityKey es nulo. Tras darle varias vueltas al asunto la única posibilidad que me quedó fue recorrer por reflexión todas las propiedades del objeto y detectar aquella que tuviera el atributo EdmScalarPropertyAttribute definido y la propiedad EntityKeyProperty a true. Al final el código no queda muy largo y es el siguiente
private string GetIdResource(ObjectStateEntry entry) {
 object entity = entry.Entity;

 PropertyInfo[] properties = entity.GetType().GetProperties();
  foreach (PropertyInfo pi in properties)
  {
  System.Object[] attributes = pi.GetCustomAttributes(true);
  foreach (object attribute in attributes)
    {
   if (attribute is EdmScalarPropertyAttribute)
    if (((EdmScalarPropertyAttribute)attribute).EntityKeyProperty == true)
     return pi.GetValue(entity, null).ToString();
  }
 }

 return null;
}
Por lo que el método TrackFactory quedo así al final
private Track TrackFactory(ObjectStateEntry entry)
{
        // ...
 if (entry.State == EntityState.Added)
 {
  // Insert (I)
  track.IdResource = this.GetIdResource(entry); 
  track.NewData = this.GetXml(entry, false);
  track.Action = TrackActionsEnum.I.ToString();
 }
        // ...
}
Con esta mejora es fácil hacer el seguimiento a nuestros cambios. Por ejemplo con este código
using (DataContext context = new DataContext())
{
 // Insertamos una películas
 context.AddToPelicula(new Pelicula() { IdPelicula = 1, Titulo = "El padrino", Sinopsis = "...", Calificacion = 10 });
 context.SaveChanges();
    
 // Modificamos la película
 Pelicula p1 = context.Pelicula.Where(m => m.IdPelicula == 1).Single();
 p1.Calificacion = 9;
 context.SaveChanges();

 p1 = context.Pelicula.Where(m => m.IdPelicula == 1).Single();
 p1.Calificacion = 10;
 context.SaveChanges();

 // Borramos la película
 p1 = context.Pelicula.Where(m => m.IdPelicula == 1).Single();
 context.Pelicula.DeleteObject(p1);
 context.SaveChanges();
}
Sería fácil (sabiendo el Id que tenía la película en la base de datos, ver la secuenda de la vida del registro en la base de datos como vemos en nuestra tabla.


En la próxima entrega intentaré resolver de una forma "no muy elegante" (pero tampoco he encontrado otra) el caso de la inserciones de registros donde el Id es un identity, por lo que no conocemos el valor del Id antes de grabar. Lo que nos llevará a tener que grabar todos nuestros objectos en la base de datos para posteriormente grabar el registro de cambios.

Happy coding!

 Mas Info | Registrar cambios en nuestros objecto en el Entity Framework (I)

miércoles, 11 de abril de 2012

Registrando los cambios de nuestros objetos en Entity Framework (I)

En estos días de descanso me he propuesto meterme más en las tripas del Entity Framework. Para ello me he planteado hacer un pequeño sistema de control de cambios en el que se controle cuando se inserta o elimina un registro, o si se modifica, los valores que tenía antes y después de la modificación.

La primero entonces sería interceptar, o bien las inserciones, borrado o modificaciones de los objetos o detectar cuando se van a grabar los cambios. Tras leer la documentación que hay parece ser que la única valida es engancharnos a el evento SavingChanges que dispara la clase ObjectContext antes de iniciar el grabado de nuestros datos.

Con esto claro, me creo el edmx de una base de datos muy simple donde tan solo tengo una tabla que almacena películas, con su titulo, sinopsis y calificación. Despliego el Model.edmx y luego abro el Model.Designer.cs para ver el nombre exacto de la clase y así poder crear una clase parcial donde pondré toda la lógica de mi sistema de control de cambios.



Con esto me creo mi clase parcial DataContext y ahí me engancharé al evento SavingChanges. Ahora bien, ¿donde me engancho a este evento? Revisando el objeto DataContext veo que hay un método parcial que se llama OnContextCreated que es llamado cuando el contexto se crea y que parece un buen sitio donde engancharnos al evento, ya que sino tendríamos que sobreescribir los 3 constructores de la clase ObjectContext. El código de nuestra clase quedaría así

public partial class DataContext
{
  partial void OnContextCreated()
  {
    this.SavingChanges += new EventHandler(DataContext_SavingChanges);
  }

  private void DataContext_SavingChanges(object sender, EventArgs e)
  {
    // Gestión del evento SavingChanges
  }
}

Con esto ya tenemos la primera parte del trabajo hecha, que es interceptar las llamadas al método SaveChanges de nuestro contexto. Ahora queda la segunda parte del trabajo, detectar los cambios en nuestros objectos para registrar la información donde estimemos oportuno, en mi caso, en la base de datos.


Para esto lo primero que haremos será definir una tabla donde almacenaremos los cambios. La tabla tendría una pinta como esta


Donde:
  • IdTrack: Identificador de cada registro en la tabla.
  • TableName: Nombre de la tabla donde estamos insertando, modificando o borrando los registros.
  • Action: Acción a realizar, inserción (I), modificación (U) o borrado (D).
  • IdResource: Identificador del registro insertado, modificado o borrado en su tabla.
  • OldData y NewData: Objeto antes y después de ser insertado, modificado o borrado.
  • UC y FC: Usuario y fecha de creación del registro en la tabla de Tracks.
Una vez hecho tendremos que recorrer la lista de objectos que mantiene en nuestro DataContext y obtener aquellos que han cambiado. Para esto usaremos el ObjetctStateManager. El ObjectStateManager mantiene el estado de los objetos y la administración de identidades en las instancias de tipo de entidad y en las instancias de relación.
IEnumerable<ObjectStateEntry> pendingChanges = 
    this.ObjectStateManager.GetObjectStateEntries(
        EntityState.Added | 
        EntityState.Deleted | 
        EntityState.Modified);

En pendingChanges tenemos aquellos elementos que han cambiado y que deberemos procesar para registrar los cambios. Tan solo debemos recorrer la lista descartando aquellos elemento que sean una relación o que sean del tipo Track (no vamos a registrar los cambios de los objetos que registran los cambios).
foreach (ObjectStateEntry obj in pendingChanges)
{
    // Comprobamos si el objeto no es una relación y no es de tipo Track
    if (!(obj.IsRelationship) && (obj.Entity != null) && !(obj.Entity is Track))
    {
        Track audit = this.TrackFactory(obj);
        tracks.Add(audit);
    }
}

if (tracks.Count > 0)
{
    foreach (var audit in tracks)
        this.AddToTrack(audit); // Añadimos los Tracks
}
Para crear el objeto Track, usaremos el método TrackFactory que tendría esta pinta
private Track TrackFactory(ObjectStateEntry entry)
{
    Track track = new Track();
    track.IdTrack = Guid.NewGuid();
    track.TableName = entry.EntitySet.Name;
    track.FC = DateTime.Now;        
    track.UC = string.Empty;

    if (entry.State == EntityState.Added)
    {
        // Insert (I)
        track.NewData = this.GetXml(entry, false);
        track.Action = TrackActionsEnum.I.ToString();
    }
    else if (entry.State == EntityState.Modified)
    {
        // Update (U)
        track.OldData = this.GetXml(entry, true);
        track.NewData = this.GetXml(entry, false);
        track.Action = TrackActionsEnum.U.ToString();
    }
    else
    {
        // Delete (D)
        track.OldData = this.GetXml(entry, true);
        track.Action = TrackActionsEnum.D.ToString();
    }

    return track;
}
Como vemos en el método tan solo miramos el estado en el que se encuentra la entidad (Added, Modified o Deleted) para consecuentemente montar el objeto Track. En caso de ser un objeto nuevo, dicho objeto sólo tendrá valores nuevos, y en caso de ser un borrado, sólo tendrá valores viejos. En el caso de las modificaciones, guardamos los valores antes y después de la modificación. El método GetXml tan solo genera un xml con el objeto que le pasemos haciendo uso del XmlSerializer de .NET.

Con esto ya tenemos montado la estructura básica de nuestro sistema, así que va siendo hora de probarlo. Para probarlo primero insertamos dos registros en nuestra base de datos de la siguiente manera
using (DataContext context = new DataContext())
{
    // Insertamos dos películas
    context.AddToPelicula(new Pelicula() { IdPelicula = 1, Titulo = "El padrino", Sinopsis = "...", Calificacion = 10 });
    context.AddToPelicula(new Pelicula() { IdPelicula = 2, Titulo = "La guerra de las galaxias", Sinopsis = "...", Calificacion = 10 });
    context.SaveChanges();
}
Y si consultamos la base de datos vemos lo siguiente
Luego probaremos a modificar y a borrar para ver el resultado
// Modificamos una película
Pelicula p1 = context.Pelicula.Where(m => m.IdPelicula == 1).Single();
p1.Calificacion = 9;
context.SaveChanges();

// Borramos una película
Pelicula p2 = context.Pelicula.Where(m => m.IdPelicula == 2).Single();
context.Pelicula.DeleteObject(p2);
context.SaveChanges();
Si consultamos la base de datos veremos que también se han registrado las operaciones
Si analizamos los campos OldData y NewData en el caso de la modificación vemos que efectivamente se guarda el estado del objeto antes de modificar y el estado del mismo después de la modificación
Como vemos el sistema funciona correctamente aunque hay algunos detalles que pulir como asignar el identificador del objecto que se está registrando al objeto Track. Esto es relativamente simplemente si lo identificadores son asignados por nosotros, pero se complica es caso que sean valores asignados en la base de datos, como puede ser valores por defecto (newid() en caso de los uniqueidentifier) o identity.

Otro punto a tener en cuenta es buscar la forma de desacoplar este código del ObjectContext y que pudiera ser un módulo independiente y que funcionará con cualquier .dbml que queramos. Tengo algunas al respecto pero lo dejaré para otros artículos.

Cualquier duda, comentario o sugerencia será bien recibida.

Happy coding!

Mas info | Entity Framework

lunes, 9 de abril de 2012

#1 Error, navegador no reconocido

Hoy voy a inaugurar un nuevo tipo de artículo, en los que iré poniendo fallos, que algunas veces son graciosos y otras vez no lo son tanto, dentro del mundo de la informática o la tecnología en general. Para inaugurar está sección os muestro un aviso que da la página de Microsoft Partner Nework a la hora de conectarnos al Partner Membership Center con el Internet Explorer 9.


En fin, que ni en Microsoft saben como identificar su navegador favorito...

jueves, 5 de abril de 2012

Unificando los métodos de hash MD5 y SHA256 en C#

Si analizamos las clases MD5CryptoServiceProvider y SHA256CryptoServiceProvider vemos que estas heredan de MD5 y SHA256 respectivamente, pero tanto la clase MD5 como SHA265 heredan de la clase HashAlgorithm. Con esto nos sería bastante fácil unificar nuestras rutinas de encriptación de la siguiente manera
public string Encrypt(string input, HashAlgorithm hashAlgorithm)
{
  byte[] inputBytes = Encoding.UTF8.GetBytes(input);
  byte[] hashedBytes = hashAlgorithm.ComputeHash(inputBytes);

  StringBuilder output = new StringBuilder();

  for (int i = 0; i < hashedBytes.Length; i++)
    output.Append(hashedBytes[i].ToString("x2").ToLower());

return output.ToString();
}

public string MD5Encrypt(string input)
{
  return Encrypt(input, new MD5CryptoServiceProvider());
}

public string SHA256Encrypt(string input)
{
  return Encrypt(input, new SHA256CryptoServiceProvider());
}
No debemos olvidarnos el importar el espacio de nombres System.Security.Cryptography.
using System.Security.Cryptography;
Happy coding!

martes, 3 de abril de 2012

Como obtener el hash SHA256 en C#

Según la wikipedia, la familia SHA (Secure Hash Algorithm, Algoritmo de Hash Seguro) es un sistema de funciones hash criptográficas relacionadas de la Agencia de Seguridad Nacional de los Estados Unidos y publicadas por el National Institute of Standards and Technology (NIST). El primer miembro de la familia fue publicado en 1993 es oficialmente llamado SHA. Sin embargo, hoy día, no oficialmente se le llama SHA-0 para evitar confusiones con sus sucesores. Dos años más tarde el primer sucesor de SHA fue publicado con el nombre de SHA-1. Existen cuatro variantes más que se han publicado desde entonces cuyas diferencias se basan en un diseño algo modificado y rangos de salida incrementados: SHA-224, SHA-256, SHA-384, y SHA-512 (llamándose SHA-2 a todos ellos).

Al igual que con MD5, .NET nos ofrece la posibilidad de obtener el valor SHA256 de una manera muy sencilla. Tan sólo debemos usar el siguiente código
public string SHA256Encrypt(string input)
{
  SHA256CryptoServiceProvider provider = new SHA256CryptoServiceProvider();

  byte[] inputBytes = Encoding.UTF8.GetBytes(input);
  byte[] hashedBytes = provider.ComputeHash(inputBytes);

  StringBuilder output = new StringBuilder();

  for (int i = 0; i < hashedBytes.Length; i++)
    output.Append(hashedBytes[i].ToString("x2").ToLower());

  return output.ToString();
}
También hay que tener en cuenta que debemos importar el espacio de nombres System.Security.Cryptography.
using System.Security.Cryptography;

Algunos ejemplos
SHA256('') = 4574890e042bd574ae8d75562fe5bca34e7e5b518d9bbe2a7d5b39d1f6ed63c1
SHA256('SHA256') = b3abe5d8c69b38733ad57ea75e83bcae42bbbbac75e3a5445862ed2f8a2cd677

Nota: En este algoritmo he usado la clase StringBuilder para concatenar el resultado. En el caso del algoritmo MD5 este cambio refleja una mejoría de unos 100ms en caso que calculemos el hash 250.000 veces. Este mejoría es despreciable en caso que queramos calcular el hash para un solo registro. En caso del algoritmo SHA256 casi no presenta una mejoría ya que el porcentaje de tiempo concatenando la cadena es realmente despreciable comparado con el tiempo que lleva el calculo del hash.

Happy coding!