SandBoxing em ASP.NET

by Israel Aece 21. August 2007 13:55

Como já havia mencionado aqui na seção de segurança, uma boa prática é não permitir que aplicações Web sejam executadas em FullTrust (padrão). Esse nível concede a aplicação Web, acesso a todos os recursos do servidor, tais como, acesso a sistema de arquivos, banco de dados, acesso ao Registry, execução de código não gerenciado, etc.. São permissões que, muitas vezes, não são necessárias e, se concedidas à alguém com más intenções, poderá comprometer a segurança do servidor e da rede.

Para evitar isso, voce pode configurar este nível de segurança tanto no arquivo Web.Config da aplicação ou, se desejar, no arquivo Web.Config que está a nível de servidor. Se optar por alterar o segundo, todas as aplicações ASP.NET que correm naquele servidor, utilizarão esse nível. E ainda, neste segundo (servidor), é importante que voce também altere o atributo allowOverride para false, justamente para que isso não possa ser sobrescrito pelas aplicações. Abaixo um exemplo de como alterar o nível de segurança apenas em uma aplicação:

<configuration>
    <system.web>
      <trust level="Medium" />
    </system.web>
</configuration>

Com isso, voce terá restrições ao rodar o seu código. Um exemplo é com relação ao sistema de arquivos. Com o nível Medium, voce apenas poderá ler e escrever arquivos no diretório da aplicação e nada mais. Se desejar ler ou salvar um arquivo em C:\Temp\ uma exceção será atirada.

Uma vez negado o acesso a todo o sistema de arquivos, pode ser necessário que a aplicação necessite acessar o diretório C:\Temp\. Com essa necessidade, temos duas técnicas que podemos recorrer para conceder o acesso que acabamos de negar. Essas técnicas estão descritas abaixo:

  1. Modificar o arquivo de segurança: essa técnica consiste na alteração do arquivo de configuração que está relacionado com o nível de segurança especificado. Como definimos Medium, as permissões que são concedidas, estão contidas dentro do arquivo web_mediumtrust.config, no diretório de configuração do .NET Framework 2.0.
  2. Utilizar a técnica de SandBoxing: SandBoxing (tema do post) consiste em voce fatorar, ou melhor, isolar o seu código em um outro Assembly, concedendo a este, permissões específicas para o que ele deseja fazer (no nosso caso é ler/salvar arquivos no diretório C:\Temp\).

O isolamento consiste basicamente em gerar uma DLL (projeto Class Library) e assiná-la com um strong-name. Uma vez criada essa DLL voce deve adicioná-la no GAC que, por padrão, Assemblies lá colocados ganham FullTrust. Se o GAC não estiver acessível, então voce deverá colocar o Assembly no diretório \Bin da aplicação e ainda mudar o arquivo de configuração do nível de segurança, concendendo mais privilégios para o Assembly, baseando-se no publicador ou strong-name. É importante dizer que para ambos os casos é necessário que voce tenha privilégios administrativos no servidor.

Ainda há mais um detalhe na criação da DLL. É extremamente necessário que voce defina o atributo AllowPartiallyTrustedCallers (System.Security) no arquivo AssemblyInfo. Esse atributo permitirá que o componente seja chamado através de aplicações (código) parcialmente confiáveis (partial trust).

Finalmente voce executa a aplicação Web, fazendo chamada para este componente e, quando tenta criar um arquivo texto no C:\Temp\, uma exceção do tipo SecurityException é atirada dizendo que voce - ainda - não tem permissões para isso (FileIOPermission). Isso ocorre justamente porque, momentos antes que o componente vai salvar o arquivo no disco, ele verifica as permissões de toda a stack; esse processo é chamado do stack walk e é onde ele percorre todas as chamadas para ver se todo mundo possui a permissão necessária para efetuar a tarefa.

No nosso caso, como adicionamos o componente no GAC, o componente possui a permissão, pois é atribuído a ele FullTrust; mas como o stack walk também verificará a aplicação Web que chama o componente, essa por sua vez, não terá a permissão necessária e, consequentemente, a exceção será atirada. Para permitir que tudo corra bem, então é necessário que voce modifique a stack, aplicando um Assert (cuidado com esse método) a permissão que está sendo requisitada. Esse método apenas verificará se o componente tem permissão, não percorrendo todos os chamadores. Sendo assim, o código do componente tem uma ligeira mudança:

public void WriteFile(string file, string content)
{
    new FileIOPermission(FileIOPermissionAccess.AllAccess, file).Assert();
    using (StreamWriter sw = new StreamWriter(file))
        sw.Write(content);
}

Obviamente que isso é apenas um exemplo de teste. No mundo real, isso é bastante útil, já que muitas pessoas não se preocupam, e sempre deixam a configuração padrão do .NET que, em muitos casos, não é necessária e, neste caso, pode comprometer a segurança do servidor, principalmente, se ele for público.

Tags:

ASP.NET | Security

Performance, Segurança e Boas Práticas

by Israel Aece 21. August 2007 13:55

Recentemente um amigo me procurou para dar algumas dicas se segurança e performance para melhorar de uma aplicação Web. Depois da ajuda, eu decidi disponibilizar essa listagem aqui no blog. Compilei algumas dicas e disponibilizei abaixo essa listagem, com algumas guidelines que julgo necessárias para quando for desenvolver e disponibilizar a aplicação em um servidor Web. É importante dizer que isso pode ou não ser importante, dependendo de qual o cenário da aplicação/empresa.

  • Performance
    • Desabilite a Session nas páginas que você não precisa dela. Isso pode ser realizado através do atributo EnableSessionState da diretiva @Page. Se não for utilizar na aplicação como um tudo, então desabilite a nível de aplicação, através do elemento enableSessionState no elemento do arquivo Web.Config.
    • Sempre defina para false a propriedade EnableViewState para quando não precisar do ViewState na página Web. Através do atributo maxPageStateFieldlength definido dentro do elemento , você tem a possibilidade de "quebrar" o campo __VIEWSTATE. Mas isso não traz melhora em termos de performance. Essa configuração é útil para quando alguns proxies e firewalls barram o acesso a determinadas páginas, quando ela, por sua vez, contém campos ocultos com um grande conteúdo.
    • Defina o atributo debug do elemento compilation para false. Quando definido como true em ambiente de produção, é um dos grandes vilões de performance. Se desejar que todas as aplicações do servidor Web obrigatoriamente estejam com este mesmo comportamento, voce pode definir para true o valor do atributo retail do elemento deployment do arquivo Machine.config do servidor Web.
    • Considere a utilização de OutputCache. Para facilitar a manutenção, utilize o CacheProfile.
    • Remova todos os módulos que não estão sendo utilizados pela aplicação.
    • Reduza a quantidade de round-trips entre o cliente e o servidor. Para isso, utilize sempre que possível o método Server.Transfer.
    • A compactação da resposta a ser enviada para o cliente é sempre uma boa opção. Além disso, remova caracteres desnecessários, como espaços em branco e TABs.
    • Utilize sempre DataReader para ter uma forma eficiente de carga de dados.
    • Paginar os resultados é quase sempre uma necessidade para exibição de dados.
    • Defina para true o atributo enableKernelOutputCache do elemento httpRuntime (acredito que isso já seja o padrão). Isso tem um benefício a nível do IIS, mas atente-se que, em alguns cenários, essa configuração pode trazer resultados inesperados.
    • Remova todos os HTTP Headers desnecessários. Quando você instala o .NET Framework, é criado um valor por padrão dentro do IIS, que é enviado como resposta para todos os clientes que consomem a aplicação. Geralmente é algo como: "X-Powered by ASP.NET". Você pode remover diretamente do IIS ou via atributo enableVersionHeader do elemento httpRuntime.
    • Considere a utilização de páginas assíncronas quando possível.
  • Segurança
    • Toda aplicação Web roda sempre em FullTrust. Considere utilizar Medium (partial trust) já que ela atende a maioria dos casos, inclusive na versão 2.0 do .NET Framework, esse nível já concede acesso ao SqlClientPermission, responsável por gerenciar o acesso à servidores SQL Server. Para alterar esse nível, você pode utilizar o atributo level do elemento trust no arquivo Web.Config da aplicação ou do servidor Web, se desejar que todas as aplicações que estão rodando ali sejam definidas como medium trust.
    • Não permita que outros desenvolvedores sobrescrevam a sua policy. Para isso, defina o atributo allowOverride como false.
    • Sempre crie uma página padrão de erros para que os usuários seja redirecionados para ela quando um problema ocorrer. Para isso, configure a seção do arquivo Web.Config. Neste mesmo local, evite que as exceções sejam enviadas para os usuários, definindo o atributo mode para RemoteOnly. Isso evitará exibir detalhes da exceção que ocorreu, qual pode comprometer a sua aplicação.
    • Todos os parâmetros fornecidos pelo usuário (Forms, QueryStrings, etc.) devem ser validados por tamanho, tipo e formato.
    • Páginas importantes, que podem sofrer alguma espécie de ataque, podemos configurar a sua propriedade ViewStateUserKey para um valor qualquer (Session.SessionID), evitando assim, ataques de "one-click".
    • Nunca armazene informações sigilosas em QueryStrings ou ViewState. Se necessitar armazenar algo importante no ViewState, então criptografe-o. Para isso defina o valor "Auto" para o atributo viewStateEncryptionMode do elemento e invoque o método RegisterRequiresViewStateEncryption na página em que deseja armazenar o conteúdo sigiloso no ViewState.
    • Use SSL somente em páginas que realmente precisam deste nível de segurança.
    • Criptografe a seção de connectionStrings.
    • Criptografe a seção de appSettings quando ele possui informações confidenciais.
    • Quando for conectar com uma base de dados qualquer, utilize um usuário que só tenha permissão necessária para manipular os objetos que estão sendo utilizados pela aplicação, nada mais.
    • Sempre armazena password "hasheados" na sua base de dados. Quando utiliza a arquitetura do Membership, ele já fornece isso intrinsicamente.
    • Defina para true o atributo httpOnlyCookies do elemento httpCookies no arquivo Web.Config. Esse atributo permite ou não que código client-side acessem o cookie.
  • Boas Práticas
    • Sempre extraia strings de arquivos de Resources (*.resx). Mais cedo ou mais tarde, alguém sempre pede para você globalizar a aplicação. :)
    • Defina o nome da aplicação no atributo applicationName quando utilizar o Membership/RoleProvider/Profile.
    • Se estiver utilizando o tratamento global de exceções, habilite o Health Monitoring para logar as exceções e efetuar uma posterior análise.
    • Desabilite o Trace em produção. Quando precisar habilitá-lo para poder monitorar alguma requisição, então define o atributo localOnly como true, para que o output possa ser somente visualizado no próprio computador onde a aplicação está sendo executada.

Bem, de momento o que me veio a cabeça é isso. É muito provável que existe outros detalhes que devo ter esquecido ao colocar aqui.

Tags: , ,

.NET Framework | ASP.NET | Data | Security

Session Timeout

by Israel Aece 21. August 2007 09:11

Uma das grandes dúvidas que vemos por aí é a questão da expiração da Session antes de chegar ao valor de Timeout, definido no elemento sessionState do arquivo de configuração Web.Config.

A questão é que quando essa aplicação corre dentro do IIS 6.0, o Application Pool relacionado ao WebSite/Diretório Virtual pode estar reciclando o worker-process antes de exceder o timeout da Session. Como na maioria das aplicações Web, o conteúdo é sempre armazenado InProc (na memória do servidor onde a aplicação corre), ao reciclar o processo, todas as variáveis de sessão e também o Cache são perdidos.

Caso voce precise manter esse conteúdo em memória por mais tempo (independente da reciclagem), é necessário que voce configure a reciclagem do worker-process diretamente no IIS para um valor mais adequado. Quando a reciclagem é algo inevitável, é necessário recorrer ao armezenamento das variáveis de sessão fora do servidor onde a aplicação está sendo executada, para que ela possa "viver". Pode, por exemplo, utilizar o SQL Server como um servidor de estado.

Tags:

ASP.NET

mscorcfg.msc

by Israel Aece 21. August 2007 09:10

Algumas ferramentas que são parte integrante do SDK do .NET Framework não estão disponíveis quando instalamos a versão do .NET Framework 2.0 (Redistributable Package (x86)), como é o caso do mscorcfg.msc. Essa ferramenta permite configurarmos o Runtime Security Policy (um caspol.exe visual) e também gerenciarmos os Assemblies que estão contidos dentro do GAC.

Apesar de somente a versão 1.x do .NET Framework disponibilizar essa ferramenta quando instalamos o Redistributable Package, há uma forma de habilitarmos na máquina. É importante dizer que quando fazemos essa configuração em uma máquina de desenvolvimento, é permitido que voce crie um arquivo do tipo *.msi, contendo a configuração necessário (de Enterprise, Machine e User) para que voce possa embutir durante a instalação do software nas máquinas clientes que exigem essa configuração de segurança.

Tags: ,

.NET Framework | Security

ASP.NET Internals

by Israel Aece 8. August 2007 10:06

O ASP.NET introduziu uma nova forma de desenvolver aplicações Web, onde temos uma produtividade muito maior em relação ao ASP Clássico e, além disso, uma base sólida e comum de tipos que nos permite desenvolver uma aplicação muito mais robusta e performática. Além disso, o ASP.NET fornece uma flexibilidade muito grande onde, através de alguns objetos, você pode estender ou mudar o comportamento do processamento de uma determinada requisição.

Com toda essa facilidade, alguns pontos ainda são bem obscuros, principalmente no que diz respeito ao processo de recebimento e execução de um pedido. Apesar de não ser uma matéria obrigatória para o desenvolvimento de aplicações ASP.NET, a idéia deste artigo é explorar e entender todo o processamento de uma requisição para um recurso ASP.NET, começando desde o IIS até o processo interno, referente ao runtime do ASP.NET.

Internet Information Services, também conhecido como IIS, é o conjunto de serviços de internet para servidores e sistemas operacionais Windows. Entre os serviços disponibilizados por ele temos: FTP, SMTP, NNTP e HTTP/HTTPS. A sua primeira versão foi lançada e disponibilizada de forma livre como add-on para o Windows NT 3.51. A versão 3.0 introduziu o ASP (Active Server Pages), que é um ambiente para a escrita de páginas dinâmicas, baseada em scripting.

Em 2002 a Microsoft lançou o .NET Framework e com ele o ASP.NET, que é uma nova tecnologia para páginas Web, baseada na plataforma .NET. Em paralelo a Microsoft foi também criando versões do IIS e, atualmente, o IIS evoluiu até a versão 7.0, lançada recentemente. Com isso, o IIS sofreu algumas mudanças em sua arquitetura interna para suportar a execução de páginas ASP.NET. A intenção desta seção é explicar o funcionamento interno do IIS quando uma requisição é recebida. As versões que serão exploradas aqui são: IIS 5.x (Windows XP Professional ou Windows 2000), IIS 6.0 (Windows Server 2003) e IIS 7.0 (Windows Vista Enterprise ou Windows Server 2008).

Quando falamos de recursos estáticos, como é o caso de páginas HTML, imagens, CSS, etc., o IIS recebe a requisição para o mesmo e, se encontrado, recupera o conteúdo do arquivo solicitado e devolve o conteúdo para o cliente que o requisitou via stream de bytes via protocolo HTTP. Agora, quando estamos falando de uma tecnologia server-side, esta por sua vez, tem uma implementação particular no servidor, como é o caso do ASP.NET, que tem um acoplamento muito forte junto ao IIS.

Mas como o IIS consegue determinar o que é conteúdo estático e dinâmico? É neste momento que entra em cena um recurso chamado extensões ISAPI (Internet Server Application Programming Interface). Extensões ISAPI são módulos implementados como DLLs Win32 em que o IIS utiliza para processar determinados recursos. Para cada recurso há um mapeamento, onde você especifica a relação entre cada recurso e um determinado ISAPI. Para efetuar a associação, você especifica a extensão do arquivo e informa qual será o ISAPI correspondente e, quando a requisição para esse recurso (arquivo com a extensão previamente configurada) chegar ao IIS, ele utilizará a extensão ISAPI especificada para aquela extensão de arquivo para efetuar o processamento da requisição.

Através da imagem abaixo podemos analisar todas as extensões de arquivos e suas respectivas extensões ISAPI definidas dentro do IIS:

Figura 1 - Arquivos e suas respectivas extensões ISAPI.

Como podemos notar na imagem acima, quando chegar requisições para páginas *.asp, o ISAPI asp.dll é executado. Quando instalamos o .NET Framework na máquina, algumas configurações são feitas e armazenadas dentro do metabase do IIS. Entre essas configurações, várias extensões de arquivos intrínsicos da plataforma .NET e ASP.NET são mapeadas para o ISAPI aspnet_isapi.dll, que está contido dentro do diretório onde o .NET Framework foi instalado que, na maioria das vezes, é %windir%\Microsoft.NET\Framework\Versao\. Quando requisições chegarem para arquivos com extensão *.aspx, *.asmx, *.asax, *.ashx, *.ascx, etc., elas serão redirecionadas para o ISAPI aspnet_isapi.dll. O ISAPI aspnet_isapi.dll é o ponto de entrada para o processamento de recursos .NET/ASP.NET.

O metabase do IIS é uma espécie de "base de dados" que armazenam as informações de configurações do mesmo em formato XML (IIS 6.0) ou binário (IIS 5.x ou anteriores). Várias informações e configurações do IIS estão dentro de base e também dentro do registry do Windows. Todas as configurações que fazemos através da console do IIS são tipicamente armazenadas neste repositório.

Antes de explorarmos um pouco mais o ISAPI do ASP.NET, precisamos entender o que é um worker process. Basicamente, o worker process trata-se de um pequeno executável que serve de host para o CLR - Common Language Runtime - que se faz necessário, pois uma aplicação ASP.NET necessita do .NET Framework para ser executada. A comunicação e o fluxo de informações entre o worker process e o ISAPI irá depender da versão do IIS que você está utilizando. Analisaremos a seguir como o processamento ocorre para as versões 5.x, 6.0 e 7.0 do IIS.

Process Model - IIS 5.x

Como vimos anteriormente, a extensão ISAPI e o worker process são as principais partes do processamento da requisição. Além deles ainda há um terceiro processo, chamado de inetinfo.exe. Esse, por sua vez, é que carrega e serve de host para o ISAPI do ASP.NET (aspnet_isapi.dll).

Uma vez que a requisição para um recurso .NET/ASP.NET chega, o ISAPI é responsável por invocar o worker process que, nesta versão do IIS, é o aspnet_wp.exe que controlará a execução e fluxo da requisição. Neste caso, haverá uma única instância deste processo para todas as aplicações ASP.NET, e o isolamento delas é feito através de AppDomains, ou seja, para cada diretório virtual um AppDomain é criado dentro do worker process.

AppDomains

Historicamente, um processo é criado para isolar uma aplicação que está sendo executada dentro do mesmo computador. Cada aplicação é carregada dentro de um processo separado, que isola uma aplicação das outras. Esse isolamento é necessário porque os endereços são relativos à um determinado processo.

O código gerenciado passa por um processo de verificação que deve ser executado antes de rodar. Esse processo determina se o código pode acessar endereços de memória inválidos ou executar alguma outra ação que poderia causar alguma falha no processo. O código que passa por essa verificação é chamado de type-safe e permite ao CLR fornecer um grande nível de isolamento, como um processo, só que com muito mais performance.

AppDomain ou domínio de aplicação é um ambiente isolado, seguro e versátil que o CLR pode utilizar para isolar aplicações. Você pode rodar várias aplicações em um único processo Win32 com o mesmo nível de isolamento que existiria em um processo separado para cada uma dessas aplicações, sem ter o overhead que existe quando é necessário fazer uma chamada entre esses processos. Além disso, um AppDomain é seguro, já que o CLR garante que um AppDomain não conseguirá acessar os dados que estão contidos em outro AppDomain. Essa habilidade de poder rodar várias aplicações dentro de um único processo aumenta consideravelmente a escalabilidade do servidor.

Um AppDomain é criado para servir de container para uma aplicação gerenciada. A inicialização de um AppDomain consiste em algumas tarefas, como por exemplo, a criação da memória heap, onde todos os reference-types são alocados e de onde o lixo é coletado e a criação de um pool de threads, que pode ser utilizado por qualquer um dos tipos gerenciados que estão carregados dentro do processo.

O Windows não fornece uma forma de rodar aplicação .NET. Isso é feito a partir de uma CLR Host (que no caso do artigo é o aspnet_wp.exe), que é uma aplicação responsável por carregar o CLR dentro de um processo, criando AppDomains dentro do mesmo e executando o código das aplicações que desenvolvemos dentro destes AppDomains.

Quando uma aplicação é inicializada, um AppDomain é criado para ela. Esse AppDomain também é conhecido como default domain e ele somente será descarregado quando o processo terminar. Sendo assim, o Assembly inicial rodará dentro deste AppDomain e, se desejar, você pode criar novos AppDomains e carregar dentro destes outros Assemblies mas, uma vez carregado, você não pode descarregá-lo e isso somente acontecerá quando a AppDomain for descarregada.

O .NET Framework disponibiliza uma classe chamada AppDomain que representa e permite manipular AppDomains. Essa classe fornece vários métodos (alguns estáticos) que auxiliam desde a criação até o término de um AppDomain. Infelizmente isso está além do escopo do artigo pois, neste caso, o responsável pela criação dos AppDomains é o próprio host. Se desejar saber um pouco mais sobre a criação de domínios de aplicações, por favor, consulte o MSDN Library.


Se analisarmos a Figura 2, podemos notar que o ISAPI e o worker process estão em processos diferentes, e a comunicação entre eles é realizada através de named pipes que, basicamente, é um mecanismo de comunicação entre processos (IPC).

Figura 2 - IIS 5.x Process Model.

Neste modo o processo inetinfo.exe é o responsável por monitorar a porta 80, esperando por requisições de HTTP, enfileirando-as unicamente, onde aguardarão o processamento. Quando essa requisição é para um recurso ASP.NET, o processamento será delegado para o ISAPI correspondente ao ASP.NET (aspnet_isapi.dll); depois disso, ele comunica com o worker process (aspnet_wp.exe) que contém todo o runtime do ASP.NET para que seja possível processar efetivamente o recurso ASP.NET. É dentro deste worker process que todo código gerenciado (.NET) é executado. O processamento daqui em diante será tratado na próxima seção, ainda neste artigo.

É possível customizar o comportamento deste worker process. Essa customização vai desde a parte de segurança até questões de performance. Como falamos anteriormente, somente uma instância do worker process aspnet_wp.exe é criada, mas você pode especificar através do elemento processModel que está contido dentro do arquivo Machine.config alguns limites que, quando atingidos, uma nova instância deste worker process será criada para servir as requisições futuras. É importante dizer que quando uma nova instância é criada, a antiga continuará servindo as requisições pendentes mas não será "extinta", caso utilize as configurações padrões do elemento processModel.

Como acabei de mencionar, quando a requisição é encaminhada para o worker process uma nova etapa é iniciada, que é o pipeline do ASP.NET, ou seja, é quando o código .NET é efetivamente executado. Mas essa parte esta fora do escopo dessa seção e será abordada logo na seqüência.

Process Model - IIS 6.0

Quando utilizamos o IIS 6.0 as regras mudam um pouco, justamente para melhorar a performance e, principalmente, o isolamento das aplicações que correm dentro do mesmo servidor. Como já sabemos, o IIS 6.0 é parte integrante do Windows Server 2003. Essa versão do IIS suporta dois modelos distintos de isolamento e processamento da requisição, sendo: worker process isolation mode (padrão) e IIS 5.0 isolation mode.

Como dito anteriormente, o worker process isolation mode é o modo padrão de isolamento e foi totalmente redesenhado no IIS 6.0. Nesta nova arquitetura, é possível você agrupar aplicações em application pools e assim você poderá customizar o comportamento do worker process para cada um dos application pools. Basicamente, cada application pool tem seu próprio worker process onde, neste caso, o ISAPI e o Runtime do ASP.NET são adicionados dentro do próprio worker process, podendo descartar a utilização do inetinfo.exe para servir de host para o ISAPI e, além disso, não é necessário haver um overhead para a comunicação entre processos, já que ambos estão carregados dentro do mesmo processo. Nesta versão do IIS, o worker process é o w3wp.exe.

Um dos maiores benefícios desta arquitetura é com relação ao isolamento das aplicações. Neste caso, podemos isolar cada aplicação em processos separados e, caso alguma aplicação se comporte de forma indevida, não prejudicará as outras aplicações que, por sua vez, estarão em processos diferentes. É importante dizer também que dentro de um application pool pode haver mais que um site e você pode especificar isso durante a criação do mesmo. Através da imagem abaixo, podemos entender melhor a distribuição destes processos dentro do IIS 6.0:

Figura 3 - IIS 6.0 Process Model.

Na imagem acima podemos reparar um novo integrante, que é o HTTP Protocol Stack, também chamado de http.sys. Este componente é implementado como um kernel-mode device driver e faz parte do subsistema de comunicação do Windows. Quando um novo site é criado, o IIS registra o site junto ao http.sys, que então passa a receber requisições HTTP para este site. Uma vez recebida a requisição para esse site, o http.sys encaminha a requisição para a aplicação correspondente e, depois de processada, devolve a resposta para quem requisitou. Um detalhe importante é a criação do worker process. Há um componente auxiliar que tem exatamente essa responsabilidade, ou seja, criar e gerenciar a vida do worker process do application pool. Esse componente é chamado de WWW Service.

Por questões de compatibilidade, o IIS 6.0 permite a mesma forma de isolamento do IIS 5.x. Isso permitirá rodar aplicações que foram desenhadas para as versões anteriores do IIS. Quando o IIS 6.0 estiver rodando em IIS 5.0 isolation mode o processamento de requisições é identico ao IIS 5.x, e as novas funcionalidades fornecidas pelo IIS 6.0 estarão desabilitadas. Para determinar se este modo de isolamento está ou não habilitado, há um flag dentro do metabase do IIS chamado de IIs5IsolationModeEnabled que deve estar definido como True.

Para finalizar, o processo inetinfo.exe tem uma comportamento diferente dentro do IIS 6.0. Esse comportamento poderá variar de acordo com o modo de isolamento escolhido para o IIS rodar. Se optou por worker process isolation mode, o inetinfo.exe servirá de host para o metabase do IIS e também para servir outros serviços "não Web", como por exemplo, FTP, SMTP e NNTP. Agora, quando o IIS estiver em IIS 5.0 isolation mode, o inetinfo.exe trabalha da mesma forma que no IIS 5.x.

Assim como o IIS 5.x, daqui em diante o processo acontece dentro do runtime do ASP.NET e será explorado na próxima seção deste artigo.

Process Model - IIS 7.0

Como pudemos perceber, nas versões anteriores do IIS, o ASP.NET foi implementado utilizando uma extensão ISAPI, ou seja, através daqueles mapeamentos realizados no IIS, podemos vincular um recurso ASP.NET/.NET a uma determinada extensão ISAPI que, por sua vez, processará a requisição. Agora, a arquitetura de processamento do IIS 7.0 consiste em uma linha de módulos - nativos e gerenciados (escritos utilizando .NET) - que, baseado no recurso solicitado, executam determinadas tarefas durante a requisição e resposta, tarefas que podem ser adicionadas de forma plug-and-play.

Essa arquitetura é muito flexível, pois permite a você utilizar recursos ASP.NET para todo e qualquer tipo de requisição, ou seja, há a possibilidade de utilizar o forms authentication para páginas ASP. Um outro benefício é a questão de duplicação de funcionalidades, por exemplo: quando há uma requisição para algum recurso ASP.NET nas versões anteriores do IIS, a autenticação acontecia no pipeline do IIS e ASP.NET.

É importante dizer que o IIS 7.0 mantém compatibilidade com o IIS 6.0 e permite rodar em classic application pool mode que representa o worker process isolation mode da versão 6.0. Neste caso, a requisição para recursos ASP.NET é passado por alguns passos nativos dentro do IIS e, em seguida, direcionados para o ISAPI aspnet_isapi.dll para executar o código .NET. Como falamos anteriormente, o ponto negativo deste processo é a repetição de alguns passos, como é o caso de autenticação e autorização.

A imagem abaixo ilustra o processamento da requisição dentro do IIS 7.0, seguida da descrição de cada um dos passos que são executados até que o processamento da página seja executado. Podemos reparar que entra em cena um novo componente, chamado de WPAS (Windows Process Activation Service), que será discutido mais adiante, ainda neste artigo.

Figura 4 - IIS 7.0 Process Model.
  • 1 - Quando o cliente efetua uma requisição HTTP, o HTTP.sys intercepta a requisição.
  • 2 - HTTP.sys contata o WPAS para obter as informações do repositório de dados do IIS.
  • 3 - WPAS requisita as informações de configuração do repositório do IIS.
  • 4 - WWW Service recebe as informações de configuração, tais como o application pool e as configurações do site.
  • 5 - WWW Service utiliza essa informação para configurar o HTTP.sys.
  • 6 - WPAS inicia o worker process para o application pool para que a requisição possa ser feita.
  • 7 - O worker process processa e retorna a resposta para o HTTP.sys.
  • 8 - O cliente recebe a resposta.

O trabalho que era realizado pelo serviço W3 Service no IIS 6.0 foi agora dividido em dois: W3Service e WPAS. No IIS 6.0, o W3Service gerenciava a administração e configuração HTTP, gerenciamento do processo e monitoramente de performance. Já na versão 7.0, ele não gerencia mais o worker process. Ao invés disso, o W3Service serve como listerner para HTTP. Com este papel, o W3Service é responsável por configurar e atualizar o HTTP.sys quando mudanças acontecerem e ainda notificar o WPAS quando uma requisição entrar na fila.

O WPAS (Windows Process Activation Service), um novo serviço adicionado nesta versão, gerencia a ativação e o tempo de vida dos worker processes. O WPAS generaliza o modelo de processamento do IIS 6.0, removendo a dependência do protocolo HTTP. Isso permite expor, além de simples páginas ASP.NET, serviços WCF que utilizam ou não o protocolo HTTP, possibilitando assim toda a infraestrutura e utilizando o IIS como hosting para serviços WCF.

Podemos ainda notar na imagem acima um novo elemento chamado applicationHost.config. Este arquivo é baseado em XML (lembram do metabase?), encontra-se no seguinte endereço: %windir%\System32\InetSrv\config e é o arquivo que contém as principais configurações para o IIS 7.0, tais como sites, aplicações, diretórios virtuais, etc.. Este arquivo possui duas grandes seções:

  • system.applicationHost: inclui as informações para o serviço de ativação, como a lista de application pools, logging, listeners e sites. Essas configurações são centralizadas e podem somente existir neste arquivo.

  • system.webServer: esta seção inclui todas as configurações do servidor Web, como por exemplo, a lista de módulos e filtros ISAPI, ASP, CGI, etc.. Essas configurações podem ser definidas (se permitidas) em qualquer arquivo Web.Config de aplicações que rodam no servidor.

Agora que entendendo a parte de processo da requisição no IIS 7.0 de uma forma macro, é importante mostrar como o processo ocorre dentro do worker process. Como foi mencionado acima, o IIS 7.0 é todo modularizado e fortemente integrado com o runtime do ASP.NET. Isso permite a nós desenvolvedores estender funcionalidades do próprio IIS em linguagem .NET ao invés de C++. Como também mencionamos acima, o IIS 7.0 pode rodar de duas formas, sendo: a forma integrada ao ASP.NET e a forma clássica, como o IIS 6.0.

Quando uma requisição ASP.NET chega para uma das versões anteriores do IIS, primeiramente ele é processado pelo IIS e, identificado que existe um ISAPI correspondente para esse recurso, o IIS encaminha a requisição para este ISAPI, que é aspnet_isapi.dll que, por sua vez, hospeda o runtime do ASP.NET para efetivamente processar a página. Quando a requisição trata de um recurso estático, como imagens, arquivos HTML ou até mesmo ASP, o próprio IIS efetua o processamento ou delega o mesmo para extensões ISAPI correspondentes, caso existam. Como já foi dito, a limitação neste caso é que não podemos utilizar recursos fornecidos pelo .NET/ASP.NET para requisições/arquivos "não ASP.NET", além também da duplicação de alguns passos, como pode ser visualizado através da figura abaixo:

Figura 5 - Modelo clássico de requisições no IIS 7.0.

Já no modo integrado, que é a nova forma de processamento do IIS 7.0, quando uma requisição para qualquer recurso chega, ela é processada pelo IIS, com módulos nativos e módulos ASP.NET, sendo capaz assim, de fornecer o processamento em qualquer estágio. Isso possibilita utilizarmos módulos do ASP.NET como o Forms Authentication ou o Output Cache ser usado por outros tipos de requisições, como o ASP 3.0 ou arquivos estáticos.

Isso permite dar uma flexibilidade muito grande, já que permite "plugar" diretamente módulos (escritos em .NET) em diversos estágios do processamento da requisição, rodando antes ou depois das funcionalidades do IIS. Um exemplo disso é a possibilidade de criar uma autenticação customizada, validando as credenciais fornecidas pela autenticação Basic (que somente trabalha com contas no Windows) em uma base de dados SQL Server, utilizando o conceito de membership. A imagem abaixo ilustra esta nova forma de processamento, que é uma característica do IIS 7.0:

Figura 6 - Modelo integrado de requisições no IIS 7.0.

Créditos: É importante dizer que as informações a respeito do IIS 7.0 foram retiradas do site oficial do produto e testadas com o Windows Vista Enterprise. Endereço: http://www.iis.net/default.aspx?tabid=7.

ASP.NET Pipeline

Uma vez que a requisição foi enviada para o servidor, o IIS a captura e inicia o seu processamento até chegar ao ISAPI que tratará a requisição. Os passos detalhados deste processo foram descritos na seção anterior. Quando o CLR é carregado, é o momento de iniciar o processamento do código .NET em si. Esta seção destina-se a entender como o processamento da requisição é feito nos bastidores do ASP.NET, analisando quando e onde os objetos mais comuns são criados, bem como os passos que são executados antes e depois do processamento efetivo da página ASP.NET requisitada.

A ponte entre o processo ISAPI e runtime do ASP.NET é feita através de alguns tipos que estão contidos dentro do namespace System.Web.Hosting. Entre esses tipos, temos a classe ISAPIRuntime que expõe alguns métodos para ser invocados a partir do mundo COM. A instância dessa classe é criada através do método Create da classe AppManagerAppDomainFactory que, por sua vez, gerencia a criação e o fechamento de application domains para a aplicação Web. Uma vez criada a instância da classe ISAPIRuntime, ela disponibiliza um método chamado ProcessRequest que é o responsável por criar, em seu interior, um objeto do tipo HttpWorkerRequest que processará a requisição corrente.

As instâncias do tipo HttpWorkerRequest retornadas pelo método estático CreateWorkerRequest da classe ISAPIWorkerRequest vão depender da versão do IIS em que a aplicação está correndo. Essas instâncias são uma implementação de alto nível, sendo uma espécie de wrapper, responsável por executar grande parte das chamadas APIs nativas, contidas dentro da classe UnsafeNativeMethods.

Ainda dentro do método ProcessRequest da classe ISAPIRuntime, depois do objeto HttpWorkerRequest devidamente criado, ele é passado (indiretamente) para método estático ProcessRequestInternal da classe HttpRuntime. Essa classe fornece uma porção de membros estáticos referentes à aplicação corrente. O método ProcessRequestInternal é responsável por criar uma instância da classe HttpContext que, como sabemos, representa e encapsula todas as informações referentes à requisição HTTP corrente, como os objetos de requisição (HttpRequest) e resposta (HttpResponse). Além disso, ele se encarrega de verificar se a requisição é ou não a primeira e, se for, inicializa várias funcionalidades e verificações, tais como: inicialização do health monitoring e tracing, verificação de permissão de acesso ao diretório temporário do ASP.NET, etc.. Depois disso, através do método estático GetApplicationInstance da classe HttpApplicationFactory, retorna uma instância da classe HttpApplication.

É importante lembrar que o objeto HttpApplication representa o arquivo Global.asax e é neste arquivo que podemos interceptar os passos executados através dos eventos disponibilizados por esta mesma classe. É no momento da criação desta classe que todos os módulos e handlers configurados na aplicação, seja a nível de servidor ou de aplicação, são carregados. Com este objeto devidamente inicializado, ele é retornado para o método ProcessRequestInternal que invoca o seu método BeginProcessRequest. Neste momento, todos os módulos são executados e, entre eles, há um módulo que materializa a requisição para um determinado objeto para, em seguida, instanciá-lo e chamar seus respectivos métodos. A materialização é realizada através de um handler que, através do recurso solicitado e o mapeamento realizado no arquivo de configuração, é criado e, em seguida, efetua o processamento da requisição.

Os módulos são como uma espécie de filtro, permitindo a nós desenvolvedores, anexar uma determinada tarefa ou verificação a qualquer etapa do processamento do pedido, antes ou depois da execução do recurso solicitado. Já os handlers são os componentes que efetivamente executam o código referente ao recurso solicitado, através do mapeamento efetuado no arquivo de configuração. A imagem abaixo ilustra como cada um desses componentes estão definidos dentro do pipeline do ASP.NET.

Figura 7 - Módulos e Handlers durante a requisição.

Cada um dos módulos que visualizamos na imagem acima trata de um módulo já criado pela Microsoft e faz parte do processamento para uma requisição para uma página ASP.NET. Se quisermos traduzir a imagem acima para os módulos e o handler específico do tratamento de uma página ASP.NET, a mesma ficaria da seguinte forma:

Figura 8 - Ordem de processamento dos módulos.

Como podemos notar, quando chega o momento de executar a página o seu respectivo handler (System.Web.UI.PageHandlerFactory) é executado para que o mesmo possa instanciar a classe correspondente a página, deixando-a pronta para que o módulo referente à execução possa processá-la, invocando o método ProcessRequest, que é o ponto inicial da execução de qualquer handler (página).

Conclusão: Com este artigo pudemos analisar e entender o que acontece nos bastidores da execução de uma página ASP.NET. Como eu disse anteriormente, isso não é uma matéria obrigatória para que você possa desenvolver aplicações ASP.NET, mas que, quando você conhece o fluxo, facilmente conseguirá customizar o processamento do mesmo, tendo conhecimento suficiente para deixar somente executar o que realmente importante, tendo um ganho considerável na aplicação, sem a necessidade de executar passos desnecessários.

Tags: ,

ASP.NET

WCF - Consumindo serviços no AJAX

by Israel Aece 8. August 2007 10:05

Uma das grandes partes do .NET Framework 3.0 foi o WCF - Windows Communication Foundation. Quando ele foi lançado, várias formas de acessar os serviços WCF também foram disponibilizadas. Entre as formas, conhecidas como endpoints, podemos citar algumas, tais como: HTTP, TCP e MSMQ. Com a vinda do ASP.NET AJAX, surgiu a necessidade de consumir serviços WCF diretamente dentro deste tipo de aplicação.

Através do Visual Studio .NET 2008 e o .NET Framework 3.5, a Microsoft se preocupou com a necessidade de consumir serviços WCF no AJAX e aproveitou esta oportunidade para criar um binding. Este binding, chamado de WebHttpBinding, trata-se de um novo tipo de binding que permite a criação de um endpoint para ser consumido por aplicações AJAX. É basicamente algo semelhante ao BasicHttpBinding, com a exceção de que os dados são serializados em formato JSON (JavaScript Object Notation). É bom dizer que esta classe está dentro do namespace System.ServiceModel do Assembly System.ServiceModel.Web.dll.

Para possibilitar a chamada de serviços WCF através do AJAX é necessário, primeiramente, a criação da estrutura do serviço, ou seja, a criação da Interface, decoradas com os atributos ServiceContract e OperationContract; além disso, a criação de um arquivo *.svc para que seja possível acessá-lo. Até este momento, não há nada de diferente em relação a criação de um serviço que será exposta via protocolo HTTP, usando como host o IIS. As mudanças começam efetivamente a partir daqui, ou seja, a configuração do endpoint no arquivo Web.Config e do controle ScriptManager.

Como foi mencionado acima, devemos recorrer a utilização do binding webHttpBinding. O único detalhe que você deve se atentar é na configuração do behavior do endpoint, onde devemos informar o elemento enableWebScript para habilitar o acesso via AJAX. O trecho de código abaixo ilustra a configuração do serviço e do endpoint no arquivo Web.Config:

<system.serviceModel>
  <services>
    <service name="Usuario" behaviorConfiguration="SrvBehavior">
      <endpoint
        address="mex"
        binding="mexHttpBinding"
        contract="IMetadataExchange"/>
      <endpoint
        binding="webHttpBinding"
        behaviorConfiguration="EdpBehavior"
        contract="IUsuario"/>
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name="SrvBehavior">
        <serviceMetadata httpGetEnabled="true"/>
        <serviceDebug includeExceptionDetailInFaults="false"/>
      </behavior>
    </serviceBehaviors>
    <endpointBehaviors>
      <behavior name="EdpBehavior">
        <enableWebScript />
      </behavior>
    </endpointBehaviors>
  </behaviors>
</system.serviceModel>

Uma vez configurado o serviço, chega o momento de consumí-lo no cliente, ou melhor, através do código Javascript. O primeiro passo é definir um controle ScriptManager na página onde deseja consumir o serviço; esse controle gerencia as bibliotecas e arquivos de scripts e, entre outras funcionalidades, a geração de proxies para o consumo de serviços via AJAX. Esse controle possui uma coleção de serviços chamada Services, onde cada um dos elementos, do tipo ServiceReference, corresponde a um serviço que será exposto e consumido pelo cliente.

O próximo passo é, através do código Javascript, invocar o código no cliente. Assim como acontecia com o Client-Side Callbacks, você cria uma função que servirá como ponto de entrada e será disparada através de um evento, também client-side; na implementação desta função, você efetivamente invocará o serviço WCF, mas na verdade é método do proxy, que foi criado pelo controle ScriptManager. O proxy cria os métodos que são disponibilizados pelo serviço, mas adiciona mais três parâmetros, que são: o ponteiro para um método de callback que será disparado quando o método retornar; um ponteiro para um método que será disparado caso alguma exceção for disparada quando o método for executado e, finalmente, um objeto que servirá como contexto e será passado para todos os métodos durante a execução. O código abaixo mostra a página ASPX devidamente configurada para invocar o serviço WCF:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
    <script type="text/javascript">
        function ExibirBoasVindas()
        {
            var proxy = new tempuri.org.IUsuario();
            proxy.BoasVindas('Israel', Resultado, null, null);
        }
        
        function Resultado(result)
        {
            alert(result);
        }
    </script>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
            <Services>
                <asp:ServiceReference Path="~/UserService.svc" />
            </Services>
        </asp:ScriptManager>
        <input
            id="Button1"
            type="button"
            value="button"
            onclick="javascript:ExibirBoasVindas();" />
    </form>
</body>
</html>

Como podemos notar, definimos o serviço WCF (arquivo *.svc) no interior do controle ScriptManager que será responsável por criar, em tempo de execução, o proxy para o consumo via AJAX. Já o tempuri.org trata-se do namespace padrão, qual você pode customizar através do da propriedade Namespace do atributo ServiceContract e ainda, se você desejar visualizar o proxy que é criado, pode acessar o serviço WCF da seguinte forma: http://localhost:1029/Projeto/UserService.svc/js, ou seja, adicionar o /js no final do endereço do serviço. Isso permitirá que você efetue o download do proxy que é gerado em tempo de execução.

Importante: Também temos uma nova factory definida dentro do namespace System.ServiceModel.Activation, chamada WebScriptServiceHostFactory que está presente no .NET Framework 3.5. Definindo essa factory no atributo ServiceHost do arquivo *.svc, elimina qualquer configuração no arquivo Web.Config da aplicação, já que ela configura todos os requisitos necessários (behaviors, binding, etc.) para o consumo do serviço via AJAX.

Chamadas entre domínios

É importante dizer que o objeto XMLHttpRequest não permite efetuar chamadas para serviços que estão fora dos limites do servidor local. Sendo assim, você não poderá referenciar serviços que estão em outros domínios ou sub-domínios e mudança de protocolos ou portas. Podemos, através da imagem abaixo, visualizar essas restrições:

Figura 1 - Invocando serviços além dos limites da aplicação.

Como podemos ver, não podemos invocar qualquer serviço que está além da aplicação local mas, se por algum motivo, desejarmos fazer isso, então devemos proceder da mesma forma que vimos no decorrer deste artigo porém, o serviço WCF local que devemos criar, servirá de proxy para o serviço que está em algum outro local.

Conclusão: Os serviços WCF fornecem uma grande flexibilidade em cenários que precisam, de alguma forma, expor informações ou componentes para que terceiros possam consumí-los. Finalmente, com a vinda do Visual Studio .NET 2008 e o .NET Framework 3.5, podemos agora invocar esses serviços através de aplicações AJAX, garantindo assim uma interatividade muito melhor, sem a necessidade de recorrer a postbacks para a execução de recursos como estes.

Tags: ,

ASP.NET | CSD | WCF

RunInUIThread

by Israel Aece 3. August 2007 09:12

Ontem enquanto estávamos falando sobre Multithreading em aplicações Windows Forms durante o curso, lembrei-me de um atributo muito interessante que o Roy Osherove criou.

Como sabemos, quando criarmos uma worker thread, voce não pode manipular qualquer controle que está dentro da aplicação, pois os controles só podem ser acessados através da thread que os criou. Sendo assim, o Roy desenvolveu um atributo chamado RunInUIThreadAttribute, que voce coloca no método que será executado pela thread de background. Automaticamente, ao executar esse método e identificar que ele está decorado com o atributo RunInUIThreadAttribute, automaticamente ele se encarregará de encontrar e processar aquele método através da thread correta.

Tags: ,

Async

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

Indicações

Introdução ao ASP.NET Web API - e-Book