Segurança e ADO.NET de acesso ao código

O .NET Framework oferece segurança baseada em função, bem como CAS (segurança de acesso a código), ambos implementados usando uma infraestrutura comum fornecida pelo Common Language Runtime (CLR). No mundo do código não gerenciado, a maioria dos aplicativos é executada com as permissões do usuário ou principal. Como resultado, os sistemas de computador podem ser danificados e os dados privados comprometidos quando software malicioso ou cheio de erros é executado por um usuário com privilégios elevados.

Por outro lado, o código gerenciado executado no .NET Framework inclui segurança de acesso ao código, que se aplica apenas ao código. Se o código pode ser executado ou não, depende da origem do código ou de outros aspetos da identidade do código, não apenas da identidade do principal. Isso reduz a probabilidade de que o código gerenciado possa ser usado indevidamente.

Nota

O CAS (Code Access Security) foi preterido em todas as versões do .NET Framework e do .NET. As versões recentes do .NET não respeitam as anotações do CAS e produzem erros se as APIs relacionadas ao CAS forem usadas. Os desenvolvedores devem procurar meios alternativos de realizar tarefas de segurança.

Permissões de acesso ao código

Quando o código é executado, ele apresenta evidências que são avaliadas pelo sistema de segurança CLR. Normalmente, essa evidência compreende a origem do código, incluindo URL, site e zona, e assinaturas digitais que garantem a identidade do assembly.

O CLR permite que o código execute apenas as operações que o código tem permissão para executar. O código pode solicitar permissões, e essas solicitações são atendidas com base na política de segurança definida por um administrador.

Nota

O código em execução no CLR não pode conceder permissões a si mesmo. Por exemplo, o código pode solicitar e receber menos permissões do que uma política de segurança permite, mas nunca receberá mais permissões. Ao conceder permissões, comece sem permissões e, em seguida, adicione as permissões mais restritas para a tarefa específica que está sendo executada. Começar com todas as permissões e, em seguida, negar as individuais leva a aplicativos inseguros que podem conter falhas de segurança não intencionais de conceder mais permissões do que o necessário. Para obter mais informações, consulte Configurando a diretiva de segurança e o gerenciamento de diretivas de segurança.

Existem três tipos de permissões de acesso ao código:

  • Code access permissions derivam da CodeAccessPermission classe. As permissões são necessárias para acessar recursos protegidos, como arquivos e variáveis de ambiente, e para executar operações protegidas, como acessar código não gerenciado.
  • Identity permissions representam características que identificam uma montagem. As permissões são concedidas a um assembly com base em evidências, que podem incluir itens como uma assinatura digital ou a origem do código. As permissões de identidade também derivam da CodeAccessPermission classe base.
  • Role-based security permissions baseiam-se no facto de uma entidade de segurança ter uma identidade especificada ou ser membro de uma função especificada. A PrincipalPermission classe permite verificações de permissão declarativas e imperativas em relação ao principal ativo.

Para determinar se o código está autorizado a acessar um recurso ou executar uma operação, o sistema de segurança do tempo de execução percorre a pilha de chamadas, comparando as permissões concedidas de cada chamador com a permissão que está sendo exigida. Se algum chamador na pilha de chamadas não tiver a permissão exigida, um será lançado e o SecurityException acesso será recusado.

Pedir permissões

O objetivo de solicitar permissões é informar ao tempo de execução quais permissões seu aplicativo requer para ser executado e garantir que ele receba apenas as permissões de que realmente precisa. Por exemplo, se o seu aplicativo precisa gravar dados no disco local, ele requer FileIOPermissiono . Se essa permissão não tiver sido concedida, o aplicativo falhará quando tentar gravar no disco. No entanto, se o aplicativo solicitar FileIOPermission e essa permissão não tiver sido concedida, o aplicativo gerará a exceção no início e não será carregado.

Em um cenário em que o aplicativo só precisa ler dados do disco, você pode solicitar que ele nunca receba permissões de gravação. No caso de um bug ou um ataque malicioso, o seu código não pode danificar os dados nos quais opera. Para obter mais informações, consulte Solicitando permissões.

Segurança baseada em funções e CAS

A implementação da segurança baseada em função e da segurança acessada por código (CAS) melhora a segurança geral do seu aplicativo. A segurança baseada em função pode ser baseada em uma conta do Windows ou em uma identidade personalizada, disponibilizando informações sobre a entidade de segurança para o thread atual. Além disso, os aplicativos geralmente são obrigados a fornecer acesso a dados ou recursos com base em credenciais fornecidas pelo usuário. Normalmente, esses aplicativos verificam a função de um usuário e fornecem acesso a recursos com base nessas funções.

A segurança baseada em função permite que um componente identifique os usuários atuais e suas funções associadas em tempo de execução. Essas informações são mapeadas usando uma política CAS para determinar o conjunto de permissões concedidas em tempo de execução. Para um domínio de aplicativo especificado, o host pode alterar a diretiva de segurança baseada em função padrão e definir uma entidade de segurança padrão que representa um usuário e as funções associadas a esse usuário.

O CLR usa permissões para implementar seu mecanismo de imposição de restrições no código gerenciado. As permissões de segurança baseadas em função fornecem um mecanismo para descobrir se um usuário (ou o agente agindo em nome do usuário) tem uma identidade específica ou é membro de uma função especificada. Para obter mais informações, consulte Permissões de segurança.

Dependendo do tipo de aplicativo que você está criando, você também deve considerar a implementação de permissões baseadas em função no banco de dados. Para obter mais informações sobre segurança baseada em função no SQL Server, consulte Segurança do SQL Server.

Assemblagens

Os assemblies formam a unidade fundamental de implantação, controle de versão, reutilização, escopo de ativação e permissões de segurança para um aplicativo .NET Framework. Um assembly fornece uma coleção de tipos e recursos que são criados para trabalhar juntos e formar uma unidade lógica de funcionalidade. Para o CLR, um tipo não existe fora do contexto de um assembly. Para obter mais informações sobre como criar e implantar assemblies, consulte Programando com assemblies.

Assembléias com nomes fortes

Um nome forte, ou assinatura digital, consiste na identidade do assembly, que inclui seu nome de texto simples, número de versão e informações de cultura (se fornecido), além de uma chave pública e uma assinatura digital. A assinatura digital é gerada a partir de um arquivo assembly usando a chave privada correspondente. O arquivo assembly contém o manifesto assembly, que contém os nomes e hashes de todos os arquivos que compõem o assembly.

A nomenclatura forte de um assembly dá a um aplicativo ou componente uma identidade exclusiva que outro software pode usar para se referir explicitamente a ele. Uma nomenclatura forte protege os assemblies contra serem falsificados por um assembly que contém código hostil. A nomenclatura forte também garante a consistência de controle de versão entre diferentes versões de um componente. Você deve nomear fortemente os assemblies que serão implantados no GAC (Global Assembly Cache). Para obter mais informações, consulte Criando e usando assemblies de nome forte.

Confiança parcial no ADO.NET 2.0

No ADO.NET 2.0, o Provedor de Dados do .NET Framework para SQL Server, o Provedor de Dados do .NET Framework para OLE DB, o Provedor de Dados do .NET Framework para ODBC e o Provedor de Dados do .NET Framework para Oracle agora podem ser executados em ambientes parcialmente confiáveis. Em versões anteriores do .NET Framework, só System.Data.SqlClient era suportado em aplicativos de confiança total.

No mínimo, um aplicativo parcialmente confiável usando o provedor do SQL Server deve ter execução e SqlClientPermission permissões.

Propriedades do atributo de permissão para confiança parcial

Para cenários de confiança parcial, você pode usar SqlClientPermissionAttribute membros para restringir ainda mais os recursos disponíveis para o Provedor de Dados do .NET Framework para SQL Server.

A tabela a seguir lista as propriedades disponíveis SqlClientPermissionAttribute e suas descrições:

Propriedade do atributo de permissão Description
Action Obtém ou define uma ação de segurança. Herdado de SecurityAttribute.
AllowBlankPassword Habilita ou desabilita o uso de uma senha em branco em uma cadeia de conexão. Os valores válidos são true (para permitir o uso de senhas em branco) e false (para desabilitar o uso de senhas em branco). Herdado de DBDataPermissionAttribute.
ConnectionString Identifica uma cadeia de conexão permitida. Várias cadeias de conexão podem ser identificadas. Nota: Não inclua um ID de utilizador ou palavra-passe na cadeia de ligação. Nesta versão, você não pode alterar as restrições de cadeia de conexão usando a Ferramenta de Configuração do .NET Framework.

Herdado de DBDataPermissionAttribute.
KeyRestrictions Identifica os parâmetros da cadeia de conexão permitidos ou não permitidos. Os parâmetros da cadeia de conexão são identificados no parâmetro name> do formulário<=. Vários parâmetros podem ser especificados, delimitados usando um ponto-e-vírgula (;). Nota: Se você não especificar KeyRestrictions, mas definir KeyRestrictionBehavior a propriedade como AllowOnly ou PreventUsage, nenhum parâmetro de cadeia de conexão adicional será permitido. Herdado de DBDataPermissionAttribute.
KeyRestrictionBehavior Identifica os parâmetros da cadeia de conexão como os únicos parâmetros adicionais permitidos (AllowOnly), ou identifica os parâmetros adicionais que não são permitidos (PreventUsage). AllowOnly é a predefinição. Herdado de DBDataPermissionAttribute.
TypeID Obtém um identificador exclusivo para esse atributo quando implementado em uma classe derivada. Herdado de Attribute.
Unrestricted Indica se a permissão irrestrita para o recurso é declarada. Herdado de SecurityAttribute.

Sintaxe ConnectionString

O exemplo a seguir demonstra como usar o connectionStrings elemento de um arquivo de configuração para permitir que apenas uma cadeia de conexão específica seja usada. Para obter mais informações sobre como armazenar e recuperar cadeias de conexão de arquivos de configuração, consulte Cadeias de conexão.

<connectionStrings>
  <add name="DatabaseConnection"
    connectionString="Data Source=(local);Initial
    Catalog=Northwind;Integrated Security=true;" />
</connectionStrings>

Importante

A Microsoft recomenda que você use o fluxo de autenticação mais seguro disponível. Se você estiver se conectando ao SQL do Azure, as Identidades Gerenciadas para recursos do Azure serão o método de autenticação recomendado.

Sintaxe KeyRestrictions

O exemplo a seguir habilita a mesma cadeia de conexão, habilita o uso das opções e Packet Size cadeia de Encrypt conexão, mas restringe o uso de quaisquer outras opções de cadeia de conexão.

<connectionStrings>
  <add name="DatabaseConnection"
    connectionString="Data Source=(local);Initial
    Catalog=Northwind;Integrated Security=true;"
    KeyRestrictions="Encrypt=;Packet Size=;"
    KeyRestrictionBehavior="AllowOnly" />
</connectionStrings>

Importante

A Microsoft recomenda que você use o fluxo de autenticação mais seguro disponível. Se você estiver se conectando ao SQL do Azure, as Identidades Gerenciadas para recursos do Azure serão o método de autenticação recomendado.

KeyRestrictionBehavior com sintaxe PreventUsage

O exemplo a seguir habilita a mesma cadeia de conexão e permite todos os outros parâmetros de conexão, exceto User Id, Passworde Persist Security Info.

<connectionStrings>
  <add name="DatabaseConnection"
    connectionString="Data Source=(local);Initial
    Catalog=Northwind;Integrated Security=true;"
    KeyRestrictions="User Id=;Password=;Persist Security Info=;"
    KeyRestrictionBehavior="PreventUsage" />
</connectionStrings>

Importante

A Microsoft recomenda que você use o fluxo de autenticação mais seguro disponível. Se você estiver se conectando ao SQL do Azure, as Identidades Gerenciadas para recursos do Azure serão o método de autenticação recomendado.

KeyRestrictionBehavior com sintaxe AllowOnly

O exemplo a seguir habilita duas cadeias de conexão que também contêm Initial Catalog, Connection Timeout, Encrypte Packet Size parâmetros. Todos os outros parâmetros da cadeia de conexão são restritos.

<connectionStrings>
  <add name="DatabaseConnection"
    connectionString="Data Source=(local);Initial
    Catalog=Northwind;Integrated Security=true;"
    KeyRestrictions="Initial Catalog;Connection Timeout=;
       Encrypt=;Packet Size=;"
    KeyRestrictionBehavior="AllowOnly" />

  <add name="DatabaseConnection2"
    connectionString="Data Source=SqlServer2;Initial
    Catalog=Northwind2;Integrated Security=true;"
    KeyRestrictions="Initial Catalog;Connection Timeout=;
       Encrypt=;Packet Size=;"
    KeyRestrictionBehavior="AllowOnly" />
</connectionStrings>

Importante

A Microsoft recomenda que você use o fluxo de autenticação mais seguro disponível. Se você estiver se conectando ao SQL do Azure, as Identidades Gerenciadas para recursos do Azure serão o método de autenticação recomendado.

Habilitar confiança parcial com um conjunto de permissões personalizado

Para habilitar o uso de permissões para uma zona específica, um administrador de sistema deve criar um conjunto de permissões personalizado e defini-lo como o conjunto de System.Data.SqlClient permissões para uma zona específica. Os conjuntos de permissões padrão, como LocalIntranet, não podem ser modificados. Por exemplo, para incluir System.Data.SqlClient permissões para o código que tem um Zone de , um administrador de sistema pode copiar o conjunto de permissões para LocalIntranet, renomeá-lo para "CustomLocalIntranet", adicionar as System.Data.SqlClient permissões, importar o conjunto de LocalIntranetpermissões CustomLocalIntranet usando o Caspol.exe (Code Access Security Policy Tool) e definir o conjunto de permissões como LocalIntranet_Zone CustomLocalIntranet.

Exemplo de conjunto de permissões

A seguir está um conjunto de permissões de exemplo para o Provedor de Dados do .NET Framework para SQL Server em um cenário parcialmente confiável. Para obter informações sobre como criar conjuntos de permissões personalizados, consulte Configurando conjuntos de permissões usando Caspol.exe.

<PermissionSet class="System.Security.NamedPermissionSet"
  version="1"
  Name="CustomLocalIntranet"
  Description="Custom permission set given to applications on
    the local intranet">

<IPermission class="System.Data.SqlClient.SqlClientPermission, System.Data, Version=2.0.0000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
AllowBlankPassword="False">
<add ConnectionString="Data Source=(local);Integrated Security=true;"
 KeyRestrictions="Initial Catalog=;Connection Timeout=;
   Encrypt=;Packet Size=;"
 KeyRestrictionBehavior="AllowOnly" />
 </IPermission>
</PermissionSet>

Importante

A Microsoft recomenda que você use o fluxo de autenticação mais seguro disponível. Se você estiver se conectando ao SQL do Azure, as Identidades Gerenciadas para recursos do Azure serão o método de autenticação recomendado.

Verificar ADO.NET acesso ao código usando permissões de segurança

Para cenários de confiança parcial, você pode exigir privilégios CAS para métodos específicos em seu código especificando um SqlClientPermissionAttributearquivo . Se esse privilégio não for permitido pela diretiva de segurança restrita em vigor, uma exceção será lançada antes que o código seja executado. Para obter mais informações sobre a diretiva de segurança, consulte Gerenciamento de políticas de segurança e Práticas recomendadas de política de segurança.

Exemplo

O exemplo a seguir demonstra como escrever código que requer uma cadeia de conexão específica. Ele simula negar permissões irrestritas ao System.Data.SqlClient, que um administrador de sistema implementaria usando uma política CAS no mundo real.

Importante

Ao projetar permissões CAS para ADO.NET, o padrão correto é começar com o caso mais restritivo (sem permissões) e, em seguida, adicionar as permissões específicas necessárias para a tarefa específica que o código precisa executar. O padrão oposto, começando com todas as permissões e, em seguida, negando uma permissão específica, não é seguro porque há muitas maneiras de expressar a mesma cadeia de conexão. Por exemplo, se você começar com todas as permissões e, em seguida, tentar negar o uso da cadeia de conexão "server=someserver", a cadeia de caracteres "server=someserver.mycompany.com" ainda será permitida. Ao começar sempre sem conceder nenhuma permissão, você reduz as chances de haver buracos no conjunto de permissões.

O código a seguir demonstra como SqlClient executa a demanda de segurança, que lança um SecurityException se as permissões CAS apropriadas não estiverem em vigor. A SecurityException saída é exibida na janela do console.

using System;
using System.Data;
using System.Data.SqlClient;
using System.Security;
using System.Security.Permissions;

namespace PartialTrustTopic
{
   public class PartialTrustHelper : MarshalByRefObject
   {
      public void TestConnectionOpen(string connectionString)
      {
         // Try to open a connection.
         using (SqlConnection connection = new SqlConnection(connectionString))
         {
            connection.Open();
         }
      }
   }

   class Program
   {
      static void Main(string[] args)
      {
         TestCAS("secure-connnection-string1", "secure-connnection-string2");
      }

      static void TestCAS(string connectString1, string connectString2)
      {
         // Create permission set for sandbox AppDomain.
         // This example only allows execution.
         PermissionSet permissions = new PermissionSet(PermissionState.None);
         permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));

         // Create sandbox AppDomain with permission set that only allows execution,
         // and has no SqlClientPermissions.
         AppDomainSetup appDomainSetup = new AppDomainSetup();
         appDomainSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
         AppDomain firstDomain = AppDomain.CreateDomain("NoSqlPermissions", null, appDomainSetup, permissions);

         // Create helper object in sandbox AppDomain so that code can be executed in that AppDomain.
         Type helperType = typeof(PartialTrustHelper);
         PartialTrustHelper firstHelper = (PartialTrustHelper)firstDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName, helperType.FullName);

         try
         {
            // Attempt to open a connection in the sandbox AppDomain.
            // This is expected to fail.
            firstHelper.TestConnectionOpen(connectString1);
            Console.WriteLine("Connection opened, unexpected.");
         }
         catch (System.Security.SecurityException ex)
         {
            Console.WriteLine("Failed, as expected: {0}",
                ex.FirstPermissionThatFailed);

            // Uncomment the following line to see Exception details.
            // Console.WriteLine("BaseException: " + ex.GetBaseException());
         }

         // Add permission for a specific connection string.
         SqlClientPermission sqlPermission = new SqlClientPermission(PermissionState.None);
         sqlPermission.Add(connectString1, "", KeyRestrictionBehavior.AllowOnly);

         permissions.AddPermission(sqlPermission);

         AppDomain secondDomain = AppDomain.CreateDomain("OneSqlPermission", null, appDomainSetup, permissions);
         PartialTrustHelper secondHelper = (PartialTrustHelper)secondDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName, helperType.FullName);

         // Try connection open again, it should succeed now.
         try
         {
            secondHelper.TestConnectionOpen(connectString1);
            Console.WriteLine("Connection opened, as expected.");
         }
         catch (System.Security.SecurityException ex)
         {
            Console.WriteLine("Unexpected failure: {0}", ex.Message);
         }

         // Try a different connection string. This should fail.
         try
         {
            secondHelper.TestConnectionOpen(connectString2);
            Console.WriteLine("Connection opened, unexpected.");
         }
         catch (System.Security.SecurityException ex)
         {
            Console.WriteLine("Failed, as expected: {0}", ex.Message);
         }
      }
   }
}
Imports System.Data.SqlClient
Imports System.Security
Imports System.Security.Permissions

Namespace PartialTrustTopic
    Public Class PartialTrustHelper
        Inherits MarshalByRefObject
        Public Sub TestConnectionOpen(ByVal connectionString As String)
            ' Try to open a connection.
            Using connection As New SqlConnection(connectionString)
                connection.Open()
            End Using
        End Sub
    End Class

    Class Program
        Public Shared Sub Main(ByVal args As String())
            TestCAS("Data Source=(local);Integrated Security=true", "Data Source=(local);Integrated Security=true;Initial Catalog=Test")
        End Sub

        Public Shared Sub TestCAS(ByVal connectString1 As String, ByVal connectString2 As String)
            ' Create permission set for sandbox AppDomain.
            ' This example only allows execution.
            Dim permissions As New PermissionSet(PermissionState.None)
            permissions.AddPermission(New SecurityPermission(SecurityPermissionFlag.Execution))

            ' Create sandbox AppDomain with permission set that only allows execution,
            ' and has no SqlClientPermissions.
            Dim appDomainSetup As New AppDomainSetup()
            appDomainSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase
            Dim firstDomain As AppDomain = AppDomain.CreateDomain("NoSqlPermissions", Nothing, appDomainSetup, permissions)

            ' Create helper object in sandbox AppDomain so that code can be executed in that AppDomain.
            Dim helperType As Type = GetType(PartialTrustHelper)
            Dim firstHelper As PartialTrustHelper = DirectCast(firstDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName, helperType.FullName), PartialTrustHelper)

            Try
                ' Attempt to open a connection in the sandbox AppDomain.
                ' This is expected to fail.
                firstHelper.TestConnectionOpen(connectString1)
                Console.WriteLine("Connection opened, unexpected.")
            Catch ex As System.Security.SecurityException

                ' Uncomment the following line to see Exception details.
                ' Console.WriteLine("BaseException: " + ex.GetBaseException());
                Console.WriteLine("Failed, as expected: {0}", ex.FirstPermissionThatFailed)
            End Try

            ' Add permission for a specific connection string.
            Dim sqlPermission As New SqlClientPermission(PermissionState.None)
            sqlPermission.Add(connectString1, "", KeyRestrictionBehavior.AllowOnly)

            permissions.AddPermission(sqlPermission)

            Dim secondDomain As AppDomain = AppDomain.CreateDomain("OneSqlPermission", Nothing, appDomainSetup, permissions)
            Dim secondHelper As PartialTrustHelper = DirectCast(secondDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName, helperType.FullName), PartialTrustHelper)

            ' Try connection open again, it should succeed now.
            Try
                secondHelper.TestConnectionOpen(connectString1)
                Console.WriteLine("Connection opened, as expected.")
            Catch ex As System.Security.SecurityException
                Console.WriteLine("Unexpected failure: {0}", ex.Message)
            End Try

            ' Try a different connection string. This should fail.
            Try
                secondHelper.TestConnectionOpen(connectString2)
                Console.WriteLine("Connection opened, unexpected.")
            Catch ex As System.Security.SecurityException
                Console.WriteLine("Failed, as expected: {0}", ex.Message)
            End Try
        End Sub
    End Class
End Namespace

Você deve ver esta saída na janela Console:

Failed, as expected: <IPermission class="System.Data.SqlClient.
SqlClientPermission, System.Data, Version=2.0.0.0,
  Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1"
  AllowBlankPassword="False">
<add ConnectionString="Data Source=(local);Initial Catalog=
  Northwind;Integrated Security=SSPI" KeyRestrictions=""
KeyRestrictionBehavior="AllowOnly"/>
</IPermission>

Connection opened, as expected.
Failed, as expected: Request failed.

Importante

A Microsoft recomenda que você use o fluxo de autenticação mais seguro disponível. Se você estiver se conectando ao SQL do Azure, as Identidades Gerenciadas para recursos do Azure serão o método de autenticação recomendado.

Interoperabilidade com código não gerenciado

O código executado fora do CLR é chamado de código não gerenciado. Portanto, mecanismos de segurança como CAS não podem ser aplicados a código não gerenciado. Componentes COM, interfaces ActiveX e funções da API do Windows são exemplos de código não gerenciado. Considerações especiais de segurança se aplicam ao executar código não gerenciado para que você não comprometa a segurança geral do aplicativo. Para obter mais informações, consulte Interoperando com código não gerenciado.

O .NET Framework também oferece suporte à compatibilidade com versões anteriores de componentes COM existentes, fornecendo acesso por meio de interoperabilidade COM. Você pode incorporar componentes COM em um aplicativo .NET Framework usando ferramentas de interoperabilidade COM para importar os tipos COM relevantes. Uma vez importados, os tipos COM estão prontos para uso. A interoperabilidade COM também permite que os clientes COM acessem o código gerenciado exportando metadados de assembly para uma biblioteca de tipos e registrando o componente gerenciado como um componente COM. Para obter mais informações, consulte Interoperabilidade COM avançada.

Consulte também