Categories
ASP.NET Seguridad

Eliminar la cabecera X-AspNet-Version

Gracias a Mads Kristensen, me entero de una forma sencilla de eliminar la cabecera X-AspNet-Version que normalmente se envía al cliente.

code:

HTTP/1.1 200 OK
Date: Mon, 02 Jul 2007 23:02:51 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET

X-AspNet-Version: 2.0.50727

Set-Cookie: ASP.NET_SessionId=f31vuhudwugmjje511e3szuy; path=/; HttpOnly
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 7391

Para evitar este comportamiento, hay que poner en false el valor del atributo enableVersionHeader del elemento httpRuntime:

xml:

<?xml version="1.0"?>
<configuration>
    <system.web>
      <httpRuntime enableVersionHeader="false"/>
    </system.web>
</configuration>

No entiendo el motivo de que enableVersionHeader esté habilitado por omisión, sabiendo que es mejor ocultar las versiones del software que se usa.

Categories
ASP.NET Web

Proteger la descarga de archivos en ASP.NET 2

En la anterior entrega (Protección de archivos con ASP.NET 2 y IIS 6), vimos que a partir de IIS 6, es posible proteger archivos que no son procesados directamente por ASP.NET; si bien es cierto que este método es fácil de implementar, tiene el inconveniente que en hostings compartidos es difícil o imposible hacer eso ya que generalmente no se tiene acceso a la configuración de IIS.

Otra forma de proteger la descarga de archivos de usuarios anónimos, es crear una página intermedia que antes de entregar los contenidos de éstos, realice las validaciones pertinentes al usuario que hace la petición. Para evitar que los usuarios descarguen directamente los recursos protegidos (por si conocen la ruta) es mejor que éstos estén ubicados en lugares no accesibles directamente desde el navegador (Ejemplo: App_Data o el directorio padre de la aplicación).

El siguiente ejemplo, ilustra de manera básica como proteger los archivos que están ubicados en App_Data\Protected:.

asp:

<%@ Page AutoEventWireup="true" CodeFile="Default.aspx.cs" EnableViewState="false"
    Inherits="_Default" Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Download</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Label ID="messageLabel" runat="server"></asp:Label>
        <asp:Repeater ID="filesRepeater" runat="server">
            <HeaderTemplate>
                <ul>
            </HeaderTemplate>
            <ItemTemplate>
                    <li><a href='?file=<%# HttpUtility.UrlEncode(Container.DataItem.ToString()) %>'><%# Container.DataItem %></a></li>
            </ItemTemplate>
            <FooterTemplate>
                </ul>
            </FooterTemplate>
        </asp:Repeater>
    </div>
    </form>
</body>
</html>

csharp:

using System;
using System.IO;
using System.Web;

public partial class _Default : System.Web.UI.Page
{
    readonly static string basePath = HttpContext.Current.Server.MapPath("~/App_Data/Protected");

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!string.IsNullOrEmpty(Request.Params["file"]))
        {
            DownloadFile(Request.Params["file"]);
        }
        LoadFiles();
    }
    void LoadFiles()
    {
        string[] files = Directory.GetFiles(basePath);
        for (int i = 0; i < files.Length; i++)
        {
            files[i] = Path.GetFileName(files[i]);
        }
        filesRepeater.DataSource = files;
        filesRepeater.DataBind();
    }
    void DownloadFile(string file)
    {

        /*
         * Usar autenticación de formularios sobre esta página
         * o realizar algún otro proceso de validación del usuario
         */
        file = Request.MapPath(file, "~/App_Data/Protected", false);
        // Sólo descargar si el archivo está dentro de App_Data/Protected
        if (!file.StartsWith(basePath) || !File.Exists(file))
        {
            messageLabel.Text = "El archivo no existe";
            return;
        }
        Response.Clear();       
        Response.ContentType = "application/octet-stream";
        Response.AppendHeader("Content-Disposition", "attachment; filename=" + Path.GetFileName(file));
        Response.TransmitFile(file);
        Response.End();

    }
}

Al implementar este tipo de cosas, hay que validar bien el archivo a descargar, puesto que por un descuido, podemos hacer que el código de nuestra aplicación esté disponible a todo el mundo. 🙂

Categories
.NET ASP.NET Miniposts PHP Seguridad Sql Injection Web XSS

Versión estable de PHPIDS (Intrusion Detection System)

Hoy acaban de anunciar que ya existe un versión estable de PHPIDS, un sistema de detección de intrusos basado en expresiones regulares. Pueden descargar el código desde el repositorio o hacer pruebas para ver como funciona esta pequeña librería.

Por otro lado, también existe .NETIDS, que es una versión en .NET -- realizada por Martin Hinks -- de PHPIDS.

Categories
.NET ASP.NET Web

Enviar la codificación adecuada en aplicaciones Web (ASP.NET)

Una pregunta que normalmente veo en los foros que participo, se relaciona con el tema de la codificación de una aplicación Web, normalmente se ven preguntas como: configuración para admitir caracteres como la "ñ", codificación de caracteres deseada, problema con tildes, tildes y caracteres especiales o algunas variantes más.

La mayoría de veces intentan establecer esta codificación usando el elemento HTML meta:

html:

<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />

En otros lenguajes de programación como PHP esto funciona sin problemas porque no se envía automáticamente una cabecera HTTP que indique el tipo de documento y la codificación, pero en el caso de páginas que son procesadas por ASP.NET no ocurre esto, puesto que en cada documento que se envía al cliente, se especifica explícitamente el tipo y la codificación de la página. Si observamos parte del código del método GenerateResponseHeaders de la clase HttpResponse, se puede apreciar porque pasa eso.

csharp:

if ((this._statusCode != 0xcc) && (this._contentType != null))
{
        string text2 = this._contentType;
        if ((this._contentType.IndexOf("charset=", StringComparison.Ordinal) < 0) && (this._customCharSet || ((this._httpWriter != null) && this._httpWriter.ResponseEncodingUsed)))
        {
                string charset = this.Charset;
                if (charset.Length > 0)
                {
                        text2 = this._contentType + "; charset=" + charset;
                }
        }
        headers.Add(new HttpResponseHeader(12, text2));
}

Si no se especifica algún otro valor para la codificación (Charset) desde el código, este es tomado del elemento globalization del Web.config o machine.config.

xml:

<configuration>
   <system.web>
      <globalization
         requestEncoding="iso-8859-1"
         responseEncoding="iso-8859-1"/>

   </system.web>
</configuration>

En conclusión, utilizar sólo el elemento meta en páginas procesadas por ASP.NET no es suficiente, puesto que los navegadores -- al parecer -- prefieren el valor que se envía en las cabeceras HTTP.

Categories
.NET ASP.NET Web

Comentarios de lado del servidor en ASP.NET 2

Existen ocasiones en que para corregir determinados problemas es necesario deshabilitar ciertas partes de una página ASP.NET, esto en algunos casos puede conseguirse usando sólo los comentarios HTML, pero para otros casos en que cierta funcionalidad no está implementada o los nombres de los controles entran en conflicto, se produce un error porque la página sigue su flujo de ejecución.

En el siguiente ejemplo se produce un error porque el identificador texto se repite:

html:

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

        <!--<asp:Label ID="texto" runat="server">

</asp:Label>-->

        <asp:TextBox ID="texto" runat="server"></asp:TextBox>

    </div>
    </form>
</body>
</html>

Para solucionar este problema, se debe usar comentarios del lado del servidor (<%-- comentario --%>).

html:

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

        <%--<asp:Label ID="texto" runat="server"></asp:Label>--%>

        <asp:TextBox ID="texto" runat="server"></asp:TextBox>
    </div>
    </form>
</body>
</html>

Al usar este tipo de comentarios, el texto contenido dentro de <%-- --%> no será enviado al navegador.

Referencia: Tip/Trick: Using Server Side Comments with ASP.NET 2.0