Skip to content

Security hardening for authentication, uploads, and public endpoints#3895

Draft
bbingz wants to merge 85 commits into
siteserver:masterfrom
bbingz:security/huwang-hardening-2026
Draft

Security hardening for authentication, uploads, and public endpoints#3895
bbingz wants to merge 85 commits into
siteserver:masterfrom
bbingz:security/huwang-hardening-2026

Conversation

@bbingz
Copy link
Copy Markdown

@bbingz bbingz commented May 30, 2026

Summary

This draft PR contains a security hardening pass across authentication, authorization, password storage, upload handling, preview rendering, editor XSS filtering, public STL endpoints, public SMS endpoints, login and search rate-limit counter concurrency, security response headers, request/form size limits, cloud token storage, SSRF-sensitive downloads, SQL-sensitive template filters, raw STL where-clause validation, regex-sensitive search highlighting, ZIP extraction safety, plugin upload/install staging, cloud restore object-key validation, Docker example secrets, public source-map exposure, public UEditor sample config exposure, STL include recursion limiting, one-time SMS code consumption, signed trigger replay limiting, and NuGet/npm dependency posture.

Key changes include:

  • tighten default web security settings, TLS validation, JWT handling, request/form size limits, and baseline response security headers
  • store newly created and changed administrator/user passwords with PBKDF2 one-way hashes instead of reversible SM4, while retaining legacy Clear/DES/SM4 verification compatibility and MD5-submission compatibility
  • add authorization checks to database sync, preview rendering, error details, administrator management, site management, content checks/imports, editor upload paths, and V1 lookup/update APIs
  • enable UEditor XSS filter rules for input and output processing instead of shipping the editor with filtering disabled
  • remove reusable secrets from Docker examples, require deployment-provided secret/password environment variables, avoid MySQL root as the application user, require Redis auth in the cluster example, and stop exposing DB/Redis internals on host ports
  • stop publishing source map files under wwwroot and remove public references to the removed maps
  • stop publishing UEditor server config samples and demo pages under wwwroot while keeping the runtime editor config on the backend /common/editor path
  • rate limit high-risk anonymous or token-like public endpoints, including STL rendering/hits/search/dynamic routes and Home/V1 SMS sends
  • synchronize login and STL search rate-limit counter updates per cache key to avoid in-process concurrent read-modify-write bypasses
  • rate limit signed STL trigger requests to reduce replay-driven page-generation DoS impact
  • consume SMS verification codes after successful login, password reset, mobile verification, registration verification, profile mobile change, and V1 form submission
  • store cloud service access tokens in session storage instead of persistent local storage
  • sign STL trigger/page contents/download requests and reject malformed tokens
  • validate uploaded image bytes and uploaded/scrawl payloads
  • constrain path handling for directory containment, form file deletion, and remote downloads
  • validate cloud restore backup IDs and object keys before constructing OSS prefixes or local restore paths
  • cap stl:include recursion depth and restore include parser state with finally after nested parsing
  • reject unsafe ZIP entries that resolve outside the extraction destination, including plugin upload/install and import/restore paths that use PathManager.ExtractZip
  • cap total extracted ZIP size at the upload request limit to reduce ZIP bomb impact across plugin, template, import, and restore paths
  • stage uploaded plugin packages under server-generated random IDs, then install/override from the validated staging directory instead of re-reading a replaceable ZIP path
  • reject unsafe HTTP downloads through both HttpClientUtils and RestUtils, including non-HTTP(S), private, loopback, link-local, reserved-IP, and unsafe redirect targets
  • sanitize STL channel group filters before they are embedded into legacy raw SQL fragments
  • validate raw STL where condition fragments before feeding them into WhereRaw or legacy SQL-string paths, rejecting comments, statement separators, unterminated literals, and compound/mutating SQL keywords
  • escape STL search highlight words before constructing regex patterns
  • invalidate cached JWTs after password changes
  • update vulnerable NuGet package resolutions, replace vulnerable npm build packages, remove unused vulnerable build dependencies, and keep a committed package lock, and verify no currently reported local package vulnerabilities

Verification

Passed:

  • /opt/homebrew/opt/dotnet@8/bin/dotnet test tests/SSCMS.Web.Tests/SSCMS.Web.Tests.csproj --filter "FullyQualifiedName~PublicSourceMapTests|FullyQualifiedName~DockerExampleSecurityTests|FullyQualifiedName~UeditorXssFilterConfigTests|FullyQualifiedName~StlIncludeSafetyTests|FullyQualifiedName~CloudRestoreSafetyTests|FullyQualifiedName~SmsCodeConsumptionSourceTests|FullyQualifiedName~PluginUploadInstallSourceTests|FullyQualifiedName~ZipExtractionSafetyTests|FullyQualifiedName~PasswordHashUtilsTests|FullyQualifiedName~PasswordStorageSourceTests|FullyQualifiedName~SecurityHeadersTests|FullyQualifiedName~CloudTokenStorageTests|FullyQualifiedName~PreviewControllerAuthorizationTests|FullyQualifiedName~HomeSmsRateLimitTests|FullyQualifiedName~ChannelRepositorySqlSafetyTests|FullyQualifiedName~RestUtilsTests|FullyQualifiedName~SitesControllerTests|FullyQualifiedName~AuthManagerPermissionsTests|FullyQualifiedName~AgentControllerTests|FullyQualifiedName~ContentsControllerTests|FullyQualifiedName~RequestSizeLimitTests|FullyQualifiedName~PathManagerUploadImageTests|FullyQualifiedName~UsersControllerTests|FullyQualifiedName~DatabaseManagerSqlSafetyTests|FullyQualifiedName~ActionsDownloadControllerTests|FullyQualifiedName~ActionsHitsControllerTests|FullyQualifiedName~ActionsTriggerControllerTests|FullyQualifiedName~ActionsPublicRateLimitTests|FullyQualifiedName~ActionsPageContentsControllerTests|FullyQualifiedName~AdministratorsControllerTests|FullyQualifiedName~LoginControllerTests|FullyQualifiedName~LostPasswordControllerTests|FullyQualifiedName~FormDataAddControllerTests|FullyQualifiedName~ActionsControllerTests|FullyQualifiedName~Admin.Cms.Editor.EditorControllerTests|FullyQualifiedName~Home.Write.EditorControllerTests|FullyQualifiedName~ContentsLayerAddControllerTests|FullyQualifiedName~ContentImportCheckPermissionTests|FullyQualifiedName~ErrorControllerAuthorizationTests|FullyQualifiedName~StartupAuthenticationTests|FullyQualifiedName~Admin.Settings.Administrators.AdministratorsControllerTests" --no-restore - 118 passed
  • /opt/homebrew/opt/dotnet@8/bin/dotnet test tests/SSCMS.Web.Tests/SSCMS.Web.Tests.csproj --filter "FullyQualifiedName~PublicSourceMapTests" --no-restore - 2 passed
  • /opt/homebrew/opt/dotnet@8/bin/dotnet test tests/SSCMS.Web.Tests/SSCMS.Web.Tests.csproj --filter "FullyQualifiedName~DockerExampleSecurityTests" --no-restore - 5 passed
  • /opt/homebrew/opt/dotnet@8/bin/dotnet test tests/SSCMS.Web.Tests/SSCMS.Web.Tests.csproj --filter "FullyQualifiedName~UeditorXssFilterConfigTests" --no-restore - 2 passed
  • /opt/homebrew/opt/dotnet@8/bin/dotnet test tests/SSCMS.Web.Tests/SSCMS.Web.Tests.csproj --filter "FullyQualifiedName~StlIncludeSafetyTests" --no-restore - 2 passed
  • /opt/homebrew/opt/dotnet@8/bin/dotnet test tests/SSCMS.Web.Tests/SSCMS.Web.Tests.csproj --filter "FullyQualifiedName~CloudRestoreSafetyTests" --no-restore - 8 passed
  • /opt/homebrew/opt/dotnet@8/bin/dotnet test tests/SSCMS.Web.Tests/SSCMS.Web.Tests.csproj --filter "FullyQualifiedName~ActionsTriggerControllerTests" --no-restore - 3 passed
  • /opt/homebrew/opt/dotnet@8/bin/dotnet test tests/SSCMS.Web.Tests/SSCMS.Web.Tests.csproj --filter "FullyQualifiedName~ZipExtractionSafetyTests" --no-restore - 3 passed
  • /opt/homebrew/opt/dotnet@8/bin/dotnet test tests/SSCMS.Web.Tests/SSCMS.Web.Tests.csproj --filter "FullyQualifiedName~SmsCodeConsumptionSourceTests" --no-restore - 8 passed
  • /opt/homebrew/opt/dotnet@8/bin/dotnet test tests/SSCMS.Web.Tests/SSCMS.Web.Tests.csproj --filter "FullyQualifiedName~PluginUploadInstallSourceTests" --no-restore - 2 passed
  • rg --files src/SSCMS.Web/wwwroot | rg '\.map$' - no published source maps remain
  • SSCMS_SECURITY_KEY=00000000-0000-4000-8000-000000000001 SSCMS_DATABASE_PASSWORD=change-me SSCMS_MYSQL_ROOT_PASSWORD=change-root docker compose -f docker/mysql/docker-compose.yml config
  • SSCMS_SECURITY_KEY=00000000-0000-4000-8000-000000000001 SSCMS_DATABASE_PASSWORD=change-me docker compose -f docker/postgres/docker-compose.yml config
  • SSCMS_SECURITY_KEY=00000000-0000-4000-8000-000000000001 SSCMS_REDIS_PASSWORD=change-redis docker compose -f docker/cluster/docker-compose.yml config
  • git diff --check
  • /opt/homebrew/opt/dotnet@8/bin/dotnet build sscms.sln -v:minimal
  • /opt/homebrew/opt/dotnet@8/bin/dotnet list /Users/bing/-Code-/cms-official-security-pr/sscms.sln package --vulnerable --include-transitive - no vulnerable packages reported for all projects

Not fully runnable in this local environment:

  • full SSCMS.Web.Tests includes an integration /api/ping path that currently returns 500 because settingsManager.SecurityKey is null in the test host configuration
  • SSCMS.Tests aborts because AngleSharp.dll is missing from the testhost dependency output
  • SSCMS.Core.Tests direct execution aborts in this environment because Aliyun.OSS.Core.dll is missing from the testhost dependency output

Remaining security work intentionally not mixed into this draft

  • plugin/package execution trust and signature policy should be reviewed as a separate, larger design PR

Update after CI feedback:

  • Skipped Win32 application icons on non-Windows builds so Linux CI does not compile Windows resources from .ico files.

  • Regenerated npm lockfile as v2 so Node 12/npm 6 and Snyk-compatible scanners can consume the committed dependency graph.

  • Updated .NET Docker base images from old/floating tags to supported 8.0-bookworm-slim tags.

  • Removed a committed ASP.NET DataProtection key file and isolated WebApplicationFactory test keys into a temp directory.

  • Regenerated malformed Windows .ico resources that failed .NET 8 Win32 resource compilation.

  • Disabled broken Win32 application icon resource compilation so .NET 8 Windows CI can build.

@bbingz
Copy link
Copy Markdown
Author

bbingz commented May 30, 2026

Security hardening status update (head 45b15be):

  • Added further hardening after the initial PR: pinned nginx base image, aligned ASP.NET test packages to .NET 8.0.27, moved .NET runtime images to explicit non-root chiseled bases, pinned compose service images, fixed Docker example ports for .NET 8 container port 8080, made the web extension manifest auditable with an empty lockfile, pinned System.Security.Cryptography.Xml to patched 8.0.3, added Docker health checks, pinned Dockerfile base images and external compose images by digest, declared the root package license, and moved tests to xUnit v3.
  • Local checks passing: dotnet restore /Users/bing/-Code-/cms-official-security-pr/sscms.sln, dotnet test tests/SSCMS.Web.Tests/SSCMS.Web.Tests.csproj -c Release --no-restore (120/120), root npm audit --json (0), src/SSCMS.Web npm audit --json (0), dotnet list /Users/bing/-Code-/cms-official-security-pr/sscms.sln package --vulnerable --include-transitive (no vulnerable packages), dotnet list /Users/bing/-Code-/cms-official-security-pr/sscms.sln package --deprecated (no deprecated packages), Docker root/core/nginx builds, compose config for mysql/postgres/cluster, core container /api/ping smoke (200 pong), nginx config test with sscms host alias, Trivy image HIGH/CRITICAL scans for root/core/nginx (0), and Trivy Dockerfile config scan across all severities (0).
  • External checks: Azure build 1146 is still pending at the time of this update. security/snyk (starlying) still reports 4 tests have failed; the Snyk target URL redirects to the Snyk login page, and no Snyk token is available in this PR workspace, so the exact four failing Snyk tests are not publicly inspectable from here.

@bbingz
Copy link
Copy Markdown
Author

bbingz commented May 30, 2026

Splitting this broad draft into smaller reviewable PRs.

Opened slices:

This PR can remain as the umbrella branch/evidence bundle for the full HuWang hardening set. The smaller PRs are intended to be reviewed and merged independently where possible.

Current note: #3896 still needs Snyk org visibility because the Snyk check reports private failures without public details. #3897 has Snyk green and is waiting on Azure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant