From 900c93c6c7ece0c7a90e22d20b73e3e8cc7282b2 Mon Sep 17 00:00:00 2001 From: lkddi Date: Sun, 19 Apr 2026 15:15:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20HTTPS=20=E8=B5=84=E6=BA=90?= =?UTF-8?q?=E9=93=BE=E6=8E=A5=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 1 + app/Providers/AppServiceProvider.php | 16 ++++++++++++++++ config/app.php | 13 +++++++++++++ tests/Feature/SecurityHardeningTest.php | 25 +++++++++++++++++++++++++ 4 files changed, 55 insertions(+) diff --git a/.env.example b/.env.example index 455f761..192eb6a 100644 --- a/.env.example +++ b/.env.example @@ -3,6 +3,7 @@ APP_ENV=local APP_KEY= APP_DEBUG=true APP_URL=http://localhost +APP_FORCE_HTTPS=false APP_LOCALE=en APP_FALLBACK_LOCALE=en diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 844c459..16adf7f 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -17,6 +17,7 @@ use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\Facades\Schema; +use Illuminate\Support\Facades\URL; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Str; @@ -38,6 +39,9 @@ class AppServiceProvider extends ServiceProvider */ public function boot(): void { + // 生产环境按配置强制生成 HTTPS 资源链接,避免反代链路下的 Mixed Content。 + $this->configureSecureUrls(); + // 注册登录入口限流器,阻断爆破和批量注册滥用。 $this->registerAuthRateLimiters(); @@ -73,6 +77,18 @@ class AppServiceProvider extends ServiceProvider } } + /** + * 根据应用配置决定是否统一强制 HTTPS 方案。 + */ + private function configureSecureUrls(): void + { + if (! config('app.force_https')) { + return; + } + + URL::forceScheme('https'); + } + /** * 注册聊天室前台登录与隐藏后台登录的独立限流器。 */ diff --git a/config/app.php b/config/app.php index 6681568..e7d0cab 100644 --- a/config/app.php +++ b/config/app.php @@ -54,6 +54,19 @@ return [ 'url' => env('APP_URL', 'http://localhost'), + /* + |-------------------------------------------------------------------------- + | Force HTTPS + |-------------------------------------------------------------------------- + | + | 某些生产环境会经过 Nginx / CDN / 面板反向代理,应用层未必总能正确识别 + | 原始 HTTPS 协议。开启后会强制所有通过 URL / asset / @vite 生成的资源 + | 使用 https 方案,避免后台页面出现 Mixed Content。 + | + */ + + 'force_https' => (bool) env('APP_FORCE_HTTPS', false), + /* |-------------------------------------------------------------------------- | Trusted Proxies diff --git a/tests/Feature/SecurityHardeningTest.php b/tests/Feature/SecurityHardeningTest.php index ab3edfc..4f2f52e 100644 --- a/tests/Feature/SecurityHardeningTest.php +++ b/tests/Feature/SecurityHardeningTest.php @@ -17,6 +17,7 @@ use App\Events\BannerNotification; use App\Http\Controllers\Admin\BannerBroadcastController; use App\Http\Middleware\CloudflareProxies; use App\Models\User; +use App\Providers\AppServiceProvider; use App\Providers\HorizonServiceProvider; use Illuminate\Broadcasting\PendingBroadcast; use Illuminate\Contracts\Broadcasting\Factory as BroadcastFactory; @@ -24,6 +25,7 @@ use Illuminate\Foundation\Http\Middleware\ValidateCsrfToken; use Illuminate\Http\Request; use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\RateLimiter; +use Illuminate\Support\Facades\URL; use Tests\TestCase; /** @@ -31,6 +33,16 @@ use Tests\TestCase; */ class SecurityHardeningTest extends TestCase { + /** + * 每个测试后重置 URL 生成器的强制协议,避免污染后续用例。 + */ + protected function tearDown(): void + { + URL::forceScheme(null); + + parent::tearDown(); + } + /** * 验证只有来自可信代理的请求才允许采用透传客户端 IP。 */ @@ -169,6 +181,19 @@ class SecurityHardeningTest extends TestCase $this->assertFalse(Gate::forUser($revokedManager)->allows('viewHorizon')); } + /** + * 验证开启强制 HTTPS 配置后,应用生成的资源链接会统一使用 https。 + */ + public function test_app_force_https_makes_generated_asset_urls_use_https(): void + { + config()->set('app.force_https', true); + + $provider = new AppServiceProvider($this->app); + $provider->boot(); + + $this->assertStringStartsWith('https://', url('/build/assets/app.css')); + } + /** * 验证前台登录入口在命中限流后会直接返回 429。 */