Files
coreality-inc/app/Http/Middleware/AddCspNonceToScripts.php
ericli1018 6dfe3e0677 release
2025-06-13 12:24:35 +08:00

86 lines
3.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class AddCspNonceToScripts
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
// 只處理 HTML 響應
if ($response->headers->get('Content-Type') === 'text/html; charset=UTF-8') {
$content = $response->getContent();
// 儲存提取出的樣式內容
$styles = [];
$styleCounter = 0;
// 第一步:處理內聯樣式,將 style 轉換為 class
$content = preg_replace_callback(
'/<([a-z][a-z0-9]*)\b([^>]*)style=["\']([^"\']+)["\']([^>]*)>/i',
function ($matches) use (&$styles, &$styleCounter) {
$tag = $matches[1]; // 標籤名稱,例如 div
$beforeAttrs = trim($matches[2]); // style 前的屬性
$styleContent = $matches[3]; // 內聯樣式內容
$afterAttrs = trim($matches[4]); // style 後的屬性
// 生成唯一的 class 名稱
$className = 'inline-style-' . $styleCounter++;
$styles[$className] = $styleContent;
// 組合新的標籤,保留其他屬性並添加 class
$newAttrs = $beforeAttrs . ($beforeAttrs ? ' ' : '') . 'class="' . $className . '"' . ($afterAttrs ? ' ' : '') . $afterAttrs;
return '<' . $tag . ' ' . trim($newAttrs) . '>';
},
$content
);
// 第二步:為 <script> 和 <style> 標籤添加 nonce
$content = preg_replace_callback(
'/<(script|style)\b([^>]*)>(.*?)<\/\1>/is',
function ($matches) {
$tag = $matches[1];
$attributes = $matches[2];
$contentInside = $matches[3];
if (strpos($attributes, 'nonce=') !== false) {
return $matches[0];
}
$nonce = csp_nonce();
return '<' . $tag . ' ' . $attributes . ' nonce="' . $nonce . '">' . $contentInside . '</' . $tag . '>';
},
$content
);
// 第三步:將提取的樣式追加為 <style> 標籤
if (!empty($styles)) {
$styleBlock = '<style nonce="' . csp_nonce() . '">' . PHP_EOL;
foreach ($styles as $className => $styleContent) {
$styleBlock .= '.' . $className . ' {' . $styleContent . '}' . PHP_EOL;
}
$styleBlock .= '</style>';
// 將樣式塊插入到 </head> 之前(如果有 head否則插入到文件末尾
$content = str_contains($content, '</head>')
? str_replace('</head>', $styleBlock . '</head>', $content)
: $content . $styleBlock;
}
$response->setContent($content);
}
return $response;
}
}