Mark of the Web

edit

Windows records where a downloaded file comes from in an NTFS alternate data stream named Zone.Identifier: the security zone, and the URLs the file was downloaded from. This is the Mark of the Web. Windows uses it to warn the user before opening the file, and to block macros and scripts from the Internet. MarkOfTheWeb from the Meziantou.Framework.Win32.MarkOfTheWeb package reads and writes it.

dotnet package add Meziantou.Framework.Win32.MarkOfTheWeb

Read and write the Mark of the Web

var path = Path.GetTempFileName();

// Add the Mark of the Web to a file
MarkOfTheWeb.SetFileZone(path, UrlZone.Internet, referrerUrl: "https://example.com/", hostUrl: "https://example.com/file.zip");

// Get the zone Windows assigns to a file
var zone = MarkOfTheWeb.GetFileZone(path);
Assert.Equal(UrlZone.Internet, zone);

// Check whether a file comes from the Internet or Restricted Sites zones
Assert.True(MarkOfTheWeb.IsUntrusted(path));

// Read what the Zone.Identifier stream records
var zoneIdentifier = MarkOfTheWeb.GetFileZoneIdentifier(path); // null when the file has no Mark of the Web
Assert.NotNull(zoneIdentifier);
Assert.Equal(UrlZone.Internet, zoneIdentifier.Zone);
Assert.Equal("https://example.com/file.zip", zoneIdentifier.HostUrl);
Assert.Equal("https://example.com/", zoneIdentifier.ReferrerUrl);

// Read the raw content of the Zone.Identifier stream
var content = MarkOfTheWeb.GetFileZoneContent(path);
Assert.Equal("""
    [ZoneTransfer]
    ZoneId=3
    ReferrerUrl=https://example.com/
    HostUrl=https://example.com/file.zip

    """, content, ignoreLineEndingDifferences: true);

// Remove the Mark of the Web from a file
MarkOfTheWeb.RemoveFileZone(path);
Assert.Null(MarkOfTheWeb.GetFileZoneIdentifier(path));
MethodDescription
SetFileZoneWrites the Zone.Identifier stream with the zone, and optionally the referrer URL and the host URL
GetFileZoneReturns the zone Windows assigns to the file, or UrlZone.Invalid when it cannot be determined
IsUntrustedReturns true when the file comes from the Internet or Restricted Sites zones, or when Windows cannot evaluate its zone
GetFileZoneIdentifierReturns the content of the stream, parsed, or null when the file has no Mark of the Web
GetFileZoneContentReturns the raw content of the stream, or null when the file has no Mark of the Web
RemoveFileZoneDeletes the stream. It does nothing when the file has no Mark of the Web

Make a security decision

Use GetFileZone or IsUntrusted to decide whether to trust a file. They ask Windows for the zone of the file, which takes the security settings of the machine into account. GetFileZoneIdentifier and GetFileZoneContent report what the stream records, which is not always the zone Windows applies.

The zones are:

UrlZoneZone
LocalMachineFiles on the local computer
IntranetAddresses on the local network
TrustedTrusted Sites
InternetThe Internet, for the addresses that belong to no other zone
UntrustedRestricted Sites

Windows can also report a custom zone, numbered 1000 and above, that has no named member.

Mark the files your application downloads

An application that downloads files, or extracts them from a downloaded archive, should mark them so Windows protects the user the same way as for the files downloaded by a browser. Copy the Mark of the Web of the archive to the extracted files:

// Give the files extracted from a downloaded archive the Mark of the Web of the archive
var zoneIdentifier = MarkOfTheWeb.GetFileZoneIdentifier(archivePath);
if (zoneIdentifier is { Zone: >= UrlZone.LocalMachine and <= UrlZone.Untrusted })
{
    MarkOfTheWeb.SetFileZone(extractedPath, zoneIdentifier.Zone, zoneIdentifier.ReferrerUrl, zoneIdentifier.HostUrl);
}
  • SetFileZone only accepts the five zones of the table above: another value throws an ArgumentOutOfRangeException.
  • The URLs cannot contain a line break or a null character (ArgumentException). An empty URL is written as no URL.
  • SetFileZone throws a FileNotFoundException when the file does not exist. A directory is not a file.
  • A read-only file is marked too, and stays read-only.

The Mark of the Web is an NTFS feature: on file systems without alternate data streams, such as FAT32, files cannot carry it.

Parse the content of a stream

ZoneIdentifier.Parse reads content in the format of the Zone.Identifier stream, for instance a stream copied from another file or received from another machine:

var zoneIdentifier = ZoneIdentifier.Parse("""
    [ZoneTransfer]
    ZoneId=3
    HostUrl=https://example.com/file.zip
    """);

Assert.Equal(UrlZone.Internet, zoneIdentifier.Zone);
Assert.Equal("https://example.com/file.zip", zoneIdentifier.HostUrl);
Assert.Null(zoneIdentifier.ReferrerUrl);

The names of the section and the keys are case-insensitive. A key that is missing, empty, or repeated with different values is reported as absent: null for the URLs, UrlZone.Invalid for the zone.