Skip to content

Send HSTS from the security headers component without preload - #33

Merged
TungYenTsai merged 1 commit into
mainfrom
fix/security-headers
Nov 6, 2026
Merged

TungYenTsai merged 1 commit into
mainfrom
fix/security-headers

Conversation

@TungYenTsai

Copy link
Copy Markdown
Collaborator

執行規劃 PR-5(S7)第一部分:HSTS。CSP 回報端點另開 PR。

原本 HSTS 由 ArrayHelper::getAttributesMigration() 以 header() 送出,這是比對資料差異的輔助函式,因此只有修改使用者、設定、候選人配置/資料、群組等存檔請求才帶 HSTS,而且帶有難以撤回的 preload 與 includeSubDomains。同一處也送出多餘的 Content-Type。

修改:

  • 移除 ArrayHelper 中的兩個 header()。
  • SecurityHeadersBootstrap 在 APP_ENV=production 時,每個回應(含檔案下載)送出 Strict-Transport-Security: max-age=31536000。
  • 不加 preload。includeSubDomains 改為 HSTS_INCLUDE_SUBDOMAINS=true 才開啟,確認同網域所有子網域皆為 HTTPS 後再設定;HSTS_MAX_AGE 可調整,設為 0 停用。
  • 瀏覽器會忽略經 HTTP 收到的 HSTS,因此不判斷連線是否為 HTTPS,反向代理終結 TLS 時也能正常送出。
  • 開發與測試環境不送 HSTS。.env.example 與 CHANGELOG 已更新。

部署影響:正式環境改為每個回應都帶 HSTS(一年)。曾收到舊標頭的瀏覽器會保留 includeSubDomains 直到原有效期結束;之後的回應不再帶此參數,不會擴大範圍。

驗證(dev6,PHP 8.2.35,HEAD d1cc6e1):

  • SecurityHeadersBootstrapTest 10 tests/18 assertions 通過(新增 6 項);新案例在修改前的程式上有 4 項失敗。
  • ArrayHelperTest 37 tests/89 assertions 通過。
  • Acceptance 129 tests/379 assertions 通過。
  • 完整 unit/functional 以本 PR CI 為準;Gitleaks 未發現問題。

🤖 Generated with Claude Code

HSTS was emitted by a data-diff helper, so only some save actions sent
it, and it carried preload, which is hard to undo. Send it on every
production response instead, drop preload, and make includeSubDomains
opt-in until every subdomain is confirmed to be HTTPS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@TungYenTsai
TungYenTsai merged commit f1b37dd into main Nov 6, 2026
2 checks passed
@TungYenTsai
TungYenTsai deleted the fix/security-headers branch November 6, 2026 05:25
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