Israel Aece

Software Developer

Protegendo arquivos "não ASP.NET"

Nas versões 1.x do ASP.NET existe um problema quando nós requisitamos arquivos não ASP.NET, quais são "protegidos" através de Forms Authentication. O problema é que estes arquivos não passam pelos módulos de autenticação e autorização do ASP.NET, então independentemente das configurações no Web.Config, o recurso sempre será visível à todos os usuários, incluindo os usuários anonimos.

A solução para isso é mapear o arquivo protegido (extensão) usando o handler HttpForbiddenHandler no arquivo Web.Config da aplicação ou configurando o IIS diretamente, como eu mostrei neste post. Mas estas soluções são muito complicadas porque, no primeiro caso, o runtime do ASP.NET servirá todas as requisições e, consequentemente, a performance irá degradar; já a segunda solução, talvez é impossível porque o serviço de hospedagem não permite configurarmos o servidor deles.

O ASP.NET 2.0 resolveu este problema adicionando um novo handler chamado de DefaultHttpHandler (para os verbos: GET, HEAD e POST). Este handler será executado para todos os arquivos que não pertencem ao ASP.NET (como imagens, *.htm, *.asp, etc.), fazendo a validação do usuário e se o mesmo tem permissão para isso. Se for válido, o IIS devolverá a requisição ao responsável pelo processo deste recurso. Agora a performance é muito melhor e voce pode utilizar toda a infraestrutura (autenticação e autorização) do FormsAuthentication para proteger seus arquivos que não fazem parte do ASP.NET.

Segurança via Server.Transfer

Quando trabalhamos com acesso restrito à determinadas páginas/seções da aplicação ASP.NET, devemos ser muito cuidadoso com o uso do método Transfer da classe HttpServerUtility.

Imagine que o usuário com privilégios mínimos não tem acesso à uma determinada página por não pertencer a role "Administradores". Coloque um botão nessa sua página que todos tem acesso e chame o método Server.Transfer apontando para a página restrita. Rode a aplicação e clique no botão. Verá que mesmo que o usuário não tenha acesso aquela página específica, o mesmo conseguirá visualizá-la.

Isso acontece porque o processo de autenticação e autorização não acontece quando se usa o método Transfer. Isso na verdade já aconteceu, ou seja, essa validação já foi feita antes do ASP.NET chamar efetivamente o recurso (página) solicitado.

Para resolver esse problema voce tem duas formas; chamar o método Redirect ao invés do Transfer. Isso forçará uma requisição do browser/cliente, qual necessitará que o processo de autenticação e autorização novamente seja executado; já a segunda possibilidade é continuar utilizando o método Transfer e, na página de destino fazer a verificação se o usuário tem ou não acesso aquele recurso. Para isso, deve-se utilizar o método IsInRole.

EnableViewState e TextBox

Uma das grandes novidades do ASP.NET foi a introdução do Viewstate, o qual se encarrega de manter os estados dos controles entre os postbacks. Com isso, muitos pensam que a persistencia da propriedade Text está atrelada a propriedade EnableViewState do controle, ou seja, quando alguém não quer que a mesma seja mantida durante os postbacks, tenta definí-la como False, mas isso não é possível.

O que acontece é que o ASP.NET não usa o ViewState para isso e sim para uma outra finalidade. O segredo está na interface IPostBackDataHandler que o controle TextBox implementa. Ela contém um método chamado LoadPostData, que retorna um valor booleano indicando se o valor foi ou não alterado. É baseado neste retorno que é ou não disparado o evento TextChanged do TextBox. É passado como parametro para este método, a coleção de parametros enviados pelo post do formulário, qual internamente recupera o valor da propriedade value do respectivo controle. Podemos visualizar isso ao decompilar tal método:

protected virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection)
{
      base.ValidateEvent(postDataKey);
      string text1 = this.Text;
      string text2 = postCollection[postDataKey];
      if (!this.ReadOnly && !text1.Equals(text2, StringComparison.Ordinal))
      {
            this.Text = text2;
            return true;
      }
      return false;
}

Se colocar o controle TextBox no Wacth do Visual Studio, verá que no momento do postback, o valor da propriedade Text não terá nada se a propriedade EnableViewState do controle estiver como False. Somente depois de passar pelo método LoadPostData da classe é que o valor da propriedade Text é definido/restaurado, já que faz sentido, pois o ViewState do controle está desabilitado e a propriedade Text retornará vazio, qual será diferente do valor vindo pela coleção de parametros do post do formulário. Logo, o valor da propriedade Text é mantido mesmo com a propriedade EnableViewState esteja False. Para ilustrar, veja o código decompilado desta propriedade:

public virtual string Text
{
      get
      {
            string text1 = (string) this.ViewState["Text"];
            if (text1 != null)
            {
                  return text1;
            }
            return string.Empty;
      }
      set
      {
            this.ViewState["Text"] = value;
      }
}

Com isso vemos o porque os valores são mantidos. Se a propriedade EnableViewState estiver como False, retornará string.Empty que é diferente do valor vindo pela coleção (postCollection). Seguindo o fluxo do método LoadPostData, veremos que a condicional if é atentida e o valor do Text mudado, ou melhor, mantido. Para finalizar, verá que o evento TextChanged SEMPRE SERÁ DISPARADO, a menos que a propriedade Text seja igual a string.Empty e, o valor armazenado no ViewState["Text"] é somente utilizado para verificar se o evento TextChanged deve ou não ser disparado.

Server.Transfer é limitado?

Eu estou trabalhando em um projeto ASP.NET e estou criando um handler, que obviamente implementa a Interface IHttpHandler, para que processe e gere um arquivo binário para forçar o download do mesmo.

Depois que configurei o arquivo Web.Config, a requisição para arquivos com extensão "*.abc" serão agora interceptados por este handler. Mas existe um grande problema aqui, porque eu estou utilizando o método Server.Transfer, então eu não posso enviar para um dos overloads deste método uma instancia deste handler que criei ou chamar diretamente o "caminho virtual", como "Pagina.abc". Voce pode confirmar essa informação decompilando o método Transfer utilizando o Reflector:

[ --- Suprimido --- ]
else if (!(handler is Page))
{
   error = new HttpException(0x194, string.Empty);
}
[ --- Suprimido --- ]
if (error != null)
{
   [ --- Suprimido --- ]
   throw new HttpException(SR.GetString("Error_executing_child_request_for_handler",
      new object[] { handler.GetType().ToString() }), error);
}

Independentemente do overload do método Transfer que voce use, a mensagem de erro é a mesma: "Error executing child request for [handler | Pagina.abc].". A razão porque eu não utilizo o método Response.Redirect é que eu preciso enviar parametros através do coleção de Context.Items por questões de segurança.

A solução temporária para isso é herdar a classe Page ao invés de implementar a interface IHttpHandler no meu handler, mas eu acredito que isso não seja lá muito elegante.

Control State

Eu estou finalizando a leitura do livro de ASP.NET 2.0 do Luis Abreu e lá ele fala sobre a funcionalidade Control State. Ela é bem legal porque os controles de servidor não mais salvam informações importantes sobre o seu funcionamento no ViewState.

Nas versões 1.x do ASP.NET, essas informações são armazenadas no ViewState então, quando voce desabilitava o ViewState, alguns controles não funcionavam corretamente. Por exemplo, desabilite o ViewState e tente mudar o índice de paginação de um controle DataGrid. Voce verá que o controle desaparecerá.

Com o ControlState, essas informações são armazenadas em outro local e, se você desabilitar o ViewState, o controle continuará trabalhando normalmente. Desabilitar o ViewState é muito importante quando você não precisa manter o estado dos controle durante os postbacks ou quando a performance da sua aplicação está baixa.

Security Trimming - Visualização de Items

A Microsoft implementou na versão 2.0 do ASP.NET uma opção para criar uma forma de navegação dentro de um determinado WebSite. Isso possibilita a definição dos items de menus via arquivo XML (*.sitemap), onde definimos a hierarquia dos items que serão utilizandos e, consequentemente, apresentados pelos mais diversos controles de navegação que o ASP.NET 2.0 prove.

Para aqueles que não sabem, os arquivos *.sitemap permite-nos definir (através de um atributo chamado roles do elemento siteMapNode) os papéis que o usuário deverá pertencer para que sejam possíveis a visualização ou não destes itens nos controles que os carregarem.

Há uma certa confusão quando desejamos trabalhar com "Security Trimming" dentro desta aplicação, pois não basta apenas definirmos o atributo securityTrimmingEnabled do provider para True e as roles que serão permitidas no elemento siteMapNode dentro do arquivo *.sitemap. Além disso, é necessário que, as roles definidas no arquivo *.sitemap, precisam estar em sincronia com os elementos authorization no arquivo Web.Config, pois o ASP.NET analisa as roles do usuário corrente e o elemento authorization no arquivo de configuração antes de exibir ou não o item no controle.

Mas tudo isso AINDA NÃO É SUFICIENTE. A questão é que, por default, o elemento authorization é permitido para todos os usuários (allow users="*"), independentemente das roles, logo, se não se atentar a isso, os items continuam sendo visíveis, porém não acessíveis. Para solucionar isso, voce precisa antes de ir definindo os recursos que cada role terá, negar o acesso a todos os usuários, assim como é mostrado no trecho de código logo abaixo:

<configuration>
  <system.web>
    <authorization>
      <deny users="*" />
    </authorization>
  </system.web>
  <location path="Pagamentos.aspx">
    <system.web>
      <authorization>
        <allow roles="Gerente" />
      </authorization
    </system.web>
  </location>
  <location path="Extrato.aspx" >
    <system.web>
      <authorization>
        <allow roles="Funcionario" />
      </authorization>
    </system.web>
  </location>
<configuration>

Explorando Segurança do ASP.NET - WSAT

WSAT, Web Site Administration Tool, é uma aplicação ASP.NET fornecida junto com o Visual Studio .NET 2005 ou Visual Web Developer (versão Express). Esta aplicação fornece uma interface amigável para configurarmos as API's de Membership e Roles de uma determinada aplicação ASP.NET. O WSAT se encarrega de configurar o arquivo Web.Config (se o arquivo não existir, ele mesmo o cria) automaticamente na medida em que vamos alterando e definindo alguns valores. Além disso, se você optar pelo uso do SqlMembershipProvider, é automaticamente criado um banco de dados chamado ASPNETDB.MDF dentro do diretório App_Data da aplicação corrente, já incluindo todo o schema para suportar as API's que falamos anterioramente.

Para administrar sua aplicação ASP.NET através do WSAT, as credenciais do usuário que está executando o Visual Studio ou Visual Web Developer deve ter permissões de escrita e leitura no arquivo Web.Config e também à pasta App_Data da aplicação que está sendo administrada.

O WSAT está dividido em algumas Tabs: Application, Security e Provider. Veremos cada uma delas mais detalhadamente abaixo:

Tab Application

Nesta tab, você pode gerenciar as configurações mais comuns relacionadas à uma aplicação Web. Entre essas configurações temos:

  • Application Settings: esta opção permite-nos gerenciar as entradas de chave/valor, onde colocamos alguns valores que são utilizados pela aplicação para não deixarmos em hard-code, que comumente são: timeouts, URL para Web Services, endereços de arquivos e/ou diretórios usados dentro da aplicação. Tendo os valores aqui, faz com que se, algum dia precisarmos mudá-los, não será necessário recompilar a aplicação. Mas cuidado: os dados, por padrão, ficam em clean-text, ou seja, não será criptografado. Logo, não armazene dados sensíveis neste local.

  • Simple Mail Transfer Protocol (SMTP): se a aplicação exige o envio de e-mails você pode estar configurando o SMTP nesta seção. Para exemplificar o uso disso, lembre-se do controle PasswordRecovery que, dado um login, o ASP.NET se encarrega de enviar um e-mail para o usuário contendo a senha.

  • Application Status: você pode estar "desligando" a sua aplicação temporariamente para efetuar alguma manutenção.

  • Debug e Trace: nesta seção você pode estar definindo alguns parâmetros para configurar o Debug e Trace da sua aplicação, podendo habilitar ou desabilitar e também aplicar alguns filtros.

 

Figura 1 - Tab Application.

Tab Security

A Tab Security traz informações e faz o gerenciamento a respeito das roles da aplicação corrente para recursos específicos, o que permite restringir acesso à determinados usuários que estão contidos dentro de uma role específica. Além disso, permite também o gerencimento dos usuários da aplicação, podendo criar, alterar ou excluir usuários. É também fornecido um Security Setup Wizard, que permite-nos configurar um nível básico de segurança através deste wizard.

Temos duas formas de autenticação: Forms e Windows. Veremos sobre cada um logo abaixo:

  • Forms: este tipo de autenticação é utilizada para aplicações que são acessadas através da internet. Esta estrutura utiliza a infraestrutura de Membership e Roles para gerenciar os usuários e, através das roles, definir os recursos que ele pode ou não ter acesso.

  • Windows: este tipo já é voltado para uma rede local (intranet), onde podemos aproveitar a arquitetura do Windows para autenticar os usuários e, através dos grupos, gerenciarmos os recursos à que eles têm acesso. Neste cenário não é necessário a criação de uma página de login, pois as credenciais que serão utilizadas já foram informadas quando o usuário fez o login no Windows.

Figura 2 - Tab Security.

Como podemos ver na imagem acima, na seção Users podemos gerenciar os usuários da aplicação, inserindo, alterando ou excluindo-os. Ainda nesta seção há uma opção chamada Select authentication type, onde você define o tipo de autenticação que a sua aplicação usará. Na seção Roles você pode estar criando novas roles e ainda gerenciar os usuários que ela contém. É possível também através do link Disable Roles, habilitar ou não essa estrutura para a aplicação. Finalmente, na seção Access Rules, você pode estar definindo os acessos a determinados recursos da aplicação.

E, como podem reparar, o WSAT fará uso das classes Membership e Roles definidas para a aplicação internamente, via arquivo Web.Config.

Tab Provider

Na aba Provider você especifica o provider de Membership e Role que a sua aplicação irá utilizar. Logo, você pode ter vários providers especificados no seu arquivo Web.Config que a interface do WSAT interpreta e as exibe para que você possa selecionar.

Há duas opções nesta aba: Select a single provider for all site management data e Select a different provider for each feature (advanced). A primeira delas é usada quando utilizamos um mesmo provider para todas as funcionalidades. Já na segunda, podemos especificar diferentes providers para cada funcionalidade. As imagens abaixo mostram um exemplo destas configurações:

Figura 3 - Tab Provider.

Figura 4 - Tab Provider.