A importância do StrongName

by Israel Aece 11. May 2010 22:28

Imagine que você precise criar um conjunto de componentes que gerenciam a área de recursos humanos de uma determinada empresa. Entre esses componentes, vamos ter um chamado Salario, que dado o nome do funcionário, valor base do salário e a quantidade de horas extras que o funcionário realizou no mês, retorná o valor líquido que a empresa deverá pagar a ele.

Para ilustrar isso, temos um projeto do tipo Class Library, chamado de RegrasDeNegocio. Dentro deste projeto, criamos uma classe chamada Salario, que possuirá apenas o método chamado de Calcular, que receberá os parâmetros mencionados acima. Traduzindo tudo isso em código, teremos algo como:

public class Salario
{
    public decimal Calcular(string nomeDoFuncionario, decimal valorBase, decimal horasExtras)
    {
        return valorBase + horasExtras;
    }
}

Note que não há nenhuma complexidade envolvida para não desviarmos o foco. Como sabemos, este projeto dará origem à uma DLL, que poderá ser utilizada por várias aplicações que rodam dentro da empresa. Como já era de se esperar, vamos referenciá-la em um projeto chamado AplicacaoDeRH, que utilizará esse componente recém criado para calcular os salários de seus respectivos funcionários. Com isso, o código de consumo na aplicação final é algo parecido com isso:

Console.WriteLine(new Salario().Calcular("Israel Aece", 1000.00M, 250.00M));

Dá mesma forma que antes, vamos manter a simplicidade aqui. Depois deste software (EXE + DLL) instalado na máquina do responsável pelo RH da empresa, eu chego até o diretório físico onde ele está instalado. Copio esses dois arquivos para minha máquina para começar a analisar como esse componente e aplicação foram desenvolvidos. Como sabemos, dentro de qualquer assembly .NET, temos apenas código IL, que é um código ainda decompilável. Podemos utilizar o Reflector para isso e, consequentemente, visualizar tudo o que eles possuem (classes, métodos, parâmetros, etc.).

A partir de agora, vamos explorar a vulnerabilidade. Como eu conheço toda a estrutura que o componente (DLL) tem, nada impede de eu criar um projeto no Visual Studio .NET, com o mesmo nome, com os mesmos tipos e os mesmos parâmetros. Com isso, eu vou manipular o corpo do método Calcular, verificando se o funcionário que está sendo calculado o salário sou eu, e se for, multiplico a quantidade de horas extras por 2, para que eu possa ganhar um valor maior do que realmente deveria.

public class Salario
{
    public decimal Calcular(string nomeDoFuncionario, decimal valorBase, decimal horasExtras)
    {
        if (nomeDoFuncionario == "Israel Aece")
            horasExtras *= 2;

        return valorBase + horasExtras;
    }
}

Depois de compilado, isso dará origem à uma - nova - DLL com o mesmo nome. De alguma forma, eu chego até o computador do responsável pelo RH da empresa, e substituo fisicamente a DLL anterior por essa DLL que acabamos de desenvolver, e que viola a regra de negócio, pois manipula o resultado para beneficiar um funcionário específico. Com isso, quando o responsável pelo RH for calcular o meu salário, ele me pagará duas vezes o valor das minhas horas extras. Se ele confia cegamente no software que dá o resultado, estou sendo beneficiado, a empresa prejudicada e dificilmente alguém encontrará o problema.

A importância do StrongName

Todo e qualquer assembly possui algumas características que ajudam ao runtime determinar a sua identidade. A identidade de qualquer assembly .NET é composta por quatro informações, a saber: nome, versão, cultura e uma chave pública. O nome nada mais é que o nome do assembly, desconsiderando a sua extensão. A versão é o número em que o projeto se encontra, e que por padrão é 1.0. Já a cultura determina se o assembly é sensitivo à alguma cultura e, finalmente, a chave pública, qual falaremos mais adiante.

Podemos utilizar várias ferramentas para conseguir visualizar essas informações. No nosso caso, se abrirmos dentro do Reflector o componente que criamos inicialmente, aquele que possui o código legal, poderemos comprovar essas características que ajudam a identificar o assembly:

RegrasDeNegocio, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

Quando criamos a assembly ilegal, com o código que multiplica a quantidade de horas extras por dois, poderemos notar que o resultado será idêntico, ou seja, não há nada que diferencie a identidade dos dois assemblies. Sendo assim, a substituição física é o suficiente para comprometer a regra de cálculo de salário, já que para a aplicação que a consome, acaba sendo o mesmo componente. Se repararmos melhor, entre as quatro informações que compõem a identidade do assembly, uma delas é chamada de PublicKeyToken, e que está nula (não definida). É justamente nela que está a solução para o nosso problema.

Dentro do .NET Framework existem dois tipos de assemblies: os que são fracamente nomeados e os que são fortemente nomeados. Aqueles que são fracamente nomeados (que é o padrão para todos os projetos .NET), são estruturalmente idênticos, possuindo o mesmo formato e os mesmos tipos definidos, mas como está, dá margem para esse tipo de problema.

Isso acontece porque quando efetuamos a referência do componente RegrasDeNegocio.dll na aplicação AplicacaoDeRH.exe, o .NET injeta no manifesto da AplicacaoDeRH, informações pertinentes ao assembly de regras de negócio. Se analisarmos o manifesto do EXE, veremos a seguinte informação:

Na imagem acima, podemos verificar que dentro da AplicacaoDeRH há uma referência para o componente RegrasDeNegocio, que inclui o nome e a versão dele. Como até então o componente ilegal possui a mesma identidade, a aplicação consome ele sem maiores problemas, não sabendo que se trata de um componente malicioso.

Para resolver o nosso problema, vamos recorrer aos assemblies fortemente nomeados (strong names). A única e principal diferença em relações aos assemblies fracamente nomeados, é a atribuição de uma chave única, que não se repetirá em nenhum lugar do mundo. A Microsoft criou esse recurso, desde a primeira versão do .NET Framework, que utiliza um par de chaves (pública e privada) para garantir a unicidade do assembly. Antes de ver como elas funcionam, vamos primeiramente entender como gerá-la. O .NET Framework fornece uma ferramenta chamada SN.exe, que é responsável por gerar e manipular essas chaves. Para gerar a nossa chave, podemos executar o seguinte comando (através do prompt do Visual Studio .NET):

SN -k MinhasChaves.snk

O arquivo gerado possui as duas chaves, a pública e a privada e você pode nomear o arquivo da forma que quiser. Com o par de chaves gerado, podemos criar um segundo arquivo para acomodar somente a chave pública, mas em um primeiro momento, isso é desnecessário. A ideia aqui é somente visualizarmos o que temos como chave pública:

SN -p MinhasChaves.snk MinhaChavePublica.snk

Para visualizarmos a chave pública, podemos utilizar o seguinte comando:

SN -tp MinhaChavePublica.snk

Public key is
0024000004800000940000000602000000240000525341310004000001000100f1589e575d9c20
cc36a0fb7245d74c8d69ddc26a0c92ebee5e65dba7c94a6583701176cc5a8fd795e11d7e366c49
a19f3ae28509fa8961e6eca103353fe98168a402dc35001b98d9d5325f6121bde11bc698f268a3
e7e338b950b565be26e371c2550dfaee54f9ef8993dc476f60b2ab5ad69d5ae832ddd7e35e43ad
6daafae2

Public key token is 0b8510fcd7fd739a

Só que o arquivo snk por si só não funciona. Você precisa vinculá-lo ao assembly qual deseja assinar. Para isso, você pode abrir o arquivo AssemblyInfo.cs, e adicionar o atributo AssemblyKeyFileAttribute, que recebe o caminho físico até o arquivo que contém o par de chaves, e que no nosso exemplo é MinhasChaves.snk.

[assembly: AssemblyKeyFile(@"C:\MinhasChaves.snk")]

Ao compilar o assembly com a chave vinculada, veremos que a identidade do assembly já mudará. Ao vincular a chave criada acima no nosso componente RegrasDeNegocio, a identidade irá aparecer da seguinte forma:

RegrasDeNegocio, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0b8510fcd7fd739a

A única e essencial diferença é que agora a propriedade PublicKeyToken reflete exatamente a nossa chave pública, que está contida no arquivo MinhasChaves.snk vinculado ao componente. A partir de agora, as aplicações que referenciarem o componente RegrasDeNegocio, guardarão além do nome e versão do mesmo, a chave pública que o identifica. Depois dessas alterações, se visualizarmos o manifesto da aplicação AplicacaoDeRH, teremos o seguinte resultado:

Com isso, qualquer pessoa maliciosa que tente refazer o assembly, por mais que ela se atente a criar toda a estrutura de tipos e métodos, definir o mesmo nome de assembly, e ainda, assinar com um outro strong name, ela jamais conseguirá reproduzir a mesma identidade e, consequentemente, não conseguirá mais alterar o componente que está instalado no cliente. É importante dizer que fisicamente, a substituição ainda poderá ocorrer, mas quando a aplicacação AplicacaoDeRH tentar acessar algum recurso do assembly RegrasDeNegocio, uma exceção será disparada, informando que o assembly solicitado não corresponde aquele que foi inicialmente referenciado.

Observação: Toda essa segurança pode continuar vulnerável se você deixar o arquivo com a chave privada em mãos erradas. Se a pessoa maliciosa conseguir ter acesso a esse arquivo, ela irá gerar o assembly idêntico como ela já fazia, mas ao invés de criar um novo par de chaves para assinar o assembly, ela utilizará o mesmo que você utilizou para assinar o seu, que é o verdadeiro, e com isso todo o problema volta a acontecer.

Para finalizar, vamos entender como todo esse mecanismo funciona e como o runtime do .NET Framework assegura isso. Quando geramos a chave a partir do utilitário SN.exe, um par de chaves é adicionado no arquivo MinhasChaves.snk. Quando compilamos o projeto com esse arquivo vinculado a ele, o .NET gera um hash do componente utilizando o algoritmo SHA1 e assina esse hash com a chave privada. O resultado deste processo é adicionado no próprio assembly, incluindo também a sua chave pública, que está matematicamente relacionada à chave privada. A imagem abaixo ilustra esse processo:

Como vimos acima, quando o componente é referenciado na aplicação que o utiliza, a chave pública também é adicionada à aplicação. Durante a execução, o .NET Framework irá aplicar o mesmo algoritmo de hash no conteúdo do componente (DLL) e dará origem à um novo hash e a chave pública embutida na aplicação que consome aquele componente, será utilizada para extrair o conteúdo (já "hasheado") que está embutido na DLL do componente. Para determinar se a DLL é a mesma ou não, o resultado do hash deve ser igual, do contrário, a DLL foi substituída e, felizmente, uma exceção será disparada, evitando assim de consumir um componente ilegal. A imagem abaixo ilustra esse processo:

Conclusão: Ao assinar uma aplicação/componente com um strong name, podemos tirar proveito de várias funcionalidades, como por exemplo, o refinamento de segurança, a instalação no GAC, que por sua vez possibilita a centralização, execução lado a lado de múltiplas versões de um mesmo componente, etc. Mas um dos principais benefícios fornecidos por ele, é a unicidade do componente, evitando que alguém consiga reproduzí-lo e, consequentemente, colocar em risco a execução e a confiabilidade das aplicações que a consomem.

Tags:

.NET Framework | Security

Movendo tipos entre Assemblies

by Israel Aece 1. October 2009 09:15

Muitas vezes, quando estamos desenvolvendo algum componente, geralmente colocamos todos os tipos (classes, estruturas, interfaces, etc.) dentro do mesmo assembly, sem nos preocuparmos com possíveis reutilizações de tipos que, futuramente, possam acontecer. Uma vez que temos esse componente pronto, o referenciamos em diversas aplicações, que farão uso dos tipos que o mesmo fornece.

Mais tarde, você começa a notar a necessidade de precisar fazer uso destes tipos em outros assemblies que você venha a criar. Sim, referenciar esse primeiro assembly, que contém todos os tipos, resolveria o problema, mas você pode estar referenciando assemblies com tipos que, na verdade, não deveriam estar a disposição de certas aplicações.

Refatorar isso, levando esses tipos para outros assemblies é uma tarefa relativamente fácil, pois basta você tirar de um e colocar noutro. O problema é manter a compatibilidade com as aplicações que já usam esses tipos que serão movidos. Imagine que você tenha um componente chamado Componente1.dll com uma classe chamada DalHelper. Uma aplicação Windows (App.exe) referencia esse assembly e faz uso da classe DalHelper. E como falmos acima, mais tarde você achou necessário mover a essa classe para um outro assembly (Componente2.dll), que ficará mais coerente e facilitará a reutilização.

Se você remover a classe DalHelper, as aplicações que dependem daquela classe dentro do assembly Componente1.dll, irão quebrar. É importante dizer que a idéia aqui é você fazer essa alteração (mover o(s) tipo(s) entre assemblies), sem precisar redistribuir/recompilar as aplicações que já utilizam o Componente1.dll. E para resolver isso, utilizamos o atributo TypeForwardedToAttribute (namespace System.Runtime.CompilerServices), disponível a partir do .NET Framework 2.0. Quando aplicado este atributo em um assembly, ele redirecionará a requisição de um determinado tipo para algum outro lugar, outro assembly. Para ilustrarmos, imagine a seguinte situação:

[Componente1.dll]
namespace Common
{
    public class DalHelper
    {
        public string Execute() { }
    }
}

[App.exe]
Common.DalHelper dal = new Common.DalHelper();
MessageBox.Show(dal.Execute());

O que vamos fazer agora é criar um outro assembly (Componente2.dll), e mover a classe DalHelper para dentro ele, removendo essa classe do Componente1.dll. O código abaixo exibe a estrutura do assembly recém criado.

[Componente2.dll]
namespace Common
{
    public class DalHelper
    {
        public string Execute() { }
    }
}

Isso não basta, e quebrará as referências que existem para essa classe. No Componente1.dll ainda precisamos, explicitamente, direcionar a requisição desta classe, utilizando o atributo TypeForwardedToAttribute. Lembre-se que devemos referenciar o Componente2.dll no Componente1.dll. O código abaixo mostra como utilizar esse atributo, e note que a classe DalHelper que está referenciada está, agora, no Componente2.dll.

[assembly: TypeForwardedTo(typeof(Common.DalHelper))]

Para aquelas aplicações que referenciam o DalHelper do Compenente1.dll, ao enviar o Componente1.dll e Componente2.dll, você pode simplesmente substituir o Componente1.dll que não quebrará o binding, já que quando a requisição para a classe DalHelper chegar à ele, ele sabe onde estará esse tipo, e encaminhará para ele, e como podemos perceber, as aplicações continuam confiando apenas no Componente1.dll.

Podemos perceber que isso ajuda imensamente para refatorar o código e, principalmente, manter a compatibilidade com as aplicações que já fazem uso desses tipos. Novas aplicações que precisarem da classe DalHelper, tudo o que elas precisam fazer é referenciar apenas o assembly Componente2.dll. Mais fácil do que isso é tentar prever essa reusabilidade, criando os tipos separadamente antes mesmo de fazer a distribuição inicial.

Tags: ,

.NET Framework

Embutindo o PIA

by Israel Aece 24. April 2009 22:57

Quando referenciamos um componente COM em um aplicativo .NET, o Visual Studio .NET nos auxilia na criação de um Assembly de Interoperabilidade, conhecido como Primary Interop Assembly, ou somente PIA. Na verdade, a IDE recorre à um utilitário chamado tlbimp.exe, que dado um determinado componente COM, gera uma DLL com toda estrutura (tipos) do componente COM, já “traduzindo” os tipos COM para tipos .NET, facilitando assim o consumo por uma aplicação .NET.

Uma das melhorias que o .NET 4.0 está trazendo é a possibilidade de embutirmos esse Assembly de interoperabilidade no Assembly da aplicação. Para isso, basta ir até as propriedades da referência COM e definir a propriedade Embed Interop Types como True. Isso evita ter um Assembly exclusivo para servir como “ponte” entre a aplicação .NET e o componente COM. Lembro-me de alguns poucos projetos que tivemos que utilizar esse recurso que, como se já não bastasse o overhead que é causado pelo RCW, a distribuição era terrível, já que tínhamos que registrar efetivamente o componente COM no Windows, e instalar o Assembly de interoperabilidade e a aplicação em si.

Além de facilitar a distribuição, esse recurso ainda se preocupa em inserir no Assembly da aplicação somente os tipos necessários que a mesma utiliza, diminuindo consideravelmente o tamanho final da mesma. Podemos notar isso através da imagem abaixo:


PIA

Tags: ,

.NET Framework | Interoperabilidade

ComVisibleAttribute

by Israel Aece 7. July 2008 12:29

Este atributo está presente desde a versão 1.0 do .NET Framework. Ele tem a finalidade de controlar a acessibilidade de um membro ou tipo para o mundo COM. Já falei sobre ele aqui.

Esse atributo recebe em seu construtor um valor booleano indicando tal acessibilidade, com o valor padrão definido como True. Sendo assim, quando este atributo é omitido, assume-se que ele será exposto ao mundo COM. Só que há uma mudança no tipo de projeto Class Library entre a versão 1.x para a versão 2.0, mais precisamente no Visual Studio 2005. Quando criamos um projeto do tipo Class Library agora, esse atributo vem explicitamente definido como False, no arquivo AssemblyInfo.cs, impedindo que qualquer membro dentro deste Assembly seja visível.

Com isso, sempre que precisar criar um Assembly para hospedá-lo no COM+ ou até mesmo para interoperar com o mundo não gerenciado e, estiver criando sob o Visual Studio 2005, atente-se e defina este atributo como True.

Tags: ,

.NET Framework | Interoperabilidade

Por dentro da Base Class Library

by Israel Aece 23. February 2008 01:00

A Base Classe Library (também conhecida como BCL) é um conjunto de classes que o .NET disponibiliza para todas as linguagens que rodam sob o .NET Framework. Essa base encapsula várias funcionalidades que tornam o trabalho dos desenvolvedores muito mais fácil. As classes contidas dentro da BCL é comum para qualquer tipo de aplicação, ou seja, independentemente de tecnologia (ASP.NET, Windows Forns, WPF, etc.), você poderá consumir essas classes que, representam tarefas que são comumente utilizadas. A imagem abaixo exibe onde a BCL está encaixada dentro da plataforma .NET.

Figura 1 - Base Class Library (BCL).

A versão 2.0 adicionou novas tipos e namespaces, enriquecendo ainda mais esta estrutura. Essas classes vão desde novas coleções (tipadas) até novos namespaces, como é o caso do System.Transactions. Apesar do .NET Framework estar em sua versão 3.5, ele utiliza o .NET 2.0 como seu núcleo. A figura abaixo ilustra perfeitamente a posição do .NET 2.0 dentro do .NET 3.X.

Figura 2 - Gráfico que exibe a posição do .NET 2.0.


A BCL é composta por vários namespaces e, através dos capítulos abaixo, veremos detalhadamente cada um dos principais deles. A idéia é abordar o conteúdo mais útil ao dia-à-dia, mostrando exemplos em Visual Basic .NET e Visual C#. Sendo assim, nem todas as classes/funcionalidades serão cobertas aqui mas, para isso, poderá recorrer ao MSDN Library.

ATENÇÃO: Os arquivos estão em formato XPS. Caso não tenha o visualizador, você poderá baixá-lo aqui.

Conteúdo

  • Capítulo 1 – Tipos de dados e Interfaces Este capítulo abordará a arquitetura de tipos fornecido pelo .NET Framework, onde na primeira parte do capítulo, será abordado os tipos padrões padrões e veremos como identificar se trata-se de um tipo valor ou referência. Além disso, analisaremos um problema grave, deixado de lado por muitos desenvolvedores, que é a questão do boxing e unboxing. Ainda nessa primeira parte, analisaremos alguns novos tipos introduzidos nesta versão do .NET Framework, em principal, os Generics. Na segunda e última parte do mesmo, vamos abordar as várias Interfaces que estão disponíveis para serem implementados em tipos customizados, fornecendo funcionalidades adicionais ao tipo criado.

  • Capítulo 2 – Trabalhando com Coleções As coleções são componentes importantes em qualquer tipo de aplicação e, para isso, esse capítulo abordará extensamente o uso das mesmas, começando pelas coleções primárias, fornecidas desde as primeiras versões do .NET Framework até as novas coleções, introduzidas na versão 2.0 do .NET Framework, quais fazem uso dos Generics. Além das coleções, analisaremos as Interfaces disponíveis para podermos estender as funcionalidades existentes e customizarmos para o nosso cenário.
  • Capítulo 3 – Utilização de Assemblies Um Assembly é a menor unidade de reutilização, segurança e controle de versão. O Assembly é algo importante que deve ser analisado cuidadosamente, pois toda aplicação .NET depois de compilada gerará um Assembly. Este capítulo abordará a sua criação desde um utilitário de linha de comando até o Visual Studio .NET. Além disso, abordaremos também outros assuntos relacionados a Assemblies, como por exemplo, strong names, GAC (Global Assembly Cache), instaladores e arquivos de configuração.
  • Capítulo 4 – Monitoramento e depuração de aplicações Toda e qualquer aplicação necessita de algum tipo de monitoramento de seu código para detectar possíveis problemas que possam acontecer e que devem ser analisados. O .NET Framework fornece várias classes que ajudam nesse monitoramento e, este capítulo, é responsável por apresentar essas classes que vão desde a manipulação do Event Log do Windows até classes que interagem com o WMI - Windows Management Instrumentation.
  • Capítulo 5 – Manipulando o sistema de arquivos Grande parte das aplicações comerciais que temos atualmente manipulam arquivos. Esses arquivos são arquivos de bancos, arquivos de parceiros e fornecedores que servem para troca de informações. Enquanto os XML Web Services ainda não são uma realidade para muitas empresas, a manipulação de arquivos e seus respectivos conteúdos é ainda muito utilizado. Tendo esse cenário, o capítulo em questão abordará as principais classes contidas dentro do namespace System.IO para exemplificar e facilitar a manipulação de arquivos do disco e streams de dados.
  • Capítulo 6 – Serialização A serialização de dados é cada dia mais utilizada em aplicações. Por mais que isso aconteça nos bastidores, esse capítulo abordará desde o seu conceito até como implementá-la; e ainda, em seus diversos formatos, utilizando as classes fornecidas pelo .NET Framework 2.0. Além disso, analisaremos classes e Interfaces que temos disponíveis, que proporcionaram o processo de serialização e deserialização mais flexível, onde podemos customizar e interceptar cada um desses processos de acordo com a nossa necessidade.
  • Capítulo 7 – Globalização de Aplicações Cada vez mais se desenvolve softwares que podem ser acessados por várias pessoas de diferentes idiomas e de diferentes locais do mundo. Tendo esse cenário, é importante que a aplicação que estamos desenvolvendo seja possível ao usuário poder customizar o idioma que deseja visualizar os dados e ainda, poder criar a aplicação independente de qualquer cultura. Essa capítulo tem justamente essa finalidade, ou seja, de exibir o que o .NET Framework é capaz de fazer para atender essa necessidade que, torna-se cada vez mais comum.
  • Capítulo 8 – Criptografia Criptografia de dados é um ponto muito importante nos mais diversos tipos de aplicações. Geralmente, em aplicações onde alguns dos dados são muito sigilosos, como é o caso de aplicações financeiras, quais mantém os dados de seus clientes, é necessário que se mantenha esses dados seguros pois, se esses dados cairem em mãos erradas, essas pessoas com más intenções, não consigam entender e/ou recuperar esses dados em sua forma legível. Esse capítulo abordará extensamente as classes responsáveis por criptografia e hashing que o .NET Framework disponiliza, bem como utilizá-las e como aplicá-las ao dia-à-dia.
  • Capítulo 9 – Utilizando Code Access Security – CAS Toda aplicação que utiliza o Common Language Runtime (CLR) obrigatoriamente deve interagir com o sistema de segurança do mesmo. Quando a aplicação é executada, automaticamente é avaliado se ela tem ou não determinados privilégios. Dependendo das permissões que a aplicação tem, ela poderá rodar perfeitamente ou gerar erros relacionados a segurança. Code Access Security (também conhecido como CAS), é um mecanismo que ajuda limitar/conceder o acesso que o código que está querendo realizar, protegendo recursos e operações. Este capítulo abordará como utilizar o CAS, que é fornecido juntamente com o SDK do .NET Framework e, como configurar devidamente a aplicação para evitar problemas relacionados a segurança.
  • Capítulo 10 – Envio de Mensagens (E-mails) Envio de e-mails é muito comum em qualquer tipo de aplicação, seja ela uma aplicação para internet, uma aplicação para Windows ou até mesmo serviços que rodam sem uma intervenção do usuário. O .NET Framework fornece um namespace contendo classes e muitos outros tipos que podemos utilizar nas aplicação para habilitar o envio de e-mails e, conseqüentemente, torná-las muito mais dinâmicas e inteligentes.
  • Capítulo 11 – Criando Serviços do Windows Os Serviços do Windows (Windows Services), permitem-nos criar aplicações que rodam em “background” no sistema operacional. Estes serviços podem ser automaticamente inicializados quando o sistema operacional inicializar, podendo ainda ser pausado e reinicializado, sem apresentar nenhuma interface com o usuário. Esses serviços são ideais para ser usado em servidores ou em funcionalidades de longa duração que necessitem ser executadas de forma totalmente independente, sem a intervenção de um usuário. O capítulo corrente abordará desde a sua criação, depuração e instalação do mesmo.
  • Capítulo 12 – Interoperabilidade com componentes COM A Microsoft criou a plataforma .NET e, em pouco tempo, essa plataforma foi adotada por muitas e muitas empresas. Algo importante é que muitas dessas empresas, já tinham componentes COM que eram utilizados em massa nas aplicações e que são inviáveis para serem reescritos imediatamente. Felizmente a Microsoft pensou no legado e possibilita a interoperabilidade de componentes COM, interagindo com aplicações baseadas na plataforma .NET e vice-versa. Este capítulo mostrará os passos necessários para efetuar essa interoperabilidade entre as novas aplicações e o que já existe em código legado.
  • Capítulo 13 – Reflection Reflection é a habilidade de extrair informações de metadados de um determinado tipo, ou seja, quais parâmetros, métodos, entre outros membros um determinado tipo possui. Isso torna a aplicação bastante flexível, onde podemos extrair informações necessárias para podermos customizar e automatizar a criação de ferramentas e utilitários que auxiliam os próprios desenvolvedores. Além disso, permite a criação em runtime de Assemblies e como instanciar classes via programação. Esse capítulo propõe-se a explicar como criar esse tipo de funcionalidade dentro da aplicação.
  • Capítulo 14 – Threading A criação de threads permitem aumentar consideravelmente a performance das aplicações. Elas fornecem a habilidade de conseguirmos delegar processamentos em diversas unidades de execução, aumentando a capacidade de processamento de uma aplicação. Mas utilizando isso de forma errada, poderá piorar ao invés de melhorar, consumindo mais recursos do que o necessário, tendo um comportamento inesperado e retornando valores diferentes do esperado. O .NET Framework fornece várias classes que podemos utilizar para criação e gerenciamento de threads, bloqueio de recursos em um ambiente multi-threading e sincronização. Este capítulo irá ajudá-lo a conhecer alguns problemas existentes em aplicações que fazem o uso de threads e como contorná-los.

Referências Bibliográficas

  • Code Complete – Second Edition
    Autor: Steve McConnell
    Editora: Microsoft Press
    ISBN: 0-7356-1967-0
  • Writing Secure Code – Second Edition
    Autores: Michael Howard e David LeBlanc
    Editora: Microsoft Press
    ISBN: 0-7356-1722-8
  • CLR via C# – Second Edition
    Autor: Jeffrey Richter
    Editora: Microsoft Press
    ISBN: 0-7356-2163-2
  • Programming Visual C# 2005: The Language
    Autor: Donis Marshall
    Editora Microsoft Press
    ISBN: 0-7356-2181-0
  • Expressões Regulares – Uma abordagem divertida
    Autor: Aurélio Marinho Jargas
    Editora: Novatec
    ISBN: 85-7522-100-0
  • Collection 5160: Core Development with Microsoft .NET Framework 2.0
    Autor/Editor: Microsoft
  • Collection 5161: Advanced Development with Microsoft .NET Framework 2.0
    Autor/Editor: Microsoft

Tags: , , , , , , , ,

.NET Framework | Async | C# | Security | VB.NET

Configurando uma aplicação ASP.NET

by Israel Aece 15. August 2005 15:51

Hoje temos dois arquivos bastante importantes quando trabalhamos com aplicações ASP.NET. São eles: Global.asax e o arquivo Web.Config. Esses arquivos tornam a Aplicação bastante flexível, pois reúnem as configurações e eventos em um único lugar.

Com o arquivo Global.asax, você executa um determinado código quando sua Aplicação é iniciada/finalizada ou até mesmo quando qualquer página da Aplicação for requisitada. Já o arquivo Web.Config, armazena as configurações da Aplicação, tais como: Estado de Sessão, Segurança, Globalização, etc.

Quando se trabalha com o Visual Studio .NET, ao criar uma nova Aplicação do tipo WebApplication, automaticamente ele se encarrega de adicioná-los por padrão è ela.

Figura 1 - Os arquivos Global.asax e Web.Config são criados automaticamente.

O fato de termos várias páginas ASP.NET agrupadas você até pode chamar de Aplicativo, mas isso vai muito além. Um Aplicativo ASP.NET tem além de todas as páginas, handlers de evento, módulos e código executável que é executado dentro de um Diretório Virtual no servidor Web (IIS).

O Diretório \bin

Dentro da raiz do Aplicativo há um diretório chamado "\bin". Dentro dele são armazenados arquivos em formato binário compilados utilizados por seu Aplicativo (um exemplo são os arquivos com extensão *.dll).

Os assemblies colocados aqui estão automaticamente disponíveis para seus arquivos *.aspx, e com isso não precisamos nos preocupar com os procedimentos complexos de criação.

O Arquivo Global.asax

O Diretório Virtual no IIS é grande parte do Aplicativo ASP.NET. Independente da página, o aplicativo é iniciado na primeira vez que ela é solicitada. Enquanto os usuários navegam pelas páginas, o processamento ocorre em segundo plano para tratar do Aplicativo.

O Aplicativo pode cair e ser reiniciado da mesma forma que qualquer Aplicativo tradicional, com a seguinte excessão: enquanto um Aplicativo tradicional é iniciado e executado em um computador desktop permitindo a interação direta com o usuário, um Aplicativo ASP.NET é iniciado e executado em um servidor Web, e o usuário utiliza um browser para acessá-lo.

Lembrando que tudo em .NET Framework é um objeto, temos um objeto chamado HttpApplication, que nos fornece métodos e eventos. Com isso podemos deduzir que sempre que uma página em seu Diretório Virtual for solicitada pela primeira vez, um objeto do tipo HttpApplication é instanciado.

Como as páginas ASP.NET realizamos uma ou mais tarefas individualmente, elas não controlam o Aplicativo de modo geral, não podendo uma página afetar diretamente a outra. Deve existir uma localização central que controla a execução do Aplicativo. Tendo este cenário, entra em cena o arquivo Global.asax.

Conhecido como Arquivo de Aplicação de ASP.NET, o Global.asax permite-nos programar no lugar do objeto HttpApplication e com isso você poderá controlar o seu Aplicativo ASP.NET como faz com qualquer outro objeto por meio de métodos e eventos.

OBS.: Apesar do Visual Studio .NET incluir o arquivo Global.asax por default, ele é totalmente opcional. Se seu Aplicativo não conter um arquivo desse tipo, o programa opera de forma padrão. Desejando adicionar funcionalidades, a sua utilização torna-se essencial.

O arquivo Global.asax é colocado no Diretório raiz do Aplicativo (Exemplo: http://servidor/site/ - c:\inetpub\wwwwroot\site\). O ASP.NET controla o acesso à esse arquivo, de modo que ele não é acessível através do browser, o que garante a segurança. Se alguém tentar executar a URL: "http://www.site.com.br/global.asax" o receberá a seguinte notificação de erro:

Figura 2 - Acessando o arquivo Global.asax através do browser.


Este arquivo é gerenciado pela .NET Framework. Sempre que ele for modificado, o CLR detecta isso e faz com que o Aplicativo seja reiniciado para que as novas mudanças tenham efeito. Isso pode ocasionar transtornos para os usuários finais. Modifique este arquivo somente o necessário.

Programando o arquivo Global.asax

O arquivo Global.asax opera de maneira semelhante as páginas *.aspx. Você utiliza o Global.asax para sincronizar qualquer evento exposto pela classe HttpApplication. Eventos quais veremos abaixo:

Evento Descrição
AcquireRequestState Acionado quando o Aplicativo obtém o cache para a solicitação.
AuthenticateRequest Acionado quando o Aplicativo tenta autenticar a solicitação de HTTP.
AuthorizeRequest Acionado quando o Aplicativo tenta autorizar a solicitação de HTTP.
BeginRequest Acionado quando a solicitação de HTTP é iniciada.
EndRequest Acionado quando a solicitação de HTTP é concluída.
Error Acionado quando surge um erro.
PostRequestHandlerExecute Acionado imediatamente depois do handler de HTTP processar a solicitação.
PreRequestHandlerExecute Acionado imediatamente antes do handler de HTTP processar a solicitação.
PreSenderRequestContent Se a solicitação tiver conteúdo adicional (QueryString, Variáveis de Formulário, etc.), esse evento é acionado imediatamente antes daquele conteúdo ser recebido.
PreSenderRequestHeaders Acionado imediatamente antes de os cabeçalhos de solicitação serem recebidos.
ReleaseRequestState Acionado quando o Aplicativo libera o estado de sessão para a solicitação.
ResolveRequestCache Acionado quando o Aplicativo determina o cache para a solicitação.
UpdateRequestCache Acionado quando o Aplicativo autaliza e libera o cache para a solicitação.

Abaixo o arquivo Global.asax padrão:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 
Imports System.Web
Imports System.Web.SessionState
 
Public Class Global
    Inherits System.Web.HttpApplication
 
    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
        '...
    End Sub
 
    Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
        '...
    End Sub
 
    Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
        '...
    End Sub
 
    Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As EventArgs)
        '...
    End Sub
 
    Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
        '...
    End Sub
 
    Sub Session_End(ByVal sender As Object, ByVal e As EventArgs)
        '...
    End Sub
 
    Sub Application_End(ByVal sender As Object, ByVal e As EventArgs)
        '...
    End Sub
 
End Class
 
Código 1 - O arquivo Global.asax.

Repare que na Linha 5 herdamos a classe HttpApplication dentro do Global.asax, como expliquei acima.

O arquivo mostrado no Código 1 esta apenas com o Eventos que o próprio Visual Studio .NET deixa criado. Você ainda poderá trabalhar com os Eventos mostrados na tabela acima, e como vemos na Figura 3, logo abaixo:

Figura 3 - Os Eventos do arquivo Global.asax.

Agora veremos abaixo qual a ordem de execução dos Eventos do arquivo Global.asax:

1.  Application_Start
2.  Application_BeginRequest
3.  Application_AuthenticateRequest
4.  Application_AuthorizeRequest
5.  Application_ResolveRequestCache
6.  Session_Start
7.  Application_AcquireRequestState
8.  Application_PreRequestHandlerExecute
9.  Page_Load (arquivo *.aspx) ou qualquer outra saída de página
10. Application_PostRequestHandlerExecute
11. Application_ReleaseRequestState
12. Application_UpdateRequestCache
13. Application_EndRequest
14. Application_PreSendRequestHeaders

OBS.: Vale lembrar que alguns eventos são executados de acordo com alguma circunstância. Um exemplo disso é o caso do Evento Session_Start, que somente é executado quando qualquer página é solicitada por aquele usuário, a partir da segunda vez/página, o Evento não ocorre novamente.

Configurando a Aplicação ASP.NET

Da mesma forma que é importante controlar o processamento do Aplicativo, é necessário configurá-lo. Controle de Acesso, Segurança, Estado de Sessão e até mesmo configurações personalizadas. Para isso o ASP.NET nos fornece um arquivo baseado em texto, que nos dá extensibilidade e fácil configuração.

Além disso, a configuração é hierárquica, ou seja, as informações de configuração de aplicativos são aplicadas de acordo com a estrutura de Diretórios Virtuais do seu site. Os sub-diretórios podem herdar ou anular opções de configuração de seus diretórios-pai.

Por padrão, todos os diretório são herdados de um arquivo de configuração padrão de sistema chamado de machine.config, (localizado em: "WinNT\Microsoft.NET\Framework\Versão\CONFIG).

O arquivo que é responsável pela configuração é o Web.Config, que é um arquivo XML, qual veremos à seguir.

O Arquivo Web.Config

Dentro deste arquivo não há nada de especial, à não ser que ele contém chaves e valores que são reconhecidos pelo ASP.NET. Tais valores são facilmente modificáveis, e como mencionei anteriormente, você pode adicionar suas próprias chaves, para controlar outras operações que o ASP.NET não faz para você.

A estrutura básica de um arquivo Web.Config é essa:

1
2
3
4
5
6
7
8
9
10
11
 
<configuration>
    <configSections>
 
    </configSections>
    <system.web>
 
    </system.web>
    <system.net>
 
    </system.net>
</configuration>
 
Código 2 - Estrutura básica do arquivo Web.Config.

Como vemos, é somente XML. Entre as tags <configuration> há dois elementos diferentes: handlers de seção de configuração e configurações da seção de configurações.

As configurações que configuram seu Aplicativo são pares de chave/valor. Há dois tipos destas seções: system.net e system.web. A primeira seção é para configurar o tempo de execução da .NET em si, portanto, não se preocupe muito com isso. Já a segunda é para controlar o ASP.NET. Suas configurações serão colocadas nas tags <system.web>.

OBS.: O arquivo Web.Config faz distinção entre letras maiúsculas e minúsculas, e sem o formato correto, seu Aplicativo poderá gerar erros.

Abaixo uma tabela com as configurações disponíveis para a utilização no arquivo Web.Config:

Seção Descrição
<appSettings> Utilizada para armazenar suas próprias configurações personalizadas de Aplicativo.
<authentication> Configura como o ASP.NET autentica seus usuários.
<authorization> Configura a autorização de recursos no ASP.NET.
<browsercaps> Responsável por controlar as configurações do componente de capacidades do navegador.
<compilation> Responsável por todas as configurações de compilação.
<customErrors> Indica como exibir erros no navegador.
<globalization> Responsável por configurar as opções de globalização.
<httpHandlers> Responsável pelo mapeamento de URLs de entrada em classes IHttpHandler.
<httpModules> Responsável por configurar Módulos de HTTP dentro de um aplicativo.
<identity> Controla como o ASP.NET acessa seus recursos.
<location> Controla como as configurações se aplicam a um diretório.
<pages> Controla configurações de páginas.
<processModel> Configura as configurações de modelo de processo do ASP.NET em Sistemas de Servidor da Web do IIS.
<sessionState> Configura o Estado de Sessão.
<trace> Configura o Trace (Rastreamento).
<webServices> Controla as configurações dos Serviços da Web.

Como podem ver, o arquivo Web.Config ajuda a tornar a Aplicação bastante flexível, ou seja, podemos definir funcionalidades globais em um único lugar. Além disso, uma das vantagens é que se houver a necessidade de mudar algo dentro do arquivo Web.Config, não há a necessidade de recompilar a Aplicação.

Com tantas tags e atributos, que podem nos deixar confusos, infelizmente o Visual Studio .NET em suas versões 2002 e 2003 não implementou o Intellisense em arquivos *.config. Mas há uma forma de obtermos de terceiros tal funcionalidade no seguinte endereço:

http://www.radsoftware.com.au/web/CodeZone/Articles/IntellisenseWebConfig.aspx

Conclusão: Vimos que o arquivo Global.asax nos permite controlar quase todos os aspectos do processamento de uma página ASP.NET. Você pode utilizar os Eventos do objeto HttpApplication para realizar operações imperceptíveis para o usuário, tornando sua Aplicação muito mais robusta e eficiente. Além disso podemos tornar a nossa Aplicação bastante flexível utilizando o arquivo de configuração Web.Config, fazendo com que a mesma possa reagir rapidamente à qualquer mudança.

Tags: ,

ASP.NET

Powered by BlogEngine.NET 1.5.0.0
Theme by Mads Kristensen

Sobre

Meu nome é Israel Aece e sou especialista em tecnologias de desenvolvimento Microsoft, atuando como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. [ Mais ]

Twitter

Host