侧边栏壁纸
博主头像
seems 博主等级

学习博客

  • 累计撰写 62 篇文章
  • 累计创建 41 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Jeecg前端3.0处理CSP内容安全策略

seems
2024-03-11 / 0 评论 / 0 点赞 / 9 阅读 / 0 字

1.Vite插件(vitePlugins.push(cspPlugin(isBuild));)

csp.ts

import type { Plugin } from 'vite';

export function cspPlugin(mode: boolean): Plugin[] {
  // mode 执行方式:true为打包 ,false为本地运行
  const nonce = mode ? '__SERVER-GENERATED-NONCE__' : 'nonce';
  // const nonce = mode ? 'UzIeWf1T3ifABGK' : 'nonce';

  const regexScript = /<script(.*?)/gi;
  const replacementScript = `<script nonce="${nonce}"$1`;

  const regexStyle = /<style(.*?)/gi;
  const replacementStyle = `<style nonce="${nonce}"$1`;

  const regexLink = /<link(.*?)/gi;
  const replacementLink = `<link nonce="${nonce}"$1`;

  const fs = require('fs');
  if (fs.existsSync('log.txt')) fs.rmSync('log.txt');
  if (fs.existsSync('log1.txt')) fs.rmSync('log1.txt');
  if (fs.existsSync('log2.txt')) fs.rmSync('log2.txt');
  // something
  const viteClientRegex = /node_modules\/vite\/dist\/client\/client\.mjs$/gi;
  // antd-vue dynamicCss version 4.1.1
  const antdvCsp = /node_modules\/ant-design-vue\/es\/vc-util\/Dom\/dynamicCss\.js$/gi;
  // antd icons-vue version 3.3.11
  const iconRegex = /node_modules\/@ant-design\/icons-vue\/es\/utils\.js$/gi;
  // vite-plugin-theme version 0.8.6
  const themeRegex = /node_modules\/@rys-fe\/vite-plugin-theme\/es\/client\.js$/gi;
  // vite-plugin-svg-icons version 4.5.2
  const svgRegex = /svg-icons-register/gi;

  const transformStr: Plugin = {
    name: 'transform-file',
    transform(src, id) {
      // fs.appendFileSync('log.txt', '----------------' + id + '\r\n' + src + '\r\n' + '----------' + '\r\n', function (err) {
      //   if (err) {
      //     return console.log(err);
      //   }
      //   console.log('The file was saved!');
      // });
      if (viteClientRegex.test(id)) {
        return {
          code: src.replace(
            "style.setAttribute('data-vite-dev-id', id);",
            `style.setAttribute('data-vite-dev-id', id); style.setAttribute('nonce', '${nonce.toString()}');`
          ),
        };
      }
      if (antdvCsp.test(id)) {
        return {
          code: src.replace('  const {\n    csp,', `option.csp = {nonce:"__SERVER-GENERATED-NONCE__"};  const {\n    csp,`),
        };
      }
      if (iconRegex.test(id)) {
        return {
          code: src.replace('csp.value', `csp && csp.value ? csp.value : {nonce:"__SERVER-GENERATED-NONCE__"}`),
        };
      }
      if (themeRegex.test(id)) {
        return {
          code: src
            .replace("style.setAttribute('id', id);", `style.setAttribute('id', id);style.setAttribute("nonce", "__SERVER-GENERATED-NONCE__");`)
            .replace('styleDom.innerHTML = cssText;', "styleDom.innerHTML = cssText;styleDom.setAttribute('nonce', '__SERVER-GENERATED-NONCE__');"),
        };
      }
      if (svgRegex.test(id)) {
        return {
          code: src
            .replace(
              "svgDom.setAttribute('xmlns:link','http://www.w3.org/1999/xlink');",
              "svgDom.setAttribute('xmlns:link','http://www.w3.org/1999/xlink');svgDom.setAttribute('nonce','__SERVER-GENERATED-NONCE__');"
            )
            .replaceAll(/<symbol([\s\S]*?)><\/symbol>/g, (args) => {
              return (
                args
                  .replaceAll(/<style>/g, '<style nonce=\\"__SERVER-GENERATED-NONCE__\\">')
                  .replaceAll(/style=(\\)+"([^"]*fill):([^;>]*)/g, '$2=\\"$3\\"')
                  // .replaceAll(/style=\\"[^"]*fill:(#[0-9a-fA-F]+)[^"]*"/g, 'fill=\\"$1\\"')
                  .replaceAll(/style=(\\)+"([^"]*opacity):([^;>]*)/g, '$2=\\"$3\\"')
              );
            }),
        };
      }
    },
  };
  const createTag: Plugin = {
    name: 'html-inject-nonce-into-script-tag',
    enforce: 'post',
    transformIndexHtml(html) {
      return html.replace(regexScript, replacementScript).replace(regexStyle, replacementStyle).replace(regexLink, replacementLink);
    },
  };
  return [transformStr, createTag];
}

2.Nginx配置(include/csp.conf)

csp.conf

userid on;
set $cspNonce 'UzIeWf1T3ifABGK';
if ($http_cookie ~ "((?<=^.{4}).{4})" ){
    set $cspNonce $1;
}
if ($http_cookie ~ "((?<=^.{12}).{4})" ){
    set $cspNonce $cspNonce$1;
}
if ($http_cookie ~ "((?<=^.{17}).{4})" ){
    set $cspNonce $cspNonce$1;
}
if ($http_cookie ~ "((?<=^.{10}).{4})" ){
    set $cspNonce $cspNonce$1;
}
if ($cspNonce ~ "([^%&',;=?$\x22]+)" ){
    set $cspNonce $1;
}
sub_filter __SERVER-GENERATED-NONCE__ $cspNonce;
sub_filter_types application/javascript;
sub_filter_once off;
add_header Content-Security-Policy "default-src 'self';script-src 'strict-dynamic' 'unsafe-eval' 'nonce-$cspNonce';style-src 'self' 'nonce-$cspNonce';img-src 'self' data: ; object-src 'self';font-src 'self' data:; frame-ancestors 'self';connect-src 'self'";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";  
#proxy_set_header Accept-Encoding '';

其它可行方式(未测试)

监听htmlElment的创建,并设置nonce

  <script nonce="UzIeWf1T3ifABGK">
    var container = document.querySelector("head")
    var config = {childList: true}
    // 当观察到变动时执行的回调函数
    const callback = function (mutationsList, observer) {
      // Use traditional 'for loops' for IE 11
      for (let mutation of mutationsList) {
        if (mutation.type === "childList") {
          if (mutation.addedNodes && mutation.addedNodes[0]) {
            mutation.addedNodes[0].setAttribute("nonce", "UzIeWf1T3ifABGK");
          }
          // console.log("A child node has been added or removed.");
        } else if (mutation.type === "attributes") {
          console.log("The " + mutation.attributeName + " attribute was modified.");
        }
      }
    };
    // 创建一个观察器实例并传入回调函数
    const observer = new MutationObserver(callback);
    // 以上述配置开始观察目标节点
    observer.observe(container, config);
  </script>
0

评论区