In older versions of Windows, it was common for regular users to run with administrative privileges. In fact, most Windows applications made the assumption that the user was a member of the Administrators group and therefore had unrestricted access to the entire system. This has changed with the User Account Control (UAC) system, which limits access to critical system resources, even when the user has administrative privileges.
Least Privilege Applications
Whenever you attempt to install an application, copy files in a protected folder, modify the registry or change the system configuration, UAC will block the operation and prompt the user to confirm the action being taken. When UAC is enabled, all applications effectively run with restricted privileges, regardless of the groups the current user is a member of.
As a software developer, the most significant change you need to make is with the general assumption that you have control over the entire system. For example, you may presume that you can create files in the same directory where your application is installed, or that you can create keys anywhere in the registry. You may also assume that you can do things like register ActiveX components, install message hooks or attach to another process. All of these things are now restricted, and unlike previous versions of Windows, telling the user that they are required to have administrative privileges won’t resolve the issue. The UAC system is specifically designed to limit access to system resources, even when the user is an administrator.
The most important things to keep in mind are:
1. Do not assume that you can create files in the same directory as the application. You will not have privilege to create files under C:\Program Files or C:Windows. This is a particularly important because many applications create data and configuration files in their installation folder.
2. Store shared application data and configuration files in the common AppData folder. This is typically the C:\ProgramData folder. For per-user data and configuration files, store them in the user’s AppData folder, typically C:\Users\UserName\AppData\Roaming. Keep in mind that you should never make an assumption about what the actual path is. Use the SHGetFolderPath function that is part of the shell API to determine the correct location.
3. Never assume that you can create registry keys anywhere other than HKEY_CURRENT_USER. Access to other parts of the registry is generally read-only. Any information that your program needs to store in the registry should be stored under HKEY_CURRENT_USER\Software\Company\AppName.
4. Don’t request more privilege than required when accessing a file or registry key. For example, if you are only reading a file, don’t attempt to open it with read-write access. The same applies to the registry; don’t open a key with KEY_ALL_ACCESS rights if you’re only going to read a key value. Request only those privileges that you need at the time that you need them.
5. Never make the assumption that a particular Windows API function will always succeed. Functions which have “always worked” under older versions of Windows may fail under Windows 10 due to access restrictions. For example, installing message filters or system-wide hooks can fail because the process doesn’t have the appropriate access rights. Always check the return values and practice defensive coding. Because it worked under previous versions of Windows is no guarantee that it will work correctly under Windows 10.
What do you do about applications that have already been written which don’t follow those rules? For example, what if your program creates INI files in the same folder as the executable, or stores configuration data under HKEY_LOCAL_MACHINE\Software in the registry? The good news is that your application will probably work correctly. This is because Windows includes support for user virtualization of specific parts of the filesystem and the registry.
When your program attempts to create or modify a file under C:\Program Files or C:\Windows it is automatically redirected to the user’s virtual store, which is typically C:\Users\UserName\AppData\Local\VirtualStore. If you are modifying a file that already exists, a copy of it will be made in the virtual store for the user. If you are creating a new file, it will only exist in the user’s virtual store. This happens transparently, and as far as your program is concerned there has been no change in the file path.
The system also virtualizes registry access under the HKEY_LOCAL_MACHINE\Software key. If your program creates or modifies a key value there, it will be automatically redirected to HKEY_CURRENT_USER\Software\Classes\VirtualStore\Machine\Software. As with the filesystem, this is completely transparent to your application.
It is also important to be aware of the potential side-effects of user virtualization. While many applications will be able to run without issue, some will fail unexpectedly. This will typically affect programs which store licensing information in the registry or implement anti-piracy methods like embedding license keys in the executable after installation. Virtualization can also create the appearance of a flaw in your software. For example, if you store global configuration data in the registry under HKEY_LOCAL_MACHINE, any configuration changes that are made will always be per-user because of virtualization. If user “A” makes a change, it will be virtualized to their registry hive and users “B” and “C” will not see those changes. Any changes made by user “B” will not be seen by user “A” or “C” and so on. This can cause users to think that the configuration changes they’re making aren’t persisting correctly, or can cause more serious problems such as the wrong database being opened, files not being created in the correct location and so forth.
Although user virtualization does help lessen the impact of the security changes in Windows, it is not intended to be used with new software. Microsoft has explicitly stated that developers should not depend on virtualization for two specific reasons. First, virtualization is a configurable option. By default it is enabled, but a system administrator can choose to disable it. Second, it is not guaranteed that it will be supported in future versions of Windows. Virtualization exists solely to help developers during a transition period, ensuring that legacy applications will continue to run. It is not a feature developers should count on in the future.