Protecting Data

for RuBoard

Data protection is something of which you should be aware. If you deal with any data that is sensitive in nature, the .NET Framework can help you protect it. In addition, there are some coding principles you should follow to prevent trivial discovery of sensitive data. Depending on where the sensitive data is ” persisted to disk, stored in memory, or sent on the network ”there are different approaches to protecting that data.

Data Persisted to Disk

Some data you want to store in your application may need to be protected. For example, if you want to store a credit card number or if you want to store data that is used to authenticate users (passwords, tokens, and so on), you probably don't want to just create a file named secrets.txt and write out the data. This allows malicious code or malicious users to easily discover the critical data.

To limit who can read data an application persists to disk, the .NET Framework provides two sets of functionality ”isolated storage and cryptography classes. Isolated storage provides storage capabilities to semi-trusted .NET Framework applications that other semi-trusted .NET Framework applications cannot read. Cryptography is a general field of study in computer science that allows for clever obfuscation of data. The .NET Framework has a rich set of cryptography classes that allow you to encrypt data, among other things.

Isolated Storage

Isolated storage is provided by the .NET Framework as a mechanism for semi-trusted applications to store data that other semi-trusted applications cannot read. Just as Web sites use cookies on a client machine to store data such as user preferences, .NET Framework assemblies can store data in isolated storage on a client machine. Also, just as Web sites should not be able to access cookies for other Web sites, semi-trusted applications should not be able to read for isolated storage for other semi-trusted applications.

Note that isolated storage only protects data from other semi-trusted applications, such as applications granted only the LocalIntranet or Internet permission sets. Applications that are granted unrestricted FileIOPermissions will be able to read your isolated storage data. This is because isolated storage is contained somewhere on the hard drive, so the application can search for it and will be allowed to read any files it finds. This is also true for cookies because they are stored on the hard drive. Basically, remember that any application that is given unrestricted access to a hard drive can read anything it finds (as long as the operating system doesn't block it due to ACLs or some other protection mechanism).

Isolated storage is provided by the types located in the System.IO.IsolatedStorage namespace. In that namespace, there are two primary classes that do the work ” IsolatedStorageFile and IsolatedStorageFileStream . IsolatedStorageFile defines a scope of containment for isolated storage. An IsolatedStorageFile then contains any number of IsolatedStorageFileStream s.

To use isolated storage, you start by calling the static method IsolatedStroageFile.GetStore . The most interesting parameter of this method is the IsolatedStorageScope parameter. Table 29.1 lists the most useful values for this parameter. You will generally want to pass null for the second and third parameters to indicate that you want to use the scope of the current application domain and assembly evidence. If you pass anything except null , you will have to be granted the IsolatedStorageFilePermission with the flag AdministerIsolatedStorageByUser . This permission is not given in the Internet or LocalIntranet permission sets.

Table 29.1. Different Ways to Utilize Isolated Storage Using IsolatedStorageScope Parameters
IsolatedStorageScope Values Description
User Assembly Isolates storage on the basis of a specific assembly for a given user. This scope will use the same storage for an assembly no matter what application calls it. That is, an identical assembly can be used by multiple applications, but this scope will cause all of those applications to share the same storage.
User Assembly Domain Isolates storage on the basis of a specific assembly in a specific application for a given user. This scope will provide different storage for an assembly if is in different applications.
User Assembly Roaming The same as User Assembly , except that the data stored on the client will roam if the user is using a roaming profile.
User Assembly Domain Roaming The same as User Assembly Domain , except that the data stored on the client will roam if the user is using a roaming profile.

After you have an IsolatedStorageFile object, you can just read or write data from IsolatedStorageFileStream objects as long as you don't exceed the quota listed in the IsolatedStorageFilePermission granted to your assembly. Listing 29.1 shows an example of code that saves and loads data in isolated storage.

Listing 29.1 Example of Using IsolatedStorage
 using System; using System.IO.IsolatedStorage; using System.IO; class IsolatedStorageExample {   public static void Main()   {     IsolatedStorageFile isf = IsolatedStorageFile.GetStore( IsolatedStorageScope.Assembly graphics/ccc.gif IsolatedStorageScope.User, null, null);     IsolatedStorageFileStream isfs = new IsolatedStorageFileStream("file1", FileMode. graphics/ccc.gif OpenOrCreate, isf);     StreamWriter sw = new StreamWriter(isfs);     sw.WriteLine("This data is contained in isolated storage.");     sw.Close();     isfs = new IsolatedStorageFileStream("file1", FileMode.Open, isf);     StreamReader sr = new StreamReader(isfs);     Console.WriteLine(sr.ReadToEnd());     sr.Close();   } } 

There are more things you can do with the IsolatedStorageFile and IsolatedStorageFileStream objects. For example, you can create directories or enumerate files with IsolatedStorageFile objects. See the .NET Framework SDK for a complete listing of methods on these objects.

Cryptography

While isolated storage can hide your data from other semi-trusted applications, it can't do anything about protecting the data if it is discovered by a fully trusted application or malicious user. That's where cryptography can help you. For example, you could encrypt data with a password provided by an application user. That way, even though the sensitive data might be discovered , nothing could actually understand what it found unless it had the password.

Cryptography functionality is provided in the System.Security.Cryptography namespace. Chapter 30, "Using Cryptography with the .NET Framework: The Basics," Chapter 31, "Using Cryptography with the .NET Framework: Advanced Topics," and Chapter 32, "Using Cryptography with the .NET Framework: Creating and Verifying XML Digital Signatures," go into great detail about using the cryptography classes in the .NET Framework.

Data Stored in Memory

Something you may not think about at first is that data you store in memory while executing may be valuable . You cannot determine what other code will be running in your process when you write an application, so some precautions might need to be taken when you are executing.

There are two types of protection that you can take for in-memory data. The first is to properly design your application to disallow other semi-trusted code from trivially reading or writing data in your classes. The second is to hold truly secret data in memory as little as possible and to clean it up when disposing of it.

Application Design ”Visibility, Inheritance, and Boxing

Depending on what your code does, it may actually be run in process with other semi-trusted .NET Framework code. Because of this, proper application design can affect the security of your application. Three specific things to look at are visibility, inheritance, and boxing.

Visibility of fields, properties, methods, classes, and so on in your application's code could potentially be a security problem. For example, if you have a field in a public class that is supposed to hold a user's password, and that field is public, other semi-trusted code can easily read the password from that field. If some element in your code doesn't need to be public, don't make it public. If it doesn't need to be visible to subclasses, make it completely private.

One caveat with visibility is that even properly designed visibility of types in an application cannot protect against code that is granted the ReflectionPermission with the MemberAccess flag or the SecurityPermission with the SerializationFormatter flag. Any applications granted the ReflectionPermission with the MemberAccess flag can read and write to private fields, call private methods, and create instances of private classes. Applications that were granted the SecurityPermission with the SerializationFormatter permission can read or write private fields of classes if they can serialize them to a data blob, change that data blob, and deserialize it back to the class. These are considered powerful permissions, so they aren't granted to any Internet or intranet code by default.

Inheritance can have detrimental effects on security if it isn't considered while designing an application. All security-sensitive parts of your code need to make sure that they recognize two things. First, if your classes don't prevent subclassing, malicious code may subclass you. Second, objects returned from methods may be subclasses of the type in the method signature. Inheritance is simply a language feature. However, if you have a class that is security sensitive, think about what happens if someone subclasses it and overrides critical methods. Can malicious code bypass security checks? Similarly, consider what happens if someone subclasses some classes that you use. If you aren't careful, subtle security issues can arise.

One final issue to consider regarding securely storing data in memory is boxing. Boxing allows value types, such as structs, enumerations, and integral types, to be treated as objects. The .NET Framework will covert value types to the object type and vice versa when necessary. For example, you can assign an integer to an object value and the .NET Framework will perform the conversion. This can actually be a security problem in classes with fields of the object type. If you have an object field that is storing a value type, and you hand out that object to some other code, the field's contents can be changed at will by anyone who has that object. Listing 29.2 shows an example of such a problem.

Listing 29.2 Security Problem with Object Fields and Boxing
 using System; using System.Reflection; public class ClassWithObjectField {   private object badField;   public ClassWithObjectField (int value) {     badField = value;   }   public ClassWithObjectField (Uri value) {     badField = value;   }   public Object SomeValue {     get {       return badField;     }   }   public void PrintValue() {     Console.WriteLine(badField);   }   public static void ChangeInt(ref int i) {     i = i * 2;   }   public static void ChangeUri(ref Uri u) {     u = new Uri("http://www.msn.com");   }   public static void Main() {     ClassWithObjectField intTest = new ClassWithObjectField(123);     ClassWithObjectField uriTest = new ClassWithObjectField(new Uri("http://www.microsoft. graphics/ccc.gif com"));     Console.WriteLine("Initial values");     intTest.PrintValue();  // Prints "123"     uriTest.PrintValue();  // Prints "http://www.microsoft.com"     object intObject = intTest.SomeValue;     typeof(ClassWithObjectField).GetMethod("ChangeInt").Invoke(null, new Object[] { graphics/ccc.gif intObject } );     object uriObject = uriTest.SomeValue;     typeof(ClassWithObjectField).GetMethod("ChangeUri").Invoke(null, new Object[] { graphics/ccc.gif uriObject } );     Console.WriteLine("After attempt to modify values");     intTest.PrintValue(); // Prints "246"     uriTest.PrintValue(); // Prints "http://www.microsoft.com"   } } 

Basically, through all parts of software construction, it is important to think about how malicious code might attack your application. There are implementation details covered throughout the book, but don't forget security during the design phase!

Clearing Memory After Use

If you have to use secret data (like an encryption key) during execution, it is best to wipe your copy of the data before releasing it. Under normal circumstances, there aren't any threats about this. However, there are two cases where leaving the data can be scary. The first is where your data crashes and a debugger holds onto the dying process or saves the process memory to disk. In this case, the secret can be easily found. The second case to consider is where a totally separate process is running and reading memory from other processes. While both cases are not mainstream scenarios, they could unintentionally expose secret data in memory.

There are some mitigating factors related to clearing memory in semi-trusted .NET applications:

  • Only non- verifiable managed code or unmanaged code could directly perform this type of attack.

  • This requires a situation where highly trusted malicious code runs on the same machine as the application dealing with the valuable data.

  • Finding secrets in memory is not always easy to do, even if an application has the means to search.

Unfortunately, there is one major problem with clearing memory in a semi-trusted application. Once a managed variable holds a value, you can never directly clear the memory or know when it has been reliably cleared. Remember that the .NET Framework uses garbage collection. At any given time, a collection may occur that could move the variable in memory. The old location for the variable will still contain a copy of the data. Because of the problems involved with trying to clear managed memory, if you need to store a secret in memory that you want to clear out, you will need to do it in unmanaged code.

Because of the mitigating factors involved with discovering secrets in the memory space of other processes, you probably don't need to do worry about this in most cases. Just remember that there is a remote threat of secrets being discovered when they are stored plainly in memory.

Data Sent on the Network

One other situation during which your data can be exposed is when you send it on the network. This has been known for a long time, but passwords and other important data are still sent plainly on the network with commonly used applications. Sometimes this is part of a protocol, like Telnet. For any new applications, though, this should be avoided.

Generally, the only way to solve the problem of sending data on the network is by using cryptography in some fashion. The .NET Framework has built-in support for SSL when using the System.Net.WebRequest and System.New.WebResponse classes. See the .NET Framework SDK for more details on this. In addition to SSL, you could use classes from the System.Security.Cryptography namespace to encrypt data to go onto the network. However, you should be very careful if you don't use a standard protocol. You could easily open yourself up to attacks with a mistake in the protocol design or implementation. This is a field where experts often propose designs that are later found to have weaknesses, so don't assume you can just put some protocol together and have it be used securely.

for RuBoard


. NET Framework Security
.NET Framework Security
ISBN: 067232184X
EAN: 2147483647
Year: 2000
Pages: 235

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net