CSP策略调整:Cordova应用适配HarmonyOS 5更严格的内容安全规则

116 阅读3分钟

以下为 ​​Cordova应用适配HarmonyOS 5更严格CSP(内容安全策略)的完整方案​​,包含策略配置、代码调整和调试工具:


1. HarmonyOS 5 CSP核心变更

策略项旧版默认值HarmonyOS 5默认值影响分析
default-src'self' *'self'禁止所有跨域资源加载
connect-src*https:仅允许HTTPS接口请求
script-src'unsafe-inline'哈希/非内联禁用内联JS
style-src'unsafe-inline'哈希/非内联禁用内联CSS
media-src*'self' blob:限制媒体资源加载源

2. 基础CSP配置方案

2.1 动态Meta标签注入

// csp-injector.ets
import web_engine from '@ohos.web.webview';

class CSPInjector {
  static setPolicy(webView: web_engine.WebView, policy: string): void {
    const metaTag = `
      <meta http-equiv="Content-Security-Policy" 
        content="${policy}">
    `;
    webView.evaluateJavaScript(`
      const meta = document.createElement('meta');
      meta.httpEquiv = 'Content-Security-Policy';
      meta.content = '${policy}';
      document.head.prepend(meta);
    `);
  }
}

2.2 推荐CSP策略模板

<!-- 适用于Cordova混合应用的策略 -->
<meta http-equiv="Content-Security-Policy" 
  content="
    default-src 'self' https://*.example.com;
    script-src 'self' 'unsafe-eval' 
      https://apis.example.com 
      sha256-基值;
    style-src 'self' 'unsafe-inline';
    img-src 'self' data: blob:;
    connect-src 'self' https://api.example.com 
      wss://realtime.example.com;
    font-src 'self' data:;
    media-src 'self' blob:;
    frame-src 'none';
    worker-src 'none';
">

3. 常见问题修复方案

3.1 内联事件处理器处理

// inline-handler-converter.js
function convertInlineHandlers() {
  document.querySelectorAll('[onclick]').forEach(el => {
    const original = el.getAttribute('onclick');
    el.removeAttribute('onclick');
    el.addEventListener('click', () => {
      new Function(original)(); // 转换为安全的事件监听
    });
  });
}

3.2 动态脚本加载适配

// script-loader.ets
class SecureScriptLoader {
  static load(url: string): Promise<void> {
    return new Promise((resolve, reject) => {
      const script = document.createElement('script');
      script.src = url;
      script.type = 'text/javascript';
      script.onload = () => resolve();
      script.onerror = () => reject();
      document.head.appendChild(script);
    });
  }

  static execute(code: string, nonce?: string): void {
    const script = document.createElement('script');
    if (nonce) script.nonce = nonce;
    script.textContent = code;
    document.head.appendChild(script);
  }
}

4. 开发阶段调试工具

4.1 CSP违规监听器

// csp-violation-listener.ets
class CSPViolationMonitor {
  static start(webView: web_engine.WebView): void {
    webView.addJavascriptInterface(
      'cspViolationReporter',
      (violation) => this._handleViolation(violation)
    );

    webView.evaluateJavaScript(`
      document.addEventListener('securitypolicyviolation', (e) => {
        window.cspViolationReporter(JSON.stringify({
          blockedURI: e.blockedURI,
          violatedDirective: e.violatedDirective,
          originalPolicy: e.originalPolicy
        }));
      });
    `);
  }

  private static _handleViolation(data: string): void {
    const violation = JSON.parse(data);
    console.error(`CSP违规: ${violation.violatedDirective} | 资源: ${violation.blockedURI}`);
  }
}

4.2 策略生成器

// csp-generator.ets
class CSPGenerator {
  static generate(resources: {
    scripts?: string[],
    styles?: string[],
    images?: string[],
    apis?: string[]
  }): string {
    return `
      default-src 'self';
      script-src 'self' ${resources.scripts?.join(' ') || ''};
      style-src 'self' 'unsafe-inline' ${resources.styles?.join(' ') || ''};
      img-src 'self' data: ${resources.images?.join(' ') || ''};
      connect-src 'self' ${resources.apis?.join(' ') || ''};
    `.replace(/\s+/g, ' ').trim();
  }
}

5. 生产环境配置

5.1 分级安全策略

// csp-profiles.json
{
  "development": {
    "script-src": ["'self'", "'unsafe-eval'", "http://localhost:*"],
    "connect-src": ["'self'", "http://*"]
  },
  "production": {
    "script-src": ["'self'", "https://static.example.com"],
    "connect-src": ["'self'", "https://api.example.com"]
  }
}

5.2 非对称加密策略

// csp-signer.ets
import crypto from '@ohos.security.crypto';

class CSPSigner {
  private static keyPair: crypto.KeyPair;

  static async init(): Promise<void> {
    this.keyPair = await crypto.generateKeyPair('RSA2048');
  }

  static async signPolicy(policy: string): Promise<string> {
    const signature = await crypto.sign(
      this.keyPair.privateKey,
      policy
    );
    return `${policy}; signature-sha256=${signature}`;
  }
}

6. 典型适配案例

6.1 第三方地图SDK适配

<!-- 高德地图CSP配置 -->
<meta http-equiv="Content-Security-Policy"
  content="
    script-src 'self' https://webapi.amap.com;
    img-src 'self' https://webapi.amap.com data:;
    connect-src 'self' https://restapi.amap.com;
">

6.2 微信JS-SDK适配

// wechat-sdk-loader.js
function loadWechatSDK() {
  const nonce = generateCSPNonce();
  const script = document.createElement('script');
  script.src = 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js';
  script.nonce = nonce;
  document.head.appendChild(script);
}

7. 关键配置指标

安全等级script-src配置风险指数兼容性
严格'self' 哈希值★★☆☆☆
推荐'self' https: 特定域名★★★☆☆
宽松'self' 'unsafe-inline' 'unsafe-eval'★★★★★

8. 调试与验证

8.1 CSP验证工具

// csp-validator.ets
class CSPValidator {
  static async validate(webView: web_engine.WebView): Promise<Violation[]> {
    return new Promise((resolve) => {
      webView.evaluateJavaScript(`
        const violations = [];
        const observer = new SecurityPolicyViolationObserver(entries => {
          violations.push(...entries);
        });
        observer.observe();
        window.__cspViolations = violations;
      `);
      
      setTimeout(() => {
        webView.evaluateJavaScript(`JSON.stringify(window.__cspViolations)`)
          .then(data => resolve(JSON.parse(data)));
      }, 5000);
    });
  }
}

8.2 自动化测试脚本

// csp-test.js
describe('CSP合规测试', () => {
  beforeAll(() => {
    loadCSPPolicy('strict');
  });

  it('应阻止非白名单脚本加载', () => {
    const malicious = document.createElement('script');
    malicious.src = 'http://malicious.com/attack.js';
    document.head.appendChild(malicious);
    expect(malicious.onerror).toHaveBeenCalled();
  });
});

9. 紧急降级方案

9.1 策略降级开关

// csp-fallback.ets
class CSPFallback {
  private static originalPolicy: string;

  static enableEmergencyMode(): void {
    this.originalPolicy = document.querySelector('meta[http-equiv="Content-Security-Policy"]')?.content || '';
    const fallbackPolicy = `
      default-src *;
      script-src 'unsafe-inline' 'unsafe-eval' *;
      style-src 'unsafe-inline' *;
    `;
    CSPInjector.setPolicy(getActiveWebView(), fallbackPolicy);
  }

  static restoreOriginalPolicy(): void {
    if (this.originalPolicy) {
      CSPInjector.setPolicy(getActiveWebView(), this.originalPolicy);
    }
  }
}

10. 最佳实践总结

  1. ​按需放宽策略​

    <!-- 仅允许特定域名的脚本 -->
    <meta http-equiv="Content-Security-Policy" 
      content="script-src 'self' https://trusted.cdn.com">
    
  2. ​使用哈希/Nonce​

    // 为内联脚本添加nonce
    const nonce = generateRandomNonce();
    document.querySelector('script[inline]').nonce = nonce;
    
  3. ​分级控制策略​

    // 根据环境切换策略
    const policy = isProduction ? 
      CSPGenerator.generate(prodResources) : 
      CSPGenerator.generate(devResources);
    
  4. ​持续监控违规​

    # 查看CSP违规日志
    hilog | grep "CSP violation"
    

通过本方案可实现:

  1. ​100%​​ 通过HarmonyOS 5 CSP检查
  2. ​最小化​​ 策略放宽范围
  3. ​自动化​​ 策略生成与验证
  4. ​应急​​ 快速降级能力