Israel Aece

Software Developer

Security-Transparent Code

Uma das grandes inovações que o .NET trouxe é a capacidade de conceder ou negar acesso à uma determinada aplicação (Assembly), independentemente do usuário que está acessando-a, sendo ou não um “Administrador” da máquina onde a aplicação está sendo executada. Esse novo conceito, presente desde a primeira versão do .NET Framework, é chamada de Code Access Security – CAS.

Esse recurso é extremamente rico em detalhes, onde podemos customizar quais direitos a aplicação terá baseando-se em condições que são avaliadas durante a execução da mesma.  Para compor o CAS, temos vários objetos, tais como: Permissions, Permissions Sets, Security Levels, Membership Conditions, Demands, etc. Ao mesmo tempo que isso é extremamente poderoso, a sua configuração torna-se complexa demais, principalmente quando você não tem um conhecimento intermediário sobre o seu funcionamento.

Um dos principais cenários onde precisamos fazer o uso destas funcionalidades, é quando estamos criando libraries (DLLs) para serem consumidas em ambientes parcialmente confiáveis, ou seja, você cria uma DLL para encapsular o acesso ao banco de dados, sistema de arquivos, etc., e quer consumí-la em uma aplicação que não está sendo executada em “FullTrust” (o que muitas vezes acontece). Nesses casos, se gasta mais tempo testando e configurando as políticas com seus respectivos níveis de segurança, do que na tarefa que a DLL irá executar.

A partir do .NET Framework 2.0, a Microsoft trouxe um recurso chamado de Security-Transparent Code. Essa funcionalidade facilita a escrita e configuração de DLLs que serão consumidas por aplicações que rodam em ambientes parcialmente confiáveis. A ideia desta técnica é você refatorar o código (seja em assemblies, classes ou métodos) em “código transparente” e “código crítico”, com o propósito de separar o código que roda como parte da aplicação do código que roda como parte da infraestrutura, criando uma espécie de barreira entre essas duas seções.

O “código transparente” não fará nada (do ponto de vista da segurança) além do que lhe foi concedido, ou seja, não acessará nenhum tipo de código que exija a elevação dos privilégios atuais. Ao precisar executar uma tarefa que exige um nível de segurança mais elevado, entre em cena o “código crítico”, que rodará em “FullTrust”. Neste caso, o “código transparente” irá invocar algum membro do “código crítico”, que possuirá todos os privilégios necessários, independentemente de quais o cliente possua, evitando elevar os privilégios do “código transparente”.

Os códigos marcados como “transparente” terão algumas restrições, tais como: elevar os privilégios (Asserts) que lhe foram concedidos; efetuar Link Demands (tudo será transformado em “full demand”) e qualquer código unsafe que ele eventualmente execute em “código transparente”, também efetuará uma “full demand”.

Observação: A diferença entre Link Demand e Demand (ou Full Demand) é que a primeira ocorre somente em tempo de compilação (JIT) e apenas irá verificar se o chamador imediato tem a permissão requerida. Já a segunda opção, Demand, executará a stack walk, ou seja, irá percorrer todos os elementos da stack, verificando se todos eles possuem a permissão solicitada.

Depois desta reestruturação (física ou virtual), a CLR irá assegurar (em tempo de execução) que o “código transparente” não poderá efetuar qualquer elevação de privilégios, garantindo que tudo o que o ele tentar executar, exigirá que toda a call stack tenha a respectiva permissão. E, para customizar a comportamento desta funcionalidade, a Microsoft disponibiliza alguns atributos:

  • SecurityTransparentAttribute: Somente permitido em nível de Assembly, diz ao runtime que ele será “transparente”, ou seja, não poderá executar qualquer ação que exija a elevação de privilégios. É importante dizer que “código transparente” não pode invocar “código crítico”.
  • SecurityCriticalAttribute: Quando utilizado em nível de Assembly, indica que ele pode ter “código crítico”. Isso vai depender da opção que você define ao utilizar o enumerador SecurityCriticalScope. A opção Everything indica que todo o código será considerado “código crítico”, enquanto a opção Explicit, determina que somente o membro onde o atributo está sendo aplicado será considerado como “código crítico”.
  • SecurityTreatAsSafeAttribute: Indica que o membro onde este atributo está sendo aplicado, pode ser acessado por outros membros que estão marcados com os atributos SecurityTransparentAttribute ou AllowPartiallyTrustedCallersAttribute. Não há nenhuma validação que evite a compilação quando você aplica este atributo em métodos públicos, e se você fizer isso, estes membros podem ser acessados através do “código transparente” tendo ter possíveis vulnerabilidades.

No exemplo abaixo, estamos definindo o atributo SecurityCriticalAttribute em nível de Assembly, dizendo que ele poderá conter “código crítico”. Com isso, somos obrigados a determinar qual método será “crítico”, utilizando o atributo SecurityCriticalAttribute ou o SecurityTreatAsSafeAttribute. Neste caso, optamos pela segunda opção, pois desejarmos que o método ReadFile possa ser invocado a partir de membro internos bem como externos, e com a configuração abaixo, será considerado “transparente”, e sem este atributo não seria possível.

[assembly: SecurityCritical]

static void Main(string[] args)
{
    Console.WriteLine(ReadFile());
}

[SecurityCritical]
[SecurityTreatAsSafeAttribute] 
private static string ReadFile(string filename)
{
    new FileIOPermission(FileIOPermissionAccess.Read, filename).Assert();
    //Ler o arquivo
}

Strong Name Bypass

Um dos passos necessários durante a carga do Assembly é análise da assinatura do Strong Name. Independentemente do Code Access Security, essa análise sempre é realizada e muitas aplicações pagam esse preço sem ao menos utilizá-lo. A partir do SP1 do .NET Framework 3.5, a Microsoft incluiu uma funcionalidade chamada de Strong Name Bypass, não mais fazendo essa verificação e, consequentemente, tendo um ganho de performance durante a sua inicialização.

Essa análise não será mais realizada para Assemblies que são carregados em um ambiente full-trusted. Obviamente que essa análise acontecerá quando, para computar a segurança, voce levar em consideração o Strong Name. Se, por algum motivo, voce quiser reabilitar a verificação, então pode recorrer ao elemento bypassTrustedAppStrongNames, definindo o atributo enabled para false, assim como é mostrado abaixo, utilizando o arquivo de configuração:

<configuration>
    <runtime>
        <bypassTrustedAppStrongNames enabled="false"/>
    </runtime>
</configuration>

CAS != Windows Security

Uma das funcionalidades mais interessantes do .NET Framework ao meu ver é o CAS - Code Access Security. Toda aplicação que roda sobre a plataforma .NET pode interagir de forma direta ou indireta com o CAS e, com isso demandar os privilégios necessários para que ela possa ser executada.

Quando a aplicação é inicializada, a mesma é carregada para dentro de um processo, que é chamado também de host. Esse host extrai e examina a evidência do Assembly, podendo diferentes informações serem coletadas de acordo com a origem do mesmo. A evidência consiste no strong-name, zona e publicador do Assembly. Depois de capturada, essa evidência é passada para o sistema de segurança do .NET Framework (CAS) para que o mesmo avalie e conceda as devidas permissões para o Assembly.
 
A partir deste momento, baseando-se na evidência extraída do Assembly, o runtime security policy começará a avaliar a “árvore” de code groups. Se a condição for atendida, dizemos que o Assembly é membro do code group e, além disso, o conjunto de permissões (permission set) vinculadas a essa condição será concedido ao Assembly. Caso contrário, possíveis code groups que estiverem abaixo da condição não atendida, não serão avaliados e, obviamente, não serão concedidos. Depois deste processo, a união dos conjuntos de permissões é computada e esse processo é repetido para cada nível das políticas de segurança (policy levels) que são: User, Machine e Enterprise. Finalmente, depois de todos os níveis avaliados, o Assembly receberá a interseção de todos os grupos de permissões entre os níveis, e o Assembly receberá o que chamamos de Final Permission Grant (FG).

Essas permissões são definidas a partir do utilitário caspol.exe para aplicações Windows ou, para aplicações ASP.NET, através dos arquivos *.config que estão localizados no diretório do .NET Framework. Independentemente do tipo de aplicação, mesmo que ela ganhe permissão para acesso ao sistema de arquivos, isso não quer dizer que a aplicação irá executar uma operação (de escrita ou leitura) com sucesso.

A questão é que o CAS garante as permissões que o código tem sobre o sistema operacional. Quando executamos uma aplicação ela está sempre associada à uma identidade dentro do sistema operacional. Em aplicações Windows o padrão é rodar com as credenciais do usuário corrente que está logado no sistema operacional; já em aplicações ASP.NET, o que prevalece é o usuário do IIS, mais precisamente, a identidade definida na criação do worker process. Com esse comportamento, de nada adianta a aplicação receber o FileIOPermission se o usuário que está associado à execução naquele momento não possuir os devidos privilégios de acesso dentro do Windows.

requirePermission

As seções connectionStrings e appSettings são as únicas que podemos acessar diretamente no código sem nenhum tipo de problema, mesmo quando a aplicação está sendo executada em um ambiente parcialmente confiável. Por parcialmente confiável em uma uma aplicação ASP.NET, entenda como o trust level esteja definido como "Medium" ou algo abaixo disso:

<trust level="Medium"/>

Neste caso, qualquer outra seção (authentication, smtp, globalization, etc.) que tentarmos acessar em um neste ambiente, uma exceção do tipo SecurityException será lançada informando que não possuímos a permissão necessária que, neste caso, é ConfigurationPermission. Note que só o fato de acessar a seção já provoca o disparo da exceção:

AuthenticationSection section =
    (AuthenticationSection)WebConfigurationManager.GetSection("system.web/authentication");
Response.Write(section.Mode.ToString());

Quem determina essa restrição é o atributo requirePermission que é configurado no registro da seção dentro do arquivo de configuração. Por padrão, ele é definido como True e, como dito acima, somente as seções connectionStrings e appSettings são definidas como False, como se pode notar no arquivo machine.config. Somente altere a configuração padrão caso voce realmente saiba o que está fazendo e, principalmente, conhecendo as implicações que isso pode causar. Além disso, é importante dizer que voce também pode determinar essa restrição quando voce cria uma seção de configuração customizada.

WCF - Partial Trust

Uma das configurações mais importantes que se deve realizar em uma aplicação é a segurança, mais precisamente, que tipo de permissão devemos conceder à mesma para que ela possa ser executada. O tipo de permissão que me refiro aqui não tem a ver com a identidade do usuário, mas sim as permissões que o código (Assembly) tem para acessar recursos da máquina onde o mesmo está sendo executado.

Esse tipo de configuração foi introduzida desde a primeira versão do .NET Framework e é chamada de Code Access Security - CAS. Baseada no conceito de permissões, onde cada uma delas mapeia para um recurso (Message Queue, File System, SQL Server, etc.) da máquina, são através destas permissões que dizemos ao Runtime Security Policy (de forma declarativa ou imperativa), quais direitos nosso código tem de executar, ou melhor, a quais recursos ele terá acesso.

Se você cria qualquer aplicação .NET e não altera nenhuma configuração de segurança, ele será executado em Full Trust, ou seja, terá acesso irrestrito a qualquer recurso da máquina onde está atualmente sendo executado. Apesar disso ser muito comum, não é ideal. Uma premissa básica na segurança é jamais conceder direitos desnecessários a um código para desempenhar suas funções. Quando você opta por mudar a segurança padrão imposta pelo .NET, quer dizer que você está executando a aplicação em Partial Trust, ou seja, em um ambiente em que você não terá acesso a todos os recursos, mas sim, o essencial para a aplicação poder trabalhar.

Na primeira versão do WCF (Windows Communication Foundation) - .NET Framework 3.0 - ele não era suportado em ambientes que estavam sob Partial Trust, o que obrigava muitos clientes a conceder mais direitos do que o necessário para poder executar/invocar um serviço escrito em WCF. Depois de muitas requisições, a Microsoft decidiu afrouxar essa segurança com o lançamento do .NET Framework 3.5, permitindo (com várias restrições) que serviços sejam invocados a partir de um ambiente parcialmente confiável. Isso obrigou a Microsoft a decorar os assemblies System.ServiceModel.dll e System.ServiceModel.Web.dll com o atributo AllowPartiallyTrustedCallers que, como o nome diz, permite que o assembly seja invocado por aplicações que estão sendo executadas em um ambiente mais restrito.

Entre as limitações que o WCF 3.5 impõe, temos: permite somente os bindings BasicHttpBinding, WsHttpBinding (sem segurança ou com segurança a nível de transporte) e WebHttpBinding serem invocados a partir de uma aplicação que está sendo executada em ambiente parcialmente confiável; além disso, serviços WCF que estão rodando em aplicações de middle tier também podem invocar requisições para outros serviços, desde que tenha a sua respectiva WebPermisson, que concede direito de acesso a esse recurso. Todos os outros bindings, não HTTP, demandarão full trust de seus chamadores, obrigando-nos a conceder ao cliente mais direitos do que ele deveria ter para poder trabalhar. Para uma lista completa de todas as restrições, você pode consultar este link.

Mas e quando necessitamos referenciar em nossa aplicação parcialmente confiável, um serviço WCF que expõe apenas um endpoint com o binding definido como NetTcpBinding? Uma alternativa interessante é criar um proxy, isolando toda a chamada para este serviço. E no que consiste um proxy? Basicamente trata-se de uma DLL que conterá uma classe que servirá como um wrapper para a interface do serviço a ser acessado. Quando referenciamos o serviço diretamente na aplicação, automaticamente a IDE do Visual Studio se encarrega de criar a classe (também denominada proxy) para que possamos invocar os respectivos métodos do serviço localmente e que, durante a execução, será delegado ao serviço.

Para o nosso cenário isso não será a melhor saída. O que devemos fazer é utilizar o utilitário svcutil.exe que, entre suas utilidades, é capaz de gerar uma classe baseado no WSDL extraído do mesmo. A linha abaixo ilustra como devemos passar os parâmetros para o svcutil.exe para que o mesmo possa gerar a classe correspondente (executando a partir do prompt do Visual Studio .NET):

C:\>svcutil net.tcp://localhost:9000/Mex /out:C:\Proxy.cs

Uma vez que a classe é criada, criaremos um projeto do tipo Class Library e adicionaremos a classe recém criada, Proxy.cs, dentro deste projeto. Além disso, é também necessário fazer a referência para o assembly System.ServiceModel.dll. Depois deste processo, precisamos ainda fazer algumas configurações em nível de assembly; como essa DLL deverá ser consumida por um projeto que está sendo executado em um ambiente parcialmente confiável, adicionar no arquivo AssemblyInfo.cs o atributo AllowPartiallyTrustedCallers; finalmente, será necessário definir um Strong Name para essa DLL (veremos mais tarde, ainda neste artigo o motivo disso). O trecho de código abaixo ilustra como devemos proceder para definir o atributo APTCA:

using System.Security;

[assembly: AllowPartiallyTrustedCallers]

Para finalizar, é necessário abrir o arquivo Proxy.cs e, no ínicio da classe que representa o serviço do lado do cliente (ClientBase), e especificar o atributo PermissionSetAttribute, assim como é mostrado abaixo:

using System.Security;
using System.Security.Permissions;

[PermissionSetAttribute(SecurityAction.Assert, Name = "FullTrust")]
public partial class Service1Client : System.ServiceModel.ClientBase<IService1>, IService1
{
    //Implementação
}

O atributo PermissionSetAttribute irá permitir a definição de uma determinada ação para uma permission set (conjunto de permissões) específica. No exemplo acima, estamos especificando a ação Assert (via enumerador SecurityAction) sob a permission set predefinida, que é a FullTrust. A utilização da ação/método Assert deve ser analisada com muito cuidado, pois é o mesmo que "Eu sei o que estou fazendo; confie em mim!", e isso garantirá que a permissão seja concedida ao chamador mesmo que ele não tenha privilégio para isso.

Finalmente, a DLL está pronta para ser utilizada mas se, neste momento, a mesma for referenciada na aplicação que está sendo executada em ambiente parcialmente confiável, uma exceção será atirada, dizendo que não é possível consumir um serviço via TCP nesta aplicação. Neste caso, necessitamos adicionar essa DLL no GAC, pois os componentes que lá residem já ganham Full Trust. É importante dizer que, mesmo assim, é necessário o código acima, pois o stack walk também verificará a aplicação Web que chama o componente e essa, por sua vez, não terá a permissão necessária e, consequentemente, a exceção será atirada. Esse conceito que utilizamos aqui também é conhecido como Sandboxing.

Importante: Apesar desta técnica funcionar, ela tem um ponto negativo: suprimindo a security demand, que é o processo de avaliação dos chamadores para se certificar que todos possuem tal permissão, permitirá que qualquer cliente que execute sob ambiente parcialmente confiável chame qualquer serviço WCF. Isso permitirá a um cliente que não tenha permissão de acesso a algum recurso, como acesso TCP, aplicar um bypass nesta limitação. A solução para esse problema é criar uma classe que herde diretamente da classe base ClientBase, concedendo as permissões específicas, dependendo do tipo de binding.

Conclusão: Felizmente o WCF permite na versão 3.5 do .NET Framework o consumo de serviços em ambiente parcialmente confiáveis. Alguns bindings podem ser consumidos sem a necessidade de alguma configuração adicional, pois são acessíveis em ambientes parcialmente confiáveis; já outros bindings não podem ser acessados diretamente, e necessitam de um passo adicional, que é a criação do proxy, explicado neste artigo.

Por dentro da Base Class Library

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

SandBoxing em ASP.NET

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.