Skip to content

Commit 2473892

Browse files
committed
feat: Mod 下载校验 SHA256,文件损坏或被篡改时拒绝安装
1 parent 6d30f0b commit 2473892

1 file changed

Lines changed: 24 additions & 4 deletions

File tree

ChuChartManager/Controllers/ModController.cs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public ActionResult<ModStatus> GetStatus()
4343
private const string AppleChuAsset = "AppleChu.dll";
4444

4545
public record GitHubRelease(string Tag_name, GitHubAsset[] Assets);
46-
public record GitHubAsset(string Name, string Browser_download_url);
46+
public record GitHubAsset(string Name, string Browser_download_url, string? Digest);
4747
public record VersionInfo(string Latest, string Installed, string DownloadUrl);
4848

4949
private static readonly HttpClient Http = new();
@@ -92,12 +92,16 @@ public async Task<ActionResult> InstallLoader([FromBody] InstallRequest? request
9292

9393
var binPath = Path.Combine(gamePath, "bin");
9494
var loaderData = await Http.GetByteArrayAsync(loaderUrl);
95+
if (!VerifyDigest(loaderData, FindAssetDigest(release, loaderUrl)))
96+
return BadRequest("version.dll 校验失败,文件可能已损坏或被篡改");
9597
await System.IO.File.WriteAllBytesAsync(Path.Combine(binPath, "version.dll"), loaderData);
9698

9799
var proxyUrl = release?.Assets.FirstOrDefault(a => a.Name == ProxyAsset)?.Browser_download_url;
98100
if (!string.IsNullOrEmpty(proxyUrl))
99101
{
100102
var proxyData = await Http.GetByteArrayAsync(proxyUrl);
103+
if (!VerifyDigest(proxyData, FindAssetDigest(release, proxyUrl)))
104+
return BadRequest("d3d9.dll 校验失败,文件可能已损坏或被篡改");
101105
await System.IO.File.WriteAllBytesAsync(Path.Combine(binPath, "d3d9.dll"), proxyData);
102106
}
103107

@@ -113,19 +117,19 @@ public async Task<ActionResult> InstallAppleChu([FromBody] InstallRequest? reque
113117

114118
var binPath = Path.Combine(gamePath, "bin");
115119

120+
var release = await GetLatestRelease(AppleChuRepo, AppleChuAsset);
116121
var url = request?.Url;
117122
if (string.IsNullOrEmpty(url))
118-
{
119-
var release = await GetLatestRelease(AppleChuRepo, AppleChuAsset);
120123
url = release?.Assets.FirstOrDefault(a => a.Name == AppleChuAsset)?.Browser_download_url;
121-
}
122124
if (string.IsNullOrEmpty(url))
123125
return NotFound("No release found");
124126

125127
if (!IsAllowedDownloadUrl(url))
126128
return BadRequest("下载 URL 不在白名单内");
127129

128130
var data = await Http.GetByteArrayAsync(url);
131+
if (!VerifyDigest(data, FindAssetDigest(release, url)))
132+
return BadRequest("AppleChu.dll 校验失败,文件可能已损坏或被篡改");
129133
var modsDir = Path.Combine(binPath, "mods");
130134
Directory.CreateDirectory(modsDir);
131135
await System.IO.File.WriteAllBytesAsync(Path.Combine(modsDir, "AppleChu.dll"), data);
@@ -151,6 +155,22 @@ private static bool IsAllowedDownloadUrl(string? url)
151155
&& uri.AbsolutePath.StartsWith("/MuNET-OSS/", StringComparison.OrdinalIgnoreCase);
152156
}
153157

158+
private static string? FindAssetDigest(GitHubRelease? release, string url)
159+
{
160+
return release?.Assets.FirstOrDefault(a => a.Browser_download_url == url)?.Digest;
161+
}
162+
163+
private static bool VerifyDigest(byte[] data, string? digest)
164+
{
165+
// 旧 Release 资产没有 digest 字段时跳过校验
166+
if (string.IsNullOrEmpty(digest)) return true;
167+
if (!digest.StartsWith("sha256:", StringComparison.OrdinalIgnoreCase)) return true;
168+
169+
var expected = digest["sha256:".Length..];
170+
var actual = Convert.ToHexString(System.Security.Cryptography.SHA256.HashData(data));
171+
return string.Equals(actual, expected, StringComparison.OrdinalIgnoreCase);
172+
}
173+
154174
private static bool IsValidModId(string modId)
155175
{
156176
return !string.IsNullOrEmpty(modId) && System.Text.RegularExpressions.Regex.IsMatch(modId, @"^[A-Za-z0-9_-]+$");

0 commit comments

Comments
 (0)