Security in C#

Security is an indispensable aspect of every C# applications, and must be considered at every phase of development: not merely when design and implementation are complete.

This list is in not an exhaustive list of potential security problems. It highlights some common issues of which C# developers need to be aware.

  • Use the checked keyword to control the overflow-checking context for integral-type arithmetic operations and conversions.

  • Always use the most restrictive data type for parameters. For example, when passing a value into a method that describes the size of a data structure, use unsigned integer rather than integer.

  • Do not make decisions based on filenames. Filenames can be expressed in many different ways, and your test for a particular file may be bypassed.

  • Never, ever hardcode passwords or other sensitive information into your application.

  • Always validate input that is used to generate SQL queries.

  • Validate all inputs into your methods. The regular expression methods in System.Text.RegularExpressions namespace are useful for confirming input is of the correct form, such as an email address.

  • Don't display exception information: it provides any would-be attacker with valuable clues.

  • Ensure your application works while running with the least possible privileges. Few applications require a user to be logged in as an administrator.

  • Don't use your own encryption algorithms, use the System.Security.Cryptography classes.

  • Give your Assemblies strong names.

  • Don't store sensitive information in XML or other configuration files.

  • Check managed code that wraps native code carefully. Confirm the native code is secure, especially with regard to buffer overruns.

  • Use caution when using delegates passed from outside your application.

  • Run FxCop on your assemblies to ensure compliance with Microsoft .NET Framework Design Guidelines. FxCop can also find and warn against over 200 code defects.

Reflection in C#

Reflection provides objects (of type Type) that encapsulate assemblies, modules and types. You can use reflection to dynamically create an instance of a type, bind the type to an existing object, or get the type from an existing object and invoke its methods or access its fields and properties. If you are using attributes in your code, Reflection enables you to access them. For more information, see Attributes.

Here's a simple example of Reflection using the static method GetType - inherited by all types from the Object base class - to obtain the type of a variable:

// Using GetType to obtain type information:
int i = 42;
System.Type type = i.GetType();
System.Console.WriteLine(type);

The output is:

System.Int32

In this example, Reflection is used to obtain the full name of a loaded assembly:

// Using Reflection to get information from an Assembly:
System.Reflection.Assembly o = System.Reflection.Assembly.Load("mscorlib.dll");
System.Console.WriteLine(o.GetName());

The output is:

mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

Reflection Overview

Reflection is useful in the following situations:

Performance in C#

This section covers two issues that can adversely effect performance, and links to resources on performance issues.

Boxing and Unboxing

Boxing and unboxing are computationally expensive processes. When a value type is boxed, an entirely new object must be created. This can take up to 20 times longer than an assignment. When unboxing, the casting process can take four times as long as an assignment. For more information, see Boxing and Unboxing.

Destructors

Empty destructors should not be used. When a class contains a destructor, an entry is created in the Finalize queue. When the destructor is called, the garbage collector is invoked to process the queue. If the destructor is empty, this simply results in a loss of performance. For more information, see Destructors.

Threading In C#

Threading enables your C# program to perform concurrent processing so you can do more than one operation at a time. For example, you can use threading to monitor input from the user, perform background tasks, and handle simultaneous streams of input. The System.Threading namespace provides classes and interfaces that support multithreaded programming and enable you to easily perform tasks such as creating and starting new threads, synchronizing multiple threads, suspending threads, and aborting threads.

To incorporate threading in your C# code, simply create a function to be executed outside the main thread and point a new Thread object at it. The following code example creates a new thread in a C# application

System.Threading.Thread newThread;
newThread = new System.Threading.Thread(anObject.AMethod);

The following code example starts a new thread in a C# application:

newThread.Start();

Multithreading solves problems with responsiveness and multi-tasking, but can also introduce resource sharing and synchronization issues because threads are interrupted and resumed without warning according to a central thread scheduling mechanism. For more information, see Thread Synchronization. See Using Threads and Threading for overview information.

Threads have the following properties:

  • Threads enable your C# program to perform concurrent processing.

  • The .NET Framework's System.Threading namespace makes using threads easier.

  • Threads share the application's resources. For more information, see Using Threads and Threading.

Interoperability in C#

Interoperability enables you to preserve and leverage existing investments in unmanaged code. Code running under the control of the common language runtime (CLR) is called "managed code", and code running outside the CLR is "unmanaged code". COM, COM+, C++ components, ActiveX components, and Win32 API are examples of unmanaged code.

The .NET Framework enables interoperability with unmanaged code through platform invoke services, the System.Runtime.InteropServices namespace, and the CLR and through COM Interoperability (COM interop).

  • There are two ways to use unmanaged APIs from managed code: through platform invoke and through It Just Works (IJW) in C++. Platform invoke enables managed code to call functions exported from an unmanaged dynamic link library (DLL), such as Win32 API and custom DLLs. The CLR handles DLL loading and parameter marshaling. For performance, check if there is an equivalent function available in the .NET Framework rather than using platform invoke. For more information, see A Closer Look at Platform Invoke.

  • COM interop, which enables managed code to interact with COM objects through COM interfaces and COM clients. There are two ways to use COM components from managed code:

    • For calling OLE Automation compatible COM components, use COM interop or tlbimp.exe. The CLR handles COM component activation and parameter marshaling.

    • For IDL based COM components, use IJW and C++. Every public managed class that implements IUnknown, IDispatch, and other standard COM interfaces can be called from unmanaged code through COM interop. For more information, see Microsoft .NET/COM Migration and Interoperability.

For more information, see Interoperating with Unmanaged Code and Improving Interop Performance.

Both PInvoke and COM interop use marshaling to translate arguments such as integers, strings, arrays, structures, and pointers between managed and unmanaged code. For more information, see Interop Marshaling Overview.

Exceptions and Exception Handling in C#

The C# language's exception handling features provide a way to deal with any unexpected or exceptional situations that arise while a program is running. Exception handling uses the try, catch, and finally keywords to attempt actions that may not succeed, to handle failures, and to clean up resources afterwards. Exceptions can be generated by the common language runtime (CLR), by third-party libraries, or by the application code using the throw keyword.

In this example, a method tests for a division by zero, and catches the error. Without the exception handling, this program would terminate with a DivideByZeroException was unhandled error.

int SafeDivision(int x, int y)
{
try
{
return (x / y);
}
catch (System.DivideByZeroException dbz)
{
System.Console.WriteLine("Division by zero attempted!");
return 0;
}
}

Exceptions have the following properties:

  • When your application encounters an exceptional circumstance, such as a division by zero or low memory warning, an exception is generated.

  • Use a try block around the statements that might throw exceptions.

  • Once an exception occurs within the try block, the flow of control immediately jumps to an associated exception handler, if one is present.

  • If no exception handler for a given exception is present, the program stops executing with an error message.

  • If a catch block defines an exception variable, you can use it to get more information on the type of exception that occurred.

  • Actions that may result in an exception are executed with the try keyword.

  • An exception handler is a block of code that is executed when an exception occurs. In C#, the catch keyword is used to define an exception handler.

  • Exceptions can be explicitly generated by a program using the throw keyword.

  • Exception objects contain detailed information about the error, including the state of the call stack and a text description of the error.

  • Code in a finally block is executed even if an exception is thrown, thus allowing a program to release resources.

Collection Classes in C#

The .NET Framework provides specialized classes for data storage and retrieval. These classes provide support for stacks, queues, lists, and hash tables. Most collection classes implement the same interfaces, and these interfaces may be inherited to create new collection classes that fit more specialized data storage needs.

NoteNote

Applications that target version 2.0 and later of the .NET Framework should use the generic collection classes in the System.Collections.Generic namespace, which provide greater type-safety and efficiency than their non-generic counterparts.

C#
ArrayList list = new ArrayList();
list.Add(10);
list.Add(20);

Collection Classes Overview

Collection Classes have the following properties

  • Collection classes are defined as part of the System.Collections or System.Collections.Generic namespace.

  • Most collection classes derive from the interfaces ICollection, IComparer, IEnumerable, IList, IDictionary, and IDictionaryEnumerator and their generic equivalents.

  • Using generic collection classes provides increased type-safety and in some cases can provide better performance, especially when storing value types. For more information, see Benefits of Generics.