Secure memory

Purpose

If an attacker can retrieve sensitive data, like keys, passwords, and plaintexts, they can effectively bypass the associated cryptography. Therefore, it is recommended to limit the exposure of and protect such sensitive data in memory, reducing the likelihood of it being accessible to attackers.

Here are several steps you should take as a developer:

  1. Use byte or char arrays over strings. Strings are immutable, meaning they can't reliably be erased and copies may be made. SecureStringarrow-up-right is also not worth usingarrow-up-right.

  2. Use stackallocarrow-up-right or GC.AllocateArray()arrow-up-right with pinned: true for allocations. This prevents .NET copying the memory around, allowing proper erasure.

  3. Use ZeroMemory() to wipe arrays in a way that won't be optimised away by the compiler. This should be done as soon as the array is no longer needed and when an exception occurs. For example, you can call this method in a try-finallyarrow-up-right statement.

  4. Use LockMemory() to try and avoid data swapping to disk.

  5. Use guarded heap allocations, which take care of the above plus limit access to the memory at an operating system level. This protects against buffer overflows/underflows and other processes accessing the data.

  6. Use a library like memguardarrow-up-right (a Go library, but a C# equivalent is in the works) that encrypts memoryarrow-up-right whilst using guarded heap allocations. This does all of the above whilst protecting against things like cold boot attacksarrow-up-right and speculative execution vulnerabilitiesarrow-up-right.

  7. Support using hardware that stores secrets and performs cryptographic operations on your behalf, like YubiKeysarrow-up-right.

circle-check

Usage

ZeroMemory

Overwrites a byte array with zeros.

circle-exclamation

Exceptions

ArgumentOutOfRangeExceptionarrow-up-right

buffer has a length of 0.

ZeroMemory

Overwrites a char array or string with zeros.

circle-exclamation

Exceptions

ArgumentOutOfRangeExceptionarrow-up-right

buffer has a length of 0.

LockMemory

Locks a byte array to try and avoid the contents being swapped to disk.

circle-exclamation

Exceptions

ArgumentOutOfRangeExceptionarrow-up-right

buffer has a length that's not a multiple of PageSize.

OutOfMemoryExceptionarrow-up-right

Unable to lock memory.

UnlockAndZeroMemory

Overwrites a locked byte array with zeros before unlocking the memory.

circle-exclamation

Exceptions

ArgumentOutOfRangeExceptionarrow-up-right

buffer has a length that's not a multiple of PageSize.

InvalidOperationExceptionarrow-up-right

Unable to unlock memory.

GuardedHeapAllocation

Provides access to guarded heap allocations, which can be used instead of regular allocations for additional security. However, there is a performance penalty, and this functionality SHOULD NOT be used for large amounts of variables/data due to system limits.

triangle-exclamation

Exceptions

ArgumentOutOfRangeExceptionarrow-up-right

size is less than 1 or greater than MaxSize.

OutOfMemoryExceptionarrow-up-right

Unable to allocate memory.

InvalidOperationExceptionarrow-up-right

Unable to make memory inaccessible.

InvalidOperationExceptionarrow-up-right

Unable to make memory read-only.

InvalidOperationExceptionarrow-up-right

Unable to make memory readable and writable.

ObjectDisposedExceptionarrow-up-right

The object has been disposed.

Constants

These are used for validation and/or save you defining your own constants.

Notes

Guarded heap allocations have the following memory layout, with a few caveats:

  • The stored size in the first guard page refers to the canary plus data size rounded up to a multiple of the page size.

  • The length of the stored size in the first guard page depends on the platform, as does the page size.

  • The canary plus data does not necessarily take up a whole page; there may be padding at the beginning (unused memory) and multiple pages (if the data is large enough).

  • The canary is initialized with random bytes, and data is initialized with 0xdb bytes to help catch bugs due to uninitialized data.

  • If access protection is not supported on the platform, the last page contains a canary at the beginning, meaning there can be two canaries with the same value surrounding the data. In this scenario, access to guard pages is not restricted.

Guarded heap allocation layout
circle-exclamation
circle-exclamation
circle-check
circle-check
circle-info

Memory locking uses VirtualLockarrow-up-right on Windows and mlockarrow-up-right with madvisearrow-up-right and MADV_DONTDUMP on Unix.

Guarded heap allocations require 3 or 4 extra pages of memory over a typical allocation. Allocations are done with VirtualAllocarrow-up-right on Windows and mmaparrow-up-right with MAP_NOCORE or posix_memalignarrow-up-right on Unix. VirtualProtectarrow-up-right on Windows and mprotectarrow-up-right on Unix are used for the memory access restrictions.

The protection on Unix is likely superiorarrow-up-right to on Windows.

Last updated