Private NuGet feeds are one of those pieces of infrastructure that every .NET shop has, nobody thinks about, and everybody misconfigures in roughly the same ways. I have spent a couple of months this year hardening the private feeds at two different customers and I want to write down the pattern, because the Microsoft documentation assumes you know what "secure" means already and the internet advice is half out of date. Getting this right is boring, incremental work, but getting it wrong produces the exact dependency-confusion failure modes that have been hitting npm shops for three years.
The feed types you actually see
Private NuGet feeds come in three flavours in the wild. The first is Azure Artifacts, which is the Microsoft-hosted option integrated with Azure DevOps. The second is on-premises JFrog Artifactory or Sonatype Nexus, both of which have decent NuGet support that is not quite on par with their npm or Maven support. The third is a small fleet of niche options including ProGet, Gitea's package registry, and BaGet, which is the open-source NuGet server reference implementation.
The threat model is broadly the same across all three. A feed is a location where packages are stored and served, which means it is a target for write-side attacks (publish a malicious package, get it consumed by internal builds) and read-side attacks (tamper with packages in transit or in the backing store). Hardening means making the write-side hard to abuse, making the read-side verifiable, and making the boundary between the feed and the public nuget.org ecosystem explicit.
Authentication on writes
The single most common finding I see is feeds where the write scope is configured too loosely. Azure Artifacts, for instance, lets you grant feed-level Contributor permissions that allow anyone in the group to push packages. In one customer I found a feed where the Contributor role was granted to the entire All Developers group, and anyone in the company could publish a package to the feed that would then be consumed by production builds. Nobody had abused it, but nothing would have stopped them.
The hardening is to restrict feed write access to a specific service principal per publishing pipeline, scoped to exactly the packages that pipeline owns. Azure DevOps supports views and fine-grained permissions that let you do this. Artifactory's permissions model is richer but more complex; Nexus requires more plumbing through its security realms. The minimum bar is that no human identity should be able to push packages to a production-consumed feed. If a human needs to push a package, they should do it through a pipeline with approval controls.
Signature enforcement on reads
NuGet's signature model provides author and repository signatures. Private feeds can be configured to repository-sign every package they serve, which is analogous to what nuget.org does, and consuming projects can require that signature via <trustedSigners> in nuget.config.
The hardening is to enable feed-side repository signing for all three major feed types. Azure Artifacts does not yet do this out of the box, and customers who want it typically sign packages in the publishing pipeline instead. Artifactory has a repository-level signing capability that requires certificate provisioning but works. Nexus has a similar feature in its Pro edition. Once the feed signs, consuming projects should pin the feed's certificate thumbprint in trustedSigners and require signatures on restore.
Upstream proxying
Every private feed also acts as a proxy for nuget.org in most setups. A restore hits the private feed, the private feed checks its local cache, and on a miss it fetches from nuget.org and caches the result. This pattern is fine in principle but has subtle failure modes.
The first failure mode is that some proxies fetch and cache without preserving the repository signature from nuget.org. The fetched package makes it into the cache with no signature chain, and downstream consumers have no way to verify the package came from nuget.org. Both Artifactory and Nexus have configurations that get this right and configurations that do not; the hardening step is to verify in practice by downloading a cached package and inspecting its signature chain against the original on nuget.org.
The second failure mode is the dependency confusion pattern. If the private feed serves both internal packages and proxied nuget.org packages from the same URL, and a developer or CI pipeline asks for a package ID, the feed's resolution order determines which gets served. If an attacker publishes a package with the same ID as an internal package to nuget.org, and the proxy's ordering prefers nuget.org, the attacker wins. This was the exact shape of the dependency confusion research Alex Birsan published in February 2021, and while the NuGet community responded with packageSourceMapping in NuGet 6.0, the feed-side configurations to prevent the proxy from serving the wrong package are less well known.
The hardening is to split private feeds into two views: one that serves only internal packages and one that serves only the proxied public cache. Consuming projects configure packageSourceMapping to route internal package ID prefixes to the internal view and everything else to the public view. The ambiguity goes away.
Vulnerability scanning of feed contents
A feed is also a place where vulnerable package versions accumulate over time. A package that was the current version in 2021 but has been superseded by a version that fixes a CVE is still on the feed and still resolvable if a consuming project pins to it. CVE-2024-21319, the Microsoft.IdentityModel.Tokens vulnerability disclosed in January 2024, affected versions before 7.1.2, and if your feed has cached 6.x versions, those versions are still sitting there waiting to be resolved.
The hardening is to scan the feed's contents against the NuGet advisory database on a schedule, flag packages with known CVEs, and either quarantine them or mark them as deprecated. Azure Artifacts' native vulnerability scanning is limited; Artifactory Xray and Nexus IQ do this; third-party SCA tools that have feed integration can do it too. The mechanism matters less than the fact that somebody is running it.
Retention and immutability
A subtle concern that bit one of my customers was a feed where the retention policy silently deleted old package versions after 90 days. A critical service that built once per year (for a compliance reason) failed its build because a transitive dependency it had pinned was gone. The service had to be frozen while the dependency was re-fetched from nuget.org and re-published to the feed, breaking the chain-of-custody promise the feed was meant to provide.
The hardening is to set the retention policy to preserve every version that has ever been consumed by any build. Azure Artifacts, Artifactory, and Nexus all support retention rules that can be configured this way. The storage cost is real but usually modest.
Incident patterns
The incidents I have actually seen on NuGet private feeds fall into three patterns. The first is a developer workstation compromise where the attacker extracted the developer's PAT and used it to push a trojanised version of an internal package. The second is a build agent misconfiguration where a feed was added to a shared agent's nuget.config and then forgotten, allowing packages from that feed to leak into unrelated builds. The third is a proxy configuration where the feed served a tampered version of a cached public package, usually because of a bug in the proxy's caching logic rather than a deliberate attack.
How Safeguard Helps
Safeguard inventories every private NuGet feed configured across a tenant and enumerates its package contents, which surfaces the drift between what each feed actually serves and what the consuming projects believe is there. It flags cached packages missing signatures, detects ID collisions between internal and public namespaces, and evaluates packageSourceMapping coverage so dependency confusion exposure is measurable. For the vulnerable-package-in-cache pattern the platform scans feed contents against the NuGet advisory database daily and routes findings to the feed owner rather than to the consuming team, which is where the fix has to happen.