Tác giả: Abdul Rahman (Bhai)
Cập nhật: 7 ngày trước
Mục lục
Mục lục
Chúng ta sẽ làm gì?
Bạn nghĩ ứng dụng của mình an toàn vì bạn tự viết code? Hãy nghĩ lại. Trung bình, 80% ứng dụng của bạn đến từ các dependency bên thứ ba – và mỗi dependency đều là một rủi ro bảo mật tiềm ẩn. Trong bài viết này, chúng ta sẽ khám phá quản lý dependency và Hóa đơn Phần mềm (SBOM) trong .NET, chỉ cho bạn cách giành lại quyền kiểm soát chuỗi cung ứng phần mềm của mình.
Tại sao chúng ta cần làm điều này?
Chuỗi cung ứng phần mềm đang bị tấn công. Còn nhớ Log4j năm 2021 không? Một lỗ hổng duy nhất trong thư viện logging được sử dụng rộng rãi đó đã xâm phạm hàng triệu ứng dụng trên toàn thế giới – và các tổ chức vẫn đang tìm thấy các trường hợp vào năm 2025. Tại sao? Bởi vì hầu hết các công ty không biết những thành phần nào đang bị chôn sâu trong cây dependency của họ.
Đây là thực tế khắc nghiệt: các thành phần bên thứ ba có thể bị các tác giả bỏ rơi, bị hacker xâm nhập, hoặc đơn giản là chứa các lỗ hổng chưa được phát hiện. Cuộc tấn công EventStream năm 2018 cho thấy cách một kẻ tấn công có thể dễ dàng chiếm quyền một package phổ biến và inject code đánh cắp bitcoin. SolarWinds chứng minh rằng ngay cả các nhà cung cấp đáng tin cậy cũng có thể phân phối phần mềm bị xâm nhập.
Nhưng đây là điều khiến các đội bảo mật mất ngủ: transitive dependencies – các dependency của dependency của bạn. Thư viện PDF của bạn có thể đang sử dụng Log4j mà bạn không hề hay biết. Bạn không chỉ chịu trách nhiệm cho 5 package bạn trực tiếp tham chiếu, mà còn cho hơn 75 package mà chúng kéo theo.
Chính phủ Mỹ hiện yêu cầu SBOM cho bất kỳ phần mềm nào được bán hoặc cho thuê cho các cơ quan liên bang. Đây không phải là quan liêu – mà là sự công nhận rằng bảo mật chuỗi cung ứng là cơ sở hạ tầng quan trọng. Nếu bạn không thể trả lời “có gì trong phần mềm của tôi?” trong vòng vài phút sau khi một lỗ hổng được tiết lộ, bạn đã quá muộn.
Làm thế nào để thực hiện?
Bảo mật chuỗi dependency của bạn đòi hỏi một cách tiếp cận có hệ thống. Hãy xây dựng một khuôn khổ quản trị hoàn chỉnh mang lại cho bạn khả năng hiển thị, kiểm soát và phản hồi nhanh chóng.
Hiểu rõ Bối cảnh Rủi ro
Trước khi triển khai các giải pháp, hãy hiểu bạn đang bảo vệ chống lại điều gì. Các dependency bên thứ ba đối mặt với một số mối đe dọa quan trọng:
Thành phần bị bỏ rơi: Các nhà phát triển chuyển sang dự án khác. Các dự án bị bỏ bê. Khi một thành phần bị bỏ rơi, lỗi không bao giờ được sửa, lỗ hổng vẫn không được vá và khả năng tương thích với các framework mới hơn bị giảm sút. Chạy các thành phần .NET Core 3 trên .NET 8 không chỉ rủi ro – .NET Core 3 đã ngừng hỗ trợ trong nhiều năm.
Package bị xâm nhập: Kẻ tấn công có thể inject mã độc vào các package hợp pháp thông qua nhiều vectơ:
- Tiếp quản các dự án bị bỏ rơi với các đóng góp trông có vẻ xác thực
- Xâm nhập pipeline build của nhà cung cấp (như SolarWinds)
- Xuất bản các package typosquatting với tên tương tự
- Social engineering các người bảo trì package để lấy quyền truy cập
Xung đột giấy phép: Sử dụng thành phần có giấy phép GPL trong phần mềm độc quyền của bạn? Bạn có thể bị yêu cầu mở mã nguồn toàn bộ ứng dụng của mình. Hầu hết các nhà phát triển không bao giờ kiểm tra giấy phép cho đến khi quá muộn.
Lỗ hổng được tiết lộ: Khi một CVE được công bố, kẻ tấn công ngay lập tức bắt đầu quét các nạn nhân. Cuộc đua bắt đầu – bạn có thể vá nhanh hơn chúng khai thác không?
Bước 1: Thiết lập Quản trị Thành phần
Không còn việc tùy tiện lấy package từ NuGet. Mỗi thành phần phải trải qua quy trình kiểm duyệt trước khi vào codebase của bạn.
Tạo Bản ghi Quyết định Kiến trúc (ADR): Ghi lại lý do bạn cần thành phần này. Một ADR nên bao gồm:
# ADR: Thêm Thư viện Thanh toán Stripe.NET
## Trạng thái
Đề xuất
## Bối cảnh
Chúng ta cần xử lý thanh toán thẻ tín dụng một cách an toàn. Tự xây dựng xử lý thanh toán
sẽ tốn kém và rủi ro do yêu cầu tuân thủ PCI DSS.
## Yếu tố quyết định
- Yêu cầu tuân thủ PCI DSS
- Cần xử lý thanh toán sẵn sàng cho production
- Trải nghiệm nhà phát triển và chất lượng tài liệu
- Tính ổn định và hỗ trợ lâu dài của nhà cung cấp
## Các lựa chọn được xem xét
1. Stripe.NET
2. Braintree SDK
3. PayPal Checkout SDK
## Ưu và nhược điểm
### Stripe.NET
- ✅ 54 triệu lượt tải (được chứng minh)
- ✅ Phát triển tích cực (phát hành hàng tuần)
- ✅ Tài liệu toàn diện
- ✅ Giấy phép MIT (tương thích với code độc quyền của chúng ta)
- ✅ Không có lỗ hổng nào được biết trong 2 năm qua
- ✅ Chương trình tiết lộ bảo mật mạnh mẽ
- ❌ Yêu cầu tài khoản merchant Stripe
### Quyết định
Chọn Stripe.NET dựa trên mức độ áp dụng cộng đồng, lịch sử bảo mật,
và khả năng tương thích giấy phép.
## Hệ quả
- Nhóm phát triển cần đào tạo về API Stripe
- Yêu cầu đánh giá dependency hàng tháng
- Phải theo dõi thông báo bảo mật của Stripe
Phân loại và Kiểm kê: Đánh giá mức độ quan trọng của mỗi thành phần. Một thư viện xử lý thẻ tín dụng được phân loại quan trọng; một tiện ích tạo mã QR được ưu tiên thấp hơn.
public enum ComponentCriticality
{
Critical, // Xử lý PII, auth, payments - yêu cầu vá ngay lập tức
High, // Logic nghiệp vụ cốt lõi - vá trong vòng 48 giờ
Medium, // Tính năng hỗ trợ - vá trong vòng 1 tuần
Low // Tính năng có thì tốt - vá trong chu kỳ bình thường
}
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; }
}
Bước 2: Quy trình Đánh giá Bảo mật
Trước khi phê duyệt bất kỳ thành phần nào, chạy nó qua các kiểm tra bảo mật toàn diện:
1. Kiểm tra Cơ sở dữ liệu CVE: Tìm kiếm các lỗ hổng đã biết. Ngay cả khi lỗ hổng tồn tại, chúng có thể không áp dụng cho trường hợp sử dụng của bạn – nhưng bạn cần biết về chúng.
# Kiểm tra lỗ hổng trong dự án của bạn
dotnet list package --vulnerable
# Kiểm tra package lỗi thời
dotnet list package --outdated
2. Chạy Phân tích Tĩnh: Nếu có mã nguồn, chạy các công cụ SAST đối với nó.
3. Đánh giá Dự án: Truy cập repository GitHub. Tìm kiếm:
- Commit gần đây: Phát triển tích cực là dấu hiệu tốt
- Thời gian giải quyết issue: Người bảo trì phản hồi lỗi nhanh thế nào?
- Chính sách bảo mật: Họ có tiết lộ có trách nhiệm không?
- Quy mô cộng đồng: Nhiều sao/fork = nhiều người xem code
- Dependencies: Transitive dependencies nào sẽ được kéo theo?
4. Xác thực Giấy phép: Đảm bảo nó tương thích với giấy phép phần mềm của bạn. Sử dụng AI để giúp giải thích các điều khoản pháp lý phức tạp nếu cần.
5. Nghiên cứu OSINT: Tìm kiếm bất kỳ sự cố bảo mật nào liên quan đến nhà cung cấp hoặc thành phần. Họ đã từng bị xâm nhập trước đây chưa? Họ đã phản ứng thế nào?
Bước 3: Tạo và Duy trì SBOM
Hóa đơn Phần mềm (SBOM) là bản kiểm kê hoàn chỉnh của bạn – mọi thành phần, mọi phiên bản, mọi transitive dependency. Đó là nền tảng của bảo mật chuỗi cung ứng.
Những gì một SBOM chứa:
- Tên và phiên bản thành phần
- Thông tin nhà cung cấp/nhà xuất bản
- Chi tiết giấy phép
- Quan hệ dependency
- Hash mật mã để xác minh
Tạo SBOM với Công cụ Miễn phí của Microsoft:
# Cài đặt công cụ SBOM
dotnet tool install --global Microsoft.Sbom.DotNetTool
# Tạo SBOM ở định dạng SPDX JSON
sbom-tool generate -b /path/to/build/output -bc /path/to/project -pn "MyApp" -pv "1.0.0" -ps "MyCompany" -nsb https://mycompany.com
# Ví dụ tại thư mục gốc của dự án của bạn
sbom-tool generate -b ./ -bc ./ -pn ILoveDotNet -pv 1.0.0 -ps ILoveDotNet -pm
# Công cụ tạo manifest với hash mật mã để phát hiện giả mạo
SBOM được tạo trông như thế này (đơn giản hóa):
{
"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"
}
]
}
Tích hợp Tạo SBOM vào CI/CD: Tự động hóa việc tạo SBOM. Mỗi build nên tạo ra một SBOM mới, đã ký.
# Ví dụ Pipeline Azure DevOps
- 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'
Bước 4: Triển khai Private Package Repository
Không bao giờ kéo dependencies trực tiếp từ NuGet công cộng trong production builds. Thay vào đó, duy trì một private repository với các package đã được kiểm duyệt, phê duyệt.
Tại sao Private Repository quan trọng:
- Kiểm soát: Bạn quyết định khi nào cập nhật, không phải các tác giả package
- Ổn định: Package không thể biến mất khỏi NuGet và phá vỡ build của bạn
- Bảo mật: Quét package trước khi chúng vào môi trường của bạn
- Tuân thủ: Duy trì lịch sử kiểm toán của các thành phần đã được phê duyệt
Thiết lập với Azure Artifacts:
# Tạo feed trong Azure DevOps Artifacts
# Sau đó cấu hình NuGet.Config của bạn để sử dụng nó
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<!-- Private feed only - chặn truy cập trực tiếp vào NuGet công cộng -->
<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>
Quy trình Kiểm duyệt:
- Nhà phát triển yêu cầu thành phần qua ADR
- Đội bảo mật chạy đánh giá (kiểm tra CVE, SAST, đánh giá giấy phép)
- Các package đã được phê duyệt được tải lên private feed
- Nhà phát triển có thể tham chiếu thành phần
- Đánh giá thường xuyên đảm bảo package luôn cập nhật
Bước 5: Xác định Chiến lược Cập nhật và SLA
Không phải mọi cập nhật package đều cần triển khai ngay lập tức. Xác định Thỏa thuận Cấp độ Dịch vụ (SLA) dựa trên mức độ nghiêm trọng của lỗ hổng:
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, // Khai thác tích cực, RCE, bypass auth
High, // Tác động bảo mật đáng kể, chưa có khai thác được biết
Medium, // Rủi ro vừa phải, có sẵn biện pháp giảm nhẹ
Low // Rủi ro tối thiểu hoặc yêu cầu điều kiện khó xảy ra
}
Lịch trình Cập nhật Thường xuyên: Đối với các cập nhật không phải bảo mật, thiết lập nhịp độ:
- Đánh giá hàng tháng: Kiểm tra phiên bản mới của các thành phần quan trọng
- Cập nhật hàng quý: Cập nhật các thành phần mức độ quan trọng trung bình
- Đánh giá hàng năm: Đánh giá lại tất cả lựa chọn thành phần
Chờ trước khi Cập nhật: Đừng nhảy ngay vào phiên bản mới nhất. Cho các bản phát hành mới 2-4 tuần để loại bỏ các lỗi đã lọt qua kiểm tra của nhà cung cấp.
Bước 6: Tự động hóa Giám sát Lỗ hổng
Thiết lập quét tự động trong pipeline CI/CD của bạn:
# Ví dụ GitHub Actions
name: Security Scan
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
# Chạy hàng ngày lúc 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 build nếu phát hiện lỗ hổng critical hoặc high
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/
Bước 7: Quản lý NuGet Package Manager trong Visual Studio
NuGet Package Manager của Visual Studio là công cụ tiền tuyến của bạn để quản lý dependency. Đây là cách sử dụng nó hiệu quả:
Xem Các Package Đã Cài đặt: Điều hướng đến Tools > NuGet Package Manager > Manage NuGet Packages for Solution. Tab Installed hiển thị tất cả các dependency trực tiếp cùng với transitive dependencies. Lưu ý 5 package trực tiếp có thể kéo theo 75+ transitive dependencies.
Nhận diện Package Có Lỗ hổng: Visual Studio làm nổi bật các package có lỗ hổng đã biết, hiển thị mức độ nghiêm trọng (Critical, High, Medium, Low). Nhấp vào biểu tượng cảnh báo để xem chi tiết CVE và hành động được đề xuất.
Ví dụ: Newtonsoft.Json 12.0.1
⚠️ Phát hiện Lỗ hổng Mức độ Cao
CVE-2024-12345: Xác thực Đầu vào Không đúng
Đề xuất: Cập nhật lên phiên bản 13.0.3 hoặc mới hơn
Cập nhật Package An toàn: Tab Updates hiển thị các bản cập nhật có sẵn. Đừng cập nhật mọi thứ một cách mù quáng – xem release notes trước. Các thay đổi phiên bản chính có thể giới thiệu các breaking changes.
Cấu hình Private Feeds: Trong Tools > Options > NuGet Package Manager > Package Sources, thêm private feed của bạn và xóa hoặc vô hiệu hóa public NuGet.org để ngăn cài đặt package chưa được phê duyệt.
Ví dụ Thực tế: Phản ứng với Log4j
Khi Log4Shell (CVE-2021-44228) được tiết lộ vào tháng 12 năm 2021, các tổ chức có quản lý dependency đúng đắn đã phản hồi trong vài giờ. Những tổ chức không có? Nhiều tổ chức vẫn đang dọn dẹp vào năm 2025.
Với SBOM và Quản trị:
- Chạy:
grep -r "log4j" sbom.json - Ngay lập tức xác định tất cả các ứng dụng bị ảnh hưởng
- Kéo phiên bản đã vá từ private repository (đã được kiểm duyệt)
- Triển khai bản vá khẩn cấp sử dụng pipeline CI/CD hiện có
- Tổng thời gian: 4-6 giờ
Không có SBOM và Quản trị:
- Tìm kiếm thủ công qua hàng trăm ứng dụng
- Kiểm tra xem có sử dụng thư viện Java không (không chỉ sử dụng Log4j trực tiếp)
- Tải bản vá từ repository công cộng (nó có an toàn không? ai biết?)
- Thử nghiệm bản vá đối với từng ứng dụng riêng lẻ
- Triển khai thủ công vì không có tự động hóa
- Tổng thời gian: Vài ngày đến vài tuần (và bạn vẫn bỏ lỡ một số trường hợp)
Các Công cụ và Tùy chọn Bổ sung
Ngoài công cụ SBOM của Microsoft, hãy xem xét các lựa chọn thay thế này:
- Syft: Trình tạo SBOM mã nguồn mở hỗ trợ nhiều ngôn ngữ
- CycloneDX: Chuẩn SBOM hiện đại với hỗ trợ tiết lộ lỗ hổng
- GitHub Dependency Graph: Tạo SBOM tự động cho các repository GitHub
- OWASP Dependency-Check: Xác định các lỗ hổng đã biết trong dependencies của dự án
# Sử dụng Syft để tạo SBOM
syft packages dir:/path/to/project -o spdx-json > sbom.spdx.json
# Sử dụng CycloneDX với .NET
dotnet tool install --global CycloneDX
dotnet CycloneDX /path/to/project.csproj -o /output -j
Thẩm định Giấy phép
Đừng bỏ qua giấy phép. Một thành phần có giấy phép GPL trong phần mềm độc quyền có thể tạo ra trách nhiệm pháp lý. Các giấy phép mã nguồn mở phổ biến:
- MIT/Apache 2.0: Cho phép, an toàn cho sử dụng thương mại
- BSD: Cho phép với một số hạn chế
- GPL/LGPL: Giấy phép copyleft – yêu cầu các tác phẩm phái sinh sử dụng cùng giấy phép
- AGPL: Giống GPL nhưng áp dụng cho các dịch vụ mạng
Khi nghi ngờ, sử dụng AI để giải thích các điều khoản giấy phép: “Giải thích giấy phép GPL 3.0 ảnh hưởng thế nào đến ứng dụng .NET độc quyền của tôi sử dụng thư viện này.”
Tổng kết
Quản lý dependency không phải là tùy chọn – đó là cơ sở hạ tầng quan trọng. Mỗi thành phần bên thứ ba là một vectơ tấn công tiềm năng, và bạn chịu trách nhiệm cho tất cả chúng, bao gồm cả transitive dependencies.
Xây dựng khuôn khổ quản trị: kiểm duyệt các thành phần với ADR, duy trì một private repository, tạo SBOM tự động và xác định SLA rõ ràng để vá các lỗ hổng. Khi sự cố quy mô Log4j tiếp theo xảy ra, bạn sẽ phản hồi trong vài giờ, không phải vài tuần.
Chuỗi cung ứng đang bị tấn công. Câu hỏi không phải là liệu bạn có bị nhắm mục tiêu hay không – mà là liệu bạn đã sẵn sàng chưa. Bắt đầu với một SBOM, thiết lập quản trị, và không bao giờ tin tưởng một dependency một cách mù quáng nữa.
Tags: Bảo mật, SBOM, Hóa đơn Phần mềm, Quản lý Dependency, Bảo mật Chuỗi Cung ứng, Quản trị Thành phần, NuGet, Quét Lỗ hổng, Private Repository, Dependencies Bên thứ ba, Log4j, .NET



