.Net Security
Validate Input
- ASP:
- Page validateRequest (1.1+) or do it manually (it's not a friendly message)
- ASP.Net validators (required for non-nulls, compare for datatype and cross field, range for min-max).
- Santize (HttpUtility.HtmlEncode/ MapPath, string.Replace)
allowCrossAppMapping flag: Request.MapPath(input, Request.ApplicationPath, false) - Manual checks- esp length and Int.max (unless you use regex). Reuse the validation summary: make a class based on IValidator which takes the ErrorMessage and sets IsValid=false and add it to Page.Validators.
- Always parameterize sql parameters; beware constructing sql from strings (can be done in sprocs too...)
- SQL Server permissions- create db roles for specific tables/ops; in MixedMode create special users.
- Error Messages- details (stack etc) should go to email/ event log (NB: trust=Medium has no access to event log)
Role Based Security
IIdentity contains the user name; IPrincipal contains identity and roles (IsInRole). Normally it's WindowsIdentity, but can be GenericIdentity or your custom class.
//Set the policy to WindowsPrincipal (because the default is UnauthenticatedPrincipal).
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
if (useMethod == 1) //Either get the user -> IPrincipal
{
WindowsIdentity myIdentity = WindowsIdentity.GetCurrent(); //use GetCurrent static method
WindowsPrincipal myPrincipal = new WindowsPrincipal(myIdentity); //then get the principal from the Id
Console.WriteLine(myIdentity.Name + " " + myPrincipal.IsInRole(@"domain\Users"));
}
else //or get the Principal from the thread directly (containing the IIdentity).
{
// Gets a reference to the current WindowsPrincipal (cast from IPrincipal)
WindowsPrincipal myPrincipal2 = (WindowsPrincipal)System.Threading.Thread.CurrentPrincipal;
// Gets the WindowsIdentity of the current principal
WindowsIdentity myIdentity2 = (WindowsIdentity)myPrincipal2.Identity;
Console.WriteLine(myIdentity2.Name + " " + myPrincipal2.IsInRole(WindowsBuiltInRole.Administrator));
}
- Check manually
if(myPrincipal.IsInRole(WindowsBuiltInRole.Administrator)
//(NB: different o/s have different groups so trap errors)if(myPrincipal.IsInRole(@"Domain\Group")
//domain groups - or System.Environment.MachineName or .UserDomainName + "\Group"
- Demand with PrincipalPermission (SetPrincipalPolicy, use try/catch SecurityException)
- Declarative
[PrincipalPermission(SecurityAction.Demand, Name="Homer", Role=@"BUILTIN\Administrators")]
public void HomerOnlyMethod() - Imperative
PrincipalPermission perm= new PrincipalPermission(null, "@"BUILTIN\Administrators"); //(name, role)- use either or both
perm.Demand();
- Declarative
Using strings for role names ("BUILTIN\\Administrators") is dangerous as different Windows locales have different names. There's a short WindowsBuiltInRole enum, and the WellKnownSidType is extensive.
var wi = WindowsIdentity.GetCurrent();
var wp = new WindowsPrincipal(wi);
//NB: UAC makes this always false!
var isAdmin = wp.IsInRole(WindowsBuiltInRole.Administrator);
var si = new SecurityIdentifier(
WellKnownSidType.BuiltinAdministratorsSid,
wi.User.AccountDomainSid); //with the domain
var isAdminViaSi = wp.IsInRole(si);
//show string names of all groups, instead of SIDs
foreach (var g in wi.Groups.Translate(typeof(NTAccount)))
{
Console.WriteLine(g); //group names
}
Code Access Security
CAS is based on the evidence
- from the assembly (strong name, publisher signature) and from the host (app path, url, site). The assembly is then assigned to a code group (machine level being like IE security zones) which have a permission set.
Enterprise/Machine/User Level
The permissions (FileIO, EventLog, Registry etc) are organised into default permission sets (Internet, Nothing - can't run-, Everything...). Assemblies are put into Code Groups (MyComputer, Trusted, Internet...) which have a permission set.
.Net config msc to view the declarative permissions an assembly demands, and create a new code group/permission set to elevate the permissions (command line caspol to change).
Machine/user level admins can change the enterprise level permissions- unless you use LevelFinal attribute. Cf Exclusive attribute (=only use this permission, ignore everything else- dangerous!).
Eg KB to caspol a network share so it's full trust (eg aspx virtual directory)
NB: MyComputerZone= FullTrust == no CAS (!= Everything == CAS but can do everything)
Assembly Level
- SecurityAction.RequestMinimum - required (only one to throw exception if fails)
- SecurityAction.RequestOptional - required and refuse everything else
- SecurityAction.RequestRefuse - explicitly refuse specific permissions
NB: SecurityAction.RequestOptional runs even when fails... add combos of different requests if rqd (eg RequestOptimal for a directory, Refuse a subdirectory)
[assembly: FileIOPermission(SecurityAction.RequestOptional, Read=@"C:\temp\")]
Using both RequestOptimal and RequestRefuse - the Optimal refuses everything else anyway, so refuse should only refuse more specific actions in the set allowed by optimal.
To enable debugging add [assembly: UIPermission(SecurityAction.RequestMinimum, Unrestricted= true)] (needed by debugger!!)
Method-Level CAS
NB: The SecurityActions for assemblies are RequestMinimum/Optional/Refuse. Classes and methods have Demand/Assert/Deny etc.
- Demand and LinkDemand look at callers permissions only (not this assembly)- generally FCL only. InheritanceDemand is for assemblies inheriting from your class.
[FileIOPermission(SecurityAction.Demand, Write = @"C:\temp\")]
public static void SaveTemp()
{
try
{
//imperative - can trap the error AND dynamically create paths/urls
FileIOPermission fp =
new FileIOPermission(FileIOPermissionAccess.Write, @"C:\temp\");
fp.Demand();
//save file
}
catch (System.Security.SecurityException ex)
{
}
}
- PermitOnly and Deny == assembly level RequestOptional and Refuse.
There are revert methods after usepublic static void UseEventLog()
{
EventLogPermission errorPerms =
new EventLogPermission(PermissionState.Unrestricted);
errorPerms.PermitOnly();
// Call event log
CodeAccessPermission.RevertPermitOnly();
}
- Assert to elevate privileges for a particular sandboxed method.
Stops checking stack - so faster. NB if you call another assembly it gets the elevated privileges (unless you RevertAssert - use try/finally)
NB: .Assert overrides other permissions in the stack; .Demand requires all callers in the stack have the permission.
Untrusted- Trusted (Assert) - Trusted (Demand) = works
Untrusted- Trusted (Demand) = fails
Partial-trust can't call strong names unless explicitly allows with APTCA
[assembly:AllowPartiallyTrustedCallers]
You can Assert only once in a method- so for multiple permissions, create new PermissionSet()
AppDomain security
Creating a custom domain and execute an assembly in it with custom evidence. (You can increase permissions, so must have SecurityPermission.ControlEvidence). See Create domain example.
//using System.Security;
//using System.Security.Policy;
//create object array
object[] hostEvidence = { new Zone(SecurityZone.Internet) };
//put the array into a new evidence object
Evidence internetEvidence = new Evidence(hostEvidence, null);
//or - Evidence fakeEvidence = new Evidence(); //create empty evidence
// fakeEvidence.AddHost(new Site("www.microsoft.com")); //assign each evidence
//you can put the evidence into the Domain ctor or assembly
AppDomain myDomain = AppDomain.CreateDomain("MyDomain");
myDomain.ExecuteAssembly(@"C:\SpecialAssembly.exe", internetEvidence);
You can create a custom security policy too.
- Create MembershipConditions to match the evidence (eg ApplicationDirectoryMembershipCondition )
- Create a PermissionSet
- Create a PolicyStatement from the PermissionSet
- Create a CodeGroup with the PolicyStatement (System.Security.Policy.UnionCodeGroup can have nested codegroups or FileCodeGroup with only FileIOPermission, or NetCodeGroup with WebPermission)
- Create a PolicyLevel = PolicyLevel.CreateAppDomainLevel();
Assign CodeGroup to it: safePolicyLevel.RootCodeGroup = safeCodeGroup;
Then: myDomain.SetAppDomainPolicy(safePolicyLevel);
You can also create your own evidence (and MembershipCondition) and permissions.
COM+ Roles (Serviced components)
- You MUST add [assembly: ApplicationAccessControl(true)] to enable it
- Disable on a class basis with [ComponentAccessControl(false)].
- Automatically add roles- [assembly: SecurityRole("Manager")]
- Verify role on class or method: [SecurityRole("Manager")] public class X
- Programmatic: if (SecurityCallContext.CurrentCall.IsCallerInRole("Manager")) {}
- Programmatic: check if using security: if(SecurityCallContext.CurrentCall.IsSecurityEnabled) {}
ACLs
You can control Windows file system access control lists (from .net 2+)
- DACL (discretionary access control list) is the type that restricts access
- SACL (system acl) audits access. The auditing tab on properties/Security/Advanced
- Effective permissions: Users have the union of user and group privileges- but deny access overrides grants, so one group that denies access will take priority
- Windows Explorer Security tab (Folder Options, uncheck Use Simple File Sharing- can't in Windows XP Home )
- Cmd line- Calcs, XCalcs
- .Net 2 only: System.Security.AccessControl