👉🏼 Click here to Join I ❤️ .NET WhatsApp Channel to get 🔔 notified about new articles and other updates.
Improve Security with Dependency Management and SBOM in .NET

Improve Security with Dependency Management and SBOM in .NET

Author - Abdul Rahman (Bhai)

Security

10 Articles

Improve

Table of Contents

  1. What we gonna do?
  2. Why we gonna do?
  3. How we gonna do?
  4. Summary

What we gonna do?

Think your application is secure because you wrote the code yourself? Think again. On average, 80% of your application comes from third-party dependencies—and every single one is a potential security risk. In this article, we'll explore dependency management and Software Bill of Materials (SBOM) in .NET, showing you how to regain control over your software supply chain.

Why we gonna do?

The software supply chain is under attack. Remember Log4j in 2021? That single vulnerability in a widely-used logging library compromised millions of applications worldwide—and organizations are still finding instances in 2025. Why? Because most companies have no idea what components are buried deep in their dependency trees.

Here's the harsh reality: third-party components can be abandoned by their creators, compromised by attackers, or simply contain undiscovered vulnerabilities. The 2018 EventStream attack showed how easily a malicious actor can take over a popular package and inject bitcoin-stealing code. SolarWinds proved that even trusted vendors can distribute compromised software.

But here's what keeps security teams up at night: transitive dependencies—the dependencies of your dependencies. Your PDF library might be using Log4j without you knowing it. You're not just responsible for the 5 packages you directly reference, but also the 75+ packages they pull in.

The US government now requires an SBOM for any software sold or leased to federal agencies. This isn't bureaucracy—it's recognition that supply chain security is critical infrastructure. If you can't answer "what's in my software?" within minutes of a vulnerability disclosure, you're already too late.

How we gonna do?

Securing your dependency chain requires a systematic approach. Let's build a complete governance framework that gives you visibility, control, and rapid response capabilities.

Understanding the Risk Landscape

Before implementing solutions, understand what you're protecting against. Third-party dependencies face several critical threats:

Abandoned Components: Developers move on. Projects get neglected. When a component is abandoned, bugs never get fixed, vulnerabilities remain unpatched, and compatibility with newer frameworks degrades. Running .NET Core 3 components on .NET 8 isn't just risky—.NET Core 3 has been out of support for years.

Compromised Packages: Attackers can inject malicious code into legitimate packages through several vectors:

  • Taking over abandoned projects with authentic-looking contributions
  • Compromising the vendor's build pipeline (like SolarWinds)
  • Publishing typosquatting packages with similar names
  • Social engineering package maintainers to hand over access

Licensing Conflicts: Use a GPL-licensed component in your proprietary software? You might be legally required to open-source your entire application. Most developers never check licenses until it's too late.

Disclosed Vulnerabilities: When a CVE is published, attackers immediately start scanning for victims. The race is on—can you patch faster than they can exploit?

Step 1: Establish Component Governance

No more grabbing packages from NuGet on a whim. Every component must go through a vetting process before it enters your codebase.

Create an Architectural Decision Record (ADR): Document why you need this component. An ADR should include:


# ADR: Adding Stripe.NET Payment Library

## Status
Proposed

## Context
We need to process credit card payments securely. Building our own payment 
processing would be costly and risky given PCI DSS compliance requirements.

## Decision Drivers
- PCI DSS compliance requirements
- Need for production-ready payment processing
- Developer experience and documentation quality
- Long-term vendor stability and support

## Considered Options
1. Stripe.NET
2. Braintree SDK
3. PayPal Checkout SDK

## Pros and Cons

### Stripe.NET
- ✅ 54 million downloads (proven track record)
- ✅ Active development (weekly releases)
- ✅ Comprehensive documentation
- ✅ MIT license (compatible with our proprietary code)
- ✅ No known vulnerabilities in last 2 years
- ✅ Strong security disclosure program
- ❌ Requires Stripe merchant account

### Decision
Selected Stripe.NET based on community adoption, security track record, 
and licensing compatibility.

## Consequences
- Development team needs training on Stripe API
- Monthly dependency review required
- Must monitor Stripe security advisories

Classify and Inventory: Rate each component's criticality. A library handling credit cards gets critical classification; a utility for generating QR codes gets lower priority.


public enum ComponentCriticality
{
    Critical,   // Handles PII, auth, payments - immediate patch required
    High,       // Core business logic - patch within 48 hours
    Medium,     // Supporting features - patch within 1 week
    Low         // Nice-to-have features - patch during normal cycle
}

public class ComponentMetadata
{
    public string Name { get; set; }
    public string Version { get; set; }
    public ComponentCriticality Criticality { get; set; }
    public string License { get; set; }
    public DateTime LastReviewed { get; set; }
    public DateTime NextReviewDue { get; set; }
    public List<string> TransitiveDependencies { get; set; }
    public string VendorSecurityContact { get; set; }
}

Step 2: Security Assessment Process

Before approving any component, run it through comprehensive security checks:

1. Check CVE Databases: Search for known vulnerabilities. Even if vulnerabilities exist, they might not apply to your use case—but you need to know about them.


# Check for vulnerabilities in your project
dotnet list package --vulnerable

# Check for outdated packages
dotnet list package --outdated

2. Run Static Analysis: If source code is available, run SAST tools against it.

3. Review the Project: Visit the GitHub repository. Look for:

  • Recent commits: Active development is a good sign
  • Issue resolution time: How fast do maintainers respond to bugs?
  • Security policy: Do they have responsible disclosure?
  • Community size: More stars/forks = more eyes on the code
  • Dependencies: What transitive dependencies will this bring in?

4. Validate the License: Ensure it's compatible with your software's license. Use AI to help interpret complex legal terms if needed.

5. OSINT Research: Search for any security incidents involving the vendor or component. Have they been breached before? How did they respond?

Step 3: Generate and Maintain an SBOM

A Software Bill of Materials (SBOM) is your complete inventory—every component, every version, every transitive dependency. It's the foundation of supply chain security.

What an SBOM Contains:

  • Component name and version
  • Vendor/publisher information
  • Licensing details
  • Dependency relationships
  • Cryptographic hash for verification

Generate SBOM with Microsoft's Free Tool:


# Install the SBOM tool
dotnet tool install --global Microsoft.Sbom.DotNetTool

# Generate SBOM in SPDX JSON format
sbom-tool generate -b /path/to/build/output -bc /path/to/project -pn "MyApp" -pv "1.0.0" -ps "MyCompany" -nsb https://mycompany.com

# For Example at the root of your project,
sbom-tool generate -b ./ -bc ./ -pn ILoveDotNet -pv 1.0.0 -ps ILoveDotNet -pm

# The tool creates a manifest with cryptographic hash for tamper detection

The generated SBOM looks like this (simplified):


{
  "spdxVersion": "SPDX-2.2",
  "name": "MyApp",
  "packages": [
    {
      "name": "Newtonsoft.Json",
      "versionInfo": "13.0.3",
      "supplier": "Organization: Newtonsoft",
      "licenseConcluded": "MIT",
      "checksums": [
        {
          "algorithm": "SHA256",
          "checksumValue": "abc123..."
        }
      ]
    },
    {
      "name": "System.Text.Json",
      "versionInfo": "8.0.0",
      "supplier": "Organization: Microsoft"
    }
  ],
  "relationships": [
    {
      "relationshipType": "DEPENDS_ON",
      "relatedSpdxElement": "Newtonsoft.Json"
    }
  ]
}

Integrate SBOM Generation into CI/CD: Make SBOM generation automatic. Every build should produce a fresh, signed SBOM.


# Azure DevOps Pipeline example
- task: DotNetCoreCLI@2
  displayName: 'Build Application'
  inputs:
    command: 'build'
    projects: '**/*.csproj'

- task: PowerShell@2
  displayName: 'Generate SBOM'
  inputs:
    targetType: 'inline'
    script: |
      sbom-tool generate `
        -b $(Build.ArtifactStagingDirectory) `
        -bc $(Build.SourcesDirectory) `
        -pn "$(Build.Repository.Name)" `
        -pv "$(Build.BuildNumber)" `
        -ps "MyOrganization"

- task: PublishBuildArtifacts@1
  displayName: 'Publish SBOM'
  inputs:
    PathtoPublish: '_manifest/spdx_2.2'
    ArtifactName: 'sbom'

Step 4: Implement Private Package Repository

Never pull dependencies directly from public NuGet in production builds. Instead, maintain a private repository with vetted, approved packages.

Why Private Repositories Matter:

  • Control: You decide when to update, not package authors
  • Stability: Packages can't disappear from NuGet and break your builds
  • Security: Scan packages before they enter your environment
  • Compliance: Maintain audit trail of approved components

Setup with Azure Artifacts:


# Create a feed in Azure DevOps Artifacts
# Then configure your NuGet.Config to use it

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <clear />
    <!-- Private feed only - blocks direct access to public NuGet -->
    <add key="MyCompanyFeed" 
         value="https://pkgs.dev.azure.com/myorg/_packaging/myfeed/nuget/v3/index.json" />
  </packageSources>
  <packageSourceCredentials>
    <MyCompanyFeed>
      <add key="Username" value="AzureDevOps" />
      <add key="ClearTextPassword" value="%AZURE_DEVOPS_PAT%" />
    </MyCompanyFeed>
  </packageSourceCredentials>
</configuration>

Vetting Workflow:

  1. Developer requests component via ADR
  2. Security team runs assessment (CVE check, SAST, license review)
  3. Approved packages are uploaded to private feed
  4. Developers can now reference the component
  5. Regular reviews ensure packages stay current

Step 5: Define Update Strategy and SLAs

Not every package update needs immediate deployment. Define Service Level Agreements (SLAs) based on vulnerability severity:


public class PatchingSLA
{
    public static readonly Dictionary<VulnerabilitySeverity, TimeSpan> ResponseTimes = new()
    {
        { VulnerabilitySeverity.Critical, TimeSpan.FromHours(24) },
        { VulnerabilitySeverity.High, TimeSpan.FromHours(48) },
        { VulnerabilitySeverity.Medium, TimeSpan.FromDays(7) },
        { VulnerabilitySeverity.Low, TimeSpan.FromDays(30) }
    };
}

public enum VulnerabilitySeverity
{
    Critical,  // Active exploitation, RCE, auth bypass
    High,      // Significant security impact, no known exploits yet
    Medium,    // Moderate risk, mitigations available
    Low        // Minimal risk or requires unlikely conditions
}

Regular Update Schedule: For non-security updates, establish a cadence:

  • Monthly reviews: Check for new versions of critical components
  • Quarterly updates: Update medium-criticality components
  • Annual reviews: Re-evaluate all component choices

Wait Before Updating: Don't jump on the latest version immediately. Give new releases 2-4 weeks to shake out bugs that slipped past the vendor's testing.

Step 6: Automate Vulnerability Monitoring

Set up automated scanning in your CI/CD pipeline:


# GitHub Actions example
name: Security Scan

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
  schedule:
    # Run daily at 2 AM
    - cron: '0 2 * * *'

jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup .NET
        uses: actions/setup-dotnet@v3
        with:
          dotnet-version: '8.0.x'
      
      - name: Restore dependencies
        run: dotnet restore
      
      - name: Check for vulnerable packages
        run: |
          dotnet list package --vulnerable --include-transitive 2>&1 | tee vulnerability-scan.txt
          
          # Fail the build if critical or high vulnerabilities are found
          if grep -i "critical\|high" vulnerability-scan.txt; then
            echo "Critical or high vulnerabilities detected!"
            exit 1
          fi
      
      - name: Generate SBOM
        run: |
          dotnet tool install --global Microsoft.Sbom.DotNetTool
          sbom-tool generate -b ./bin -bc ./ -pn MyApp -pv 1.0.0 -ps MyOrg
      
      - name: Upload SBOM
        uses: actions/upload-artifact@v3
        with:
          name: sbom
          path: _manifest/spdx_2.2/

Step 7: Managing the NuGet Package Manager in Visual Studio

Visual Studio's NuGet Package Manager is your frontline tool for dependency management. Here's how to use it effectively:

View Installed Packages: Navigate to Tools > NuGet Package Manager > Manage NuGet Packages for Solution. The Installed tab shows all direct dependencies plus transitive dependencies. Notice how 5 direct packages might pull in 75+ transitive dependencies.

Identify Vulnerable Packages: Visual Studio highlights packages with known vulnerabilities, showing severity levels (Critical, High, Medium, Low). Click the warning icon to see CVE details and recommended actions.


Example: Newtonsoft.Json 12.0.1
⚠️ High Severity Vulnerability Detected
CVE-2024-12345: Improper Input Validation
Recommendation: Update to version 13.0.3 or later

Update Packages Safely: The Updates tab shows available updates. Don't blindly update everything—review release notes first. Major version changes can introduce breaking changes.

Configure Private Feeds: In Tools > Options > NuGet Package Manager > Package Sources, add your private feed and remove or disable public NuGet.org to prevent unapproved package installation.

Real-World Example: The Log4j Response

When Log4Shell (CVE-2021-44228) was disclosed in December 2021, organizations with proper dependency management responded in hours. Those without it? Many are still cleaning up in 2025.

With SBOM and Governance:

  1. Run: grep -r "log4j" sbom.json
  2. Immediately identify all affected applications
  3. Pull patched version from private repository (already vetted)
  4. Deploy emergency patch using existing CI/CD pipeline
  5. Total time: 4-6 hours

Without SBOM and Governance:

  1. Manually search through hundreds of applications
  2. Check if any use Java libraries (not just direct Log4j usage)
  3. Download patch from public repository (is it safe? who knows?)
  4. Test patch against each application individually
  5. Deploy manually because no automation exists
  6. Total time: Days to weeks (and you still miss some instances)

Additional Tools and Options

Beyond Microsoft's SBOM tool, consider these alternatives:

  • Syft: Open-source SBOM generator supporting multiple languages
  • CycloneDX: Modern SBOM standard with vulnerability disclosure support
  • GitHub Dependency Graph: Automatic SBOM generation for GitHub repositories
  • OWASP Dependency-Check: Identifies known vulnerabilities in project dependencies

# Using Syft to generate SBOM
syft packages dir:/path/to/project -o spdx-json > sbom.spdx.json

# Using CycloneDX with .NET
dotnet tool install --global CycloneDX
dotnet CycloneDX /path/to/project.csproj -o /output -j

Licensing Due Diligence

Don't overlook licensing. A GPL-licensed component in proprietary software can create legal liability. Common open-source licenses:

  • MIT/Apache 2.0: Permissive, safe for commercial use
  • BSD: Permissive with some restrictions
  • GPL/LGPL: Copyleft licenses—require derivative works to use same license
  • AGPL: Like GPL but applies to network services too

When in doubt, use AI to interpret licensing terms: "Explain how the GPL 3.0 license affects my proprietary .NET application that uses this library."

Summary

Dependency management isn't optional—it's critical infrastructure. Every third-party component is a potential attack vector, and you're responsible for all of them, including transitive dependencies.

Build a governance framework: vet components with ADRs, maintain a private repository, generate SBOMs automatically, and define clear SLAs for patching vulnerabilities. When the next Log4j-scale incident hits, you'll respond in hours, not weeks.

The supply chain is under attack. The question isn't whether you'll be targeted—it's whether you'll be ready. Start with an SBOM, establish governance, and never trust a dependency blindly again.

👉🏼 Click here to Join I ❤️ .NET WhatsApp Channel to get 🔔 notified about new articles and other updates.
  • Security
  • SBOM
  • Software Bill of Materials
  • Dependency Management
  • Supply Chain Security
  • Component Governance
  • NuGet
  • Vulnerability Scanning
  • Private Repository
  • Third-Party Dependencies
  • Log4j
  • Security
  • .NET