Skip to main content

How to Resolve Warning "CS0688: 'DerivedMethod' has a link demand, but overrides or implements 'BaseMethod' which does not have a link demand. A security hole may exist." in C#

The Compiler Warning CS0688 is a security consistency warning related to Code Access Security (CAS). The message reads: "'DerivedMethod' has a link demand, but overrides or implements 'BaseMethod' which does not have a link demand. A security hole may exist."

This warning occurs when you implement an interface or override a base method and add a LinkDemand (a specific security check) to the derived method, while the base method allows public access without that check. This creates a security loophole because a malicious caller could cast your secure object to the insecure base type and invoke the method, bypassing your security check entirely.

warning

Legacy Warning: This warning relates to Code Access Security (CAS), which is marked as obsolete in .NET Core and .NET 5+. If you are writing modern .NET code, you should generally remove these attributes entirely as they no longer function as they did in the .NET Framework.

Understanding the Security Hole

LinkDemand is a security check performed during JIT (Just-In-Time) compilation. It checks the immediate caller to ensure they have the required permission.

The problem arises due to Polymorphism:

  1. Interface: void DoWork() (No security check).
  2. Implementation: [LinkDemand] void DoWork() (Requires permission).

If a caller accesses the method via the Interface reference (IWorker worker = new Implementation()), the JIT compiler looks at the security requirements of the Interface. Finding none, it allows the call. The implementation logic then runs, effectively bypassing the [LinkDemand] you placed on the class.

Scenario: Creating the Loophole

In this scenario, a public interface is implemented by a class that tries to restrict access using SecurityAction.LinkDemand.

Example of code with warning

using System;
using System.Security.Permissions;

public interface IDataProcessor
{
// The contract implies this method is safe/open to call.
void Process();
}

public class SecureProcessor : IDataProcessor
{
// ⛔️ Warning CS0688: We are adding a security requirement here...
// ...but because IDataProcessor doesn't have it, callers can bypass this.
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
public void Process()
{
Console.WriteLine("Processing secure data...");
}
}

The Exploit:

public void Exploit(SecureProcessor secureObj)
{
// Calling secureObj.Process() would fail if we don't have UnmanagedCode permission.

// BUT, we can cast it to the interface:
IDataProcessor openObj = secureObj;

// The JIT checks permissions for 'IDataProcessor.Process'.
// It finds none. The call succeeds. Security bypassed.
openObj.Process();
}

Solution 1: Remove the LinkDemand (Modern .NET)

If you are running on .NET Core, .NET 5, 6, 7, or 8+, CAS is deprecated. Attributes like LinkDemand generally have no effect or throw runtime exceptions. The "security" they provide is nonexistent in modern runtimes.

The recommended fix is to remove the attribute.

Solution:

public interface IDataProcessor
{
void Process();
}

public class SecureProcessor : IDataProcessor
{
// ✅ Correct: Remove legacy CAS attributes.
// If you need security, use modern authorization (e.g., ClaimsPrincipal checks).
public void Process()
{
Console.WriteLine("Processing secure data...");
}
}

Solution 2: Secure the Base Interface (Legacy .NET)

If you are maintaining a legacy .NET Framework application and you specifically require the LinkDemand behavior, you must apply the security attribute to the Base Interface (or Base Class) as well. This ensures that the caller is checked regardless of whether they hold a reference to the Interface or the Implementation.

Solution: apply the attribute to the interface.

using System.Security.Permissions;

public interface IDataProcessor
{
// ✅ Correct: The contract now enforces the security check.
// Any caller using IDataProcessor.Process must pass the LinkDemand.
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
void Process();
}

public class SecureProcessor : IDataProcessor
{
// The implementation matches the base security.
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
public void Process()
{
Console.WriteLine("Processing secure data...");
}
}
note

Stack Walk vs. Link Demand: If you cannot modify the interface (e.g., it's from a third-party library), you cannot use LinkDemand on the implementation securely. Consider using SecurityAction.Demand inside the method body instead. Demand performs a full stack walk at runtime, which cannot be bypassed via casting.

Conclusion

CS0688 highlights a flaw in inheritance-based security configuration.

  1. Check the Platform: If using modern .NET, these attributes are obsolete. Remove them.
  2. Verify Consistency: If using legacy .NET Framework, ensure the Base Class or Interface has the same [LinkDemand] attribute as the derived method.
  3. Prevent Bypass: If the base is public/open, the child cannot effectively restrict access using LinkDemand.