My Development Notes

By Haemoglobin
3/10/2010 (revision 1)

Application Security

Host Evidence describes an assemblies origin:

  • Application Directory
  • Hash
  • Publisher
  • Site
  • Strong Name
  • URL
  • Zone

Assembly evidence is custom user or developer provided evidence.

Default Permissions:

  • Directory Services
  • DNS
  • Environment Variables
  • Event Log
  • File Dialog
  • File IO
  • Isolated Storage File
  • Message Queue
  • Performance Counter
  • Printing
  • Reflection
  • Registry
  • Security
  • Service Controller
  • Socket Access
  • SQL Client
  • User Interface
  • Web Access
  • X509 Store

Permission Sets

  • FullTrust
  • SkipVerification
  • Execution
  • Nothing
  • LocalIntranet
  • Internet
  • Everything

Code Access Permissions

Identity Permissions

  • There are 4 configurable poilicy levels: Enterprise, Machine, User and Application Domain (programmed).
  • Enterprise security policy is administered through AD for the entire organisation.
  • The most restrictive set of permissions is applied to the assembly if evidence matches multiple policy levels.
  • Use the .NET Framework 2.0 Configuration tool or command line Caspol to manage CAS policies.
  • A Code group (i.e Internet_Zone) associates assemblies that match particular evidence (i.e Zone: Internet) to a permission set (i.e Internet).
  • Assemblies can be a member of multiple code groups (for each evidence it matches).
  • Code groups associate assemblies to permission sets (similar to a user group).
  • Nesting code groups ensures the assembly matches all evidence including that of parent(s) (instead of OR union within the same policy level)
  • No assembly can have more permissions than the user running the assembly, regardless of how the assembly uses CAS.
  • Each policy level contains its own set of code groups, by default, Enterprise and User has "All Code", Machine has the following:
Code Group Evidence Permission Set
My_ComputerZone: My ComputerFullTrust
LocalIntranet_ZoneZone: Local IntranetLocalIntranet
Internet_ZoneZone: InternetInternet
Restricted_ZoneZone: Untrusted sitesNothing
Trusted_ZoneZone: Trusted sitesInternet

Assembly Level Declarative Security

Declarative CAS demands can be used to ensure that your assembly has all necessary permissions but none that it does not require (to stop hacker manipulation). Administrators can also use these to examine the asseembly's declarative CAS demands to identify the minimum permissions they need to grant to take advantage of your application's functionality.

CAS declarations are ignored for fully trusted assemblies.
The following could all be applied on an assembly:

[assembly: PrintingPermission(SecurityAction.RequestMinimum)]
[assembly: UIPermission(SecurityAction.RequestMinimum, Unrestricted = true)] //required for communication with a debugger [assembly:FileIOPermissionAttribute(SecurityAction.RequestOptional, Read=@"c:\")]
[assembly:FileIOPermissionAttribute(SecurityAction.RequestRefuse, Read=@"c:\windows")]
  • SecurityAction.RequestMinimum - Should be RequireMinimum (error at startup)
  • SecurityAction.RequestOptional - Should be RefuseAllExcept (no error at startup)
  • SecurityAction.RequestRefuse - Ensures the application does not have a permission it doesn't need to have (no error at startup).

Method Level Declarative/Imperative Security

AssertIgnore fact that callers might not have the specified permission. If children in the stack call Demand, it will only travel up as high as where the Assert() is called (and including), it is basically vouching for all callers. To use assert the assembly must have the SecurityPermissionFlat.Assertion privilege as well as the privilege being asserted. Is a way to allow lesser priveleged assemblies (say ones in the Internet zone) to have higher privilege functionality. Just ensure the asserting assembly has the AllowPartiallyTrustedCallersAttribute if the asserting assembly is strong named.
DemandThrow exception if all callers in the stack lack the specified permission
DenyRemoves the specified permission if it is already granted.
PermitOnlyRemove all permissions except this one.
LinkDemand(Imperative only) Throw exception if immediate caller assembly lacks the permission.
InheritanceDemand(Imperative only) Ensures derived classes (assemblies) of this class have the permission.

Declarative (must be static):

[WebPermission(SecurityAction.Demand, ConnectPattern = @"http://www\.microsoft\.com/.*")]
public static void RequestWebPage() {}

Imperative (can be dynamic):

public static void CreateFolder() {
    FileIOPermission filePermissions = new FileIOPermission(FileIOPermissionAccess.Write, @"c\Program Files"); 
    filePermissions.Demand(); //Check caller permissions. Can gracefully catch this exception and figure out what to do. 
    //Or check permissions of current assembly
    if (SecurityManager.IsGranted(filePermissions)) {
        ...
    }
    //Assert can only be used once in a method, if need to assert multiple permissions need to use a custom permission set.
    PermissionSet myPermissions = new PermissionSet(PermissionState.None); 
    myPermissions.Add(new FileIOPermission.....); 
    myPermissions.Assert|Demand(); 
    //To undo previous Deny, PermitOnly and Assert statements
    System.Security.CodeAccessPermission.[RevertDeny|RevertPermitOnly|RevertAssert|RevertAll]() 

    X509Certificate certificate = //loaded certificate;
    PublisherMembershipCondition policy = new PublisherMembershipCondition( certificate ); 
    bool pass = policy.Check( Assembly.GetCallingAssembly().Evidence ); 
}

Note that a lot of .NET classes and methods already have their own security demands so writing your own layer of demands above them can be redundant.

User and Data Security

  • Authentication - Process of identifying a user
  • Authorisation - Verifying that the user is allowed to access a requested resource
WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent(); 
currentIdentity.AuthenticationType, .IsAnonymous, .IsAuthenticated, .IsGuest, .IsSystem, .Name, .Token

WindowsPriciple currentPrinciple = new WindowsPrinciple(currentIdentity); 
//Or 
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);  //+ PrincipalPolicy.UnauthenticatedPrincipal (Default - an unauthenticated entity has Name set to the empty string ("") and IsAuthenticated set to false), PrincipalPolicy.NoPrincipal (No principal or identity objects should be created) 
WindowsPrincipal currentPrincipal = (WindowsPrincipal)Thread.CurrentPrincipal; 

if (currentPrincipal.IsInRole(WindowsBuiltInRole.Administrator)) {...}

if (currentPrincipal.IsInRole(System.Environment.MachineName|System.Environment.UserDomainName+"\Accounting")) {...}

try { AdministratorOnlyMethod() }
catch (System.Security.SecurityException ex) { //Your account lacks necessary permission }

[PrincipalPermission(SecurityAction.Demand, Role = @"BUILTIN\Administrators"]
[PrincipalPermission(SecurityAction.Demand, Name = @"CONTOSO\Administrator"]
static void AdministratorsOnlyMethod() {}

[PrincipalPermission(SecurityAction.Demand, Authenticated = true] //Allow anyone who is authenticated.

//Imperative 
PrincipalPermission(PermissionState), PrincipalPermission(Name, Role), PrincipalPermission(Name, Role, Authenticated)
try {
    new PrincipalPermission(null, Environment.MachineName+"\VS Developers", true).Demand(); 
}
catch (SecurityException ex) {} 

//Custom users/roles 
IIdentity
    string AuthenticationType;
    bool IsAuthenticated;
    string Name;

IPrincipal
    IIdentity Identity;
    bool IsInRole(string role); 

WindowsIdentity|GenericIdentity : IIdentity
WindowsPrincipal|GenericPrincipal : IPrincipal

GenericPrincipal principal = new GenericPrincipal(new GenericIdentity(username[, authenticationType]), new string[] { "role1", "role2" });
Thread.CurrentPrincipal = principal; 
//OR
AppDomain.CurrentDomain.SetThreadPrincipal(principal); //Before the call to Thread.CurrentPrincipal;

Thread.CurrentPrincipal.IsInRole("role1");

//Authentication Exceptions in Streams
System.Net.Security.NegotiateStream|System.Net.Security.SslStream throws 
    System.Security.Authentication.AuthenticationException -> prompt user to provide different credentials
    System.Security.Authentication.InvalidCredentialException -> underlying stream is in an invalid state. 

//Setting application domain policy for newly loaded assemblies
PolicyLevel domainPolicyLevel = PolicyLevel.CreateAppDomainLevel();
//..add permissions to domainPolicy...

AppDomain.CurrentDomain.SetAppDomainPolicy(domainPolicyLevel); 

Access Control Lists (ACL)

Contains access control entries (ACE's).
Discretionary access control lists (DACL) - restrict access to resources
Security access control lists (SACL) - audits access to resources

To enable auditing of events in windows Audit Object Access security policy must be enabled in the Local Security Policy console within Administrative Tools.

[Directory|File|Registry|Mutex]Security ds  = new DirectorySecurity(@"c:\windows", AccessControlSections.Access); 
AuthorizationRuleColleciton arc = ds.Get[Access|Audit]Rules(true, true, typeof(NTAccount)); 
foreach (FileSystem[Access|Audit]Rule ar in arc) {
    ar.IdentityReference, .AccessControlType, .FileSystemRights
}

//Setting ACL
DirectorySecurity ds = Directory.GetAccessControl(@"c:\windows"); 
ds.[Add|Remove]AccessRule(new FileSystemAccessRule("Guest", FileSystemRights.Read, AccessControlType.Allow)); 
Directory.SetAccessControl(@"c:\windows", ds); 

Encrypting and Decrypting Data

  • Symmetric keys rely on another medium (such as mail, or voice) to share the secret key. This key both encodes and decodes.
  • Assemmetric keys are used over the internet (public/private key pairs). Data encrypted on the public key can only be decrypted by the private key. Much slower so HTTPS/SSL uses assemetric encryption to exchange a symmetric key securely.

Symmetric Cryptography Classes

System.Security.Cryptography

  • RijndaelManaged - 128-256 bits (32bit incremements)
  • AesManaged - 128bits (based on Rijndael), government encryption standard
  • DES - 56bits (legacy, insecure)
  • TripleDES - 156bits, 112bits used for encryption. DES applied 3 times.
  • RC2 - Variable, designed to replace DES
string password = "secretpassword"; 

RijndaelManaged encryption = new RijndaelManaged(); 

byte[] salt = Encoding.ASCII.GetBytes(username); 
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt); 
encryption.Key = key.GetBytes(encryption.KeySize / 8); 
encryption.IV = key.GetBytes(encryption.BlockSize / 8); //Initialisation vector (encryptor and decryptor must have the same)

ICryptoTransform encryptor = encryption.Create[Encryptor|Decryptor]();

CryptoStream encryptStream = new CryptoStream(normalStream, encryptor, CryptoStreamMode.[Write|Read]); 
//use encryptStream as you would any other stream.

Asymmetric Cryptography Classes

Extend from System.Security.Cryptography.AsymmetricAlogrithm

  • RSACryptoServiceProvider - used for all asymmetric encryption and decryption.
  • DSACryptoServiceProvider - used for digitally signing messages.
RSACryptoServiceProvider myRSA = new RSACryptoServiceProvider(); 
RSAParameters publicKey = myRSA.ExportParameters(false); //RSAParameters is the key. False to export public key only. 
myRSA.ToXmlString(true|false); //export public (and private if true) keys to xml. Can be read again with .FromXmlString();  

//When the following application is run multiple times, the same key will be used each time based on the key container name.
CspParameters persistantCsp = new CspParameters();
persistantCsp.KeyContainerName = "PersistentAsymmetricKey"; 
RSACryptoServiceProvider myRSA = new RSACryptoServiceProvider(persistantCsp);
myRSA.PersistKeyInCsp = true; //Store the private key in the Crypto Service Provider (CSP)

//Encrypting / Decrypting
string messageToEncrypt = "Hello World"; 
RSACryptoServiceProvider myRSA = new RSACryptoServiceProvider(); 

byte[] messageBytes = Encoding.Unicode.GetBytes(messageToEncrypt); 
byte[] encryptedBytes = myRSA.Encrypt(messageBytes, false); 
//bool specified Optimal Asymmetric Encryption Padding (only available winXP and later), must be the same for both enc and dec.
byte[] decryptedBytes = myRSA.Decrypt(encryptedBytes, false); 
string decryptedMessage = Encoding.Unicode.GetString(decryptedBytes); 

//Sending encrypted data over the network
TcpClient clientCon = new TcpClient(serverName, 443); 
SslStream clientSslStream = new SslStream (clientCon.GetStream()); 
X509CertificateCollection certificate = new X509CertificateCollection(); 
certificate.Add(new X509Certificate(certFilePath)); 
clientSslStream.AuthenticateAsClient( "server", certificate, SslProtocols.Default, true ); 
//Write data into the clientSslStream

Hashes

A hash is a checksum that is unique to a specific file or piece of data (one way) - good for storing passwords. Nonkeyed Hashing Algorithms

  • MD5CryptoServiceProvider - Message Digest 128 bits
  • RIPEMD160Managed - Message Digest 160bits
  • SHA1CryptoServiceProvider - The Secure Hash Algorithm 1 160bits
  • SHA256Managed - The Secure Hash Algorithm 256bits
  • SHA384Managed - The Secure Hash Algorithm 384bits
  • SHA512Managed - The Secure Hash Algorithm 512bits
MD5 myHash = new MD5CryptoServiceProvider(); 
myHash.ComputeHash(new BinaryReader(file).ReadBytes((int)file.length));
string hash = Convert.ToBase64String(myHash.Hash);

Keyed Hashing Algorithms
Used to ensure hash itself isn't tampered with which defeats the purpose of hashing. Both sender and receiver must know the secret key.

  • HMACSHA1 - Hash-based Message Authentication Code (HMAC) using SHA1. Used to determine if a message has been tampered with provided that sender and receiver share a secret key (of any size) - hash size = 20 bytes.
  • MACTripleDES - Message Authentication Code using TripleDES. Key length 8, 16, 24. Hash legth 8 bytes.
//If either the secret key or the data changes, the hash will change. 
HMACSHA1 myHash = new HMACSHA1(secretKeyBytes[]); 
myHash.ComputeHash(dataToHash[]);

Signing

Uses asymmetric keys, the signature can only be generated with the private key, but can be verified with the public key.

DSACryptoServiceProvider signer = new DSACryptoServiceProvider(); 

byte[] signature = signer.Sign[Data|Hash]([data|hash]Bytes[]); //+ ,new SHA1CryptoServiceProvider() if using RSACryptoServiceProvider(); 
string publicKey = signer.ToXmlString(false); 

//Verifying
DSACryptoServiceProvider verifier = new DSACryptoServiceProvider(); 
verifier.FromXmlString(publicKey); 
if(verifier.Verify[Data|Hash]([data|hash]Bytes[], signature)) { //+ ,new SHA1CryptoServiceProvider() as second param if using RSACryptoServiceProvider(); 
    //data is verified and has not been tampered with
}


Comments

Powered by BlogEngine.NET 1.6.1.0 | Design by styleshout | Enhanced by GravityCube.net | 1.4.5 Changes by zembian.com | Adapted by HamishGraham.NET
(c) 2010 Hamish Graham. Banner Image (c) Chris Gin