Israel Aece

Software Developer

Propriedade ApplicationName

Quando instalamos a estrutura de Membership do ASP.NET 2.0, temos uma tabela que armazena o nome das aplicações (aspnet_Applications) que estão sendo acomodadas dentro daquele servidor SQL Server. Essa tabela possui uma coluna chamada ApplicationName e, se valor é exposto através de uma propriedade estática, também chamada ApplicationName, da classe Membership.

A propriedade ApplicationName da classe Membership trata-se de uma propriedade de escrita/leitura. Sendo assim, voce pode alterar o nome da aplicação em tempo de execução e, a partir dai, passar a executar todos os métodos baseados nesta aplicação. Eis um exemplo:

Response.Write(Membership.ValidateUser("israel", "passw0rd").ToString());
Membership.ApplicationName = "NomeDaOutraAplicacao";
Response.Write(Membership.ValidateUser("israel", "passw0rd").ToString());

Apesar de funcionar, devemos nos atentar a questão do acesso em um ambiente multithreading, como é o caso do ASP.NET. Como a estrutura de Provider Model armazena e compartilha uma única instancia do provider ativo (padrão Singleton), a alteração em um local refletirá em todas as requisições e, conseqüentemente, podemos ter comportamentos inesperados. Geralmente isso só funcionaria em uma aplicação administrativa, que permitisse apenas um único usuário por vez acessando-a.

Credenciais de ASP.NET para ASP

Recentemente estive envolvido em um problema que muita gente acredito já ter passado. Trata-se de enviar dados de uma página ASP.NET para uma página ASP que encontra-se dentro da mesma aplicação (mesmo diretório virtual). Para ser mais preciso, precisava enviar dados as credenciais de acesso (login) para a página ASP.

Para enviar os parametros, poderia ser via querystrings ou cookies, mas lembrando que é preciso aplicar algum algoritmo de criptografia para garantir que nenhum espião consiga interceptar o processo e, consequentemente, visualizar o que está ali dentro. Para tornar as coisas um pouco mais complicadas, a solução mais interessante é mandar esses dados via headers, para ficar "mais seguro". Como tudo está no mesmo servidor/aplicação, a solução para isso é criar um handler, herdando de DefaultHttpHandler que, por sua vez, fornece uma propriedade chamada ExecuteUrlHeaders que permite adicionar valores nos headers. O código abaixo ilustra essa classe:

public class TransferHandler : DefaultHttpHandler
{
    public TransferHandler() { }

    public override string OverrideExecuteUrlPath()
    {
        base.ExecuteUrlHeaders.Add("AppUserName",
            base.Context.User.Identity.Name);
        return null;
    }
}

O arquivo Web.Config fica:

<httpHandlers>
  <add path="*.asp" type="TransferHandler" verb="GET,HEAD,POST" validate="true"/>
</httpHandlers>

Com isso, todas as requisições para as página *.asp serão interceptadas por esse handler que fará o trabalho de capturar a informação de login, e enviá-la para a página *.asp. Com isso, nas páginas *.asp podemos fazer:

Response.Write Request.ServerVariables("HTTP_APPUSERNAME")

Lembre-se que o ideal é aplicar um algoritmo de criptografia/hash para "salgar" as coisas. O hash, ao meu ver, seria mais interessante, já que voce define uma chave privada (que somente voce conhece) e a utiliza para efetuar o hash do valor. Isole essa classe em um componente e exponha-o via COM, para que voce consiga acessá-lo na aplicação ASP. Neste caso, voce poderá enviar, via headers, o valor puro e também um novo item que é o resultado hash desse valor. Quando chegar na página de destino, voce pega ambos os dados, e valida através do mesmo componente (e mesmo algoritmo), para verificar se o valor não foi alterado durante a sua viagem.

Barrando acesso à elementos/atributos

Recentemente estive envolvido em um projeto simples mas um tanto quanto interessante. Estávamos fazendo a configuração de um servidor Web e todas as aplicações ASP.NET 2.0 que ali seriam hospedadas devem utilizar uma mesma base de dados para as funcionalidades de Membership, Roles e Profile.

A questão é que as aplicações podem ter um arquivo Web.Config e customizar essas configurações em cada uma delas. Só que isso não seria permitido. A solução foi até mais simples do que imaginava e, me forçou a olhar para alguns atributos que nunca prestei a devida atenção até então. Todos os elementos que são colocados nos arquivos de configuração herdam (diretamente ou indiretamente) da classe ConfigurationElement. Essa classe possui 5 principais propriedades (ou atributos):

  • lockAllAttributesExcept: Informa que todos os atributos de um elemento serão bloqueados, com exceção dos atributos que estão contidos nesta lista.
  • lockAllElementsExcept: Informa que todos os elementos de um elemento "pai" serão bloqueados, com exceção dos elementos que estão contidos nesta lista.
  • lockAttributes: Bloqueia apenas os atributos contidos nesta lista.
  • lockElements: Bloqueia apenas os elementos contidos nesta lista.
  • lockItem: Bloqueia o item como um tudo e, conseqüentemente, todos os seus "filhos".

Para que fosse possível isso, editamos o arquivo Machine.Config e lá colocamos todas as configurações necessárias para que as aplicações pudessem herdar as configurações estipuladas para Membership, Roles e Profile e não ter direito de sobrescreve-las. Com isso, o arquivo Machine.Config fica definido como:

<?xml version="1.0">
<configuration>
  <connectionStrings>
    <add
        name="LocalSqlServer"
        connectionString="CONN_STRING"
        providerName="System.Data.SqlClient"
        lockItem="true" />
  </connectionStrings>
  <system.web>
    <membership defaultProvider="AspNetSqlMembershipProvider" lockAttributes="defaultProvider">
      <providers lockElements="clear">
        <add
            name="AspNetSqlMembershipProvider"
            type="System.Web.Security.SqlMembershipProvider, System.Web"
            connectionStringName="LocalSqlServer"
            enablePasswordRetrieval="false"
            enablePasswordReset="true"
            requiresQuestionAndAnswer="true"
            applicationName="/"
            requiresUniqueEmail="false"
            passwordFormat="Hashed"
            maxInvalidPasswordAttempts="5"
            minRequiredPasswordLength="7"
            minRequiredNonalphanumericCharacters="1"
            passwordAttemptWindow="10"
            passwordStrengthRegularExpression=""
            lockAttributes="connectionStringName;enablePasswordRetrieval;passwordFormat" />
      </providers>
    </membership>
  </system.web>
</configuration>

Obviamente que algumas aplicações devem customizar algum desses atributos e, justamente por isso, que nem todos os elementos/atributos foram bloqueados. Por exemplo, se desejar customizar o nome da aplicação no arquivo Web.Config de cada uma das aplicações, então podemos fazer:

<?xml version="1.0"?>
<configuration>
    <system.web>
      <membership userIsOnlineTimeWindow="10">
        <providers>
          <remove name="AspNetSqlMembershipProvider"/>
          <add
            name="AspNetSqlMembershipProvider"
            type="System.Web.Security.SqlMembershipProvider, System.Web"
            applicationName="Teste" />
        </providers>
      </membership>
    </system.web>
</configuration>

Nada impede do desenvolvedor poder adicionar novos providers de memberships, roles e profiles mas, ele não poderá habilitá-lo, já que o atributo defaultProvider está bloqueado a nível superior. Um outro detalhe importante é podemos remover o provider especificado no arquivo Machine.Config através do elemento remove mas, sendo assim, ele não poderá utilizar as funcionalidades de Membership definidas para o servidor.

system.web/pages/namespaces

Há no ASP.NET uma possibilidades de adicionarmos no arquivo Web.Config da aplicação uma seção chamada namespaces, pertencente ao elemento pages. Um código de exemplo do mesmo é mostrado abaixo:

<configuration>
    <system.web>
      <pages>
        <namespaces>
          <add namespace="System.IO"/>
          <add namespace="System.Net.Mail"/>
        </namespaces>
      </pages>
    </system.web>
</configuration>

Várias pessoas acham que os namespaces ali colocados servem como uma "referencia global" para as todas as páginas da aplicação, ou seja, acreditam que poderão utilizar os tipos fornecidos por cada um deles (namespaces) em qualquer página/classe da aplicação.

Só que isso não é verdade. Os namespaces ali declarados são utilizados durante a pré-compilação do site, mais precisamente, quando voce utiliza o arquivo ASPX para colocar código HTML e também o código server-side e, neste caso, ele é serve como uso global. Essa é a forma de centralizar todos os namespaces utilizados pelas páginas. Cada um dos sub-elementos que estão ali correspondem a diretiva @ Import da página ASP.NET.