Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
7
Help us understand the problem. What is going on with this article?
@sh-ogawa

Vue.jsとCSP(Content Security Policy)

More than 1 year has passed since last update.

このページについて

Vue.js Advent Calendar 2018 - Qiita の 7日目 の記事です。

VueはどうやらCSPを気にしないといけないっぽいので、勉強がてらアウトプットしてみます。

このページを書くモチベーション的なの(なぜCSPなのか)

LaravelのスカフォールドでVue向けにセットアップして、
単一ファイルコンポーネントが出力されることまでを見届けて、
機能を色々入れたところ、

しばらくしたら単一ファイルコンポーネントがHTMLに展開されなくなっていた。

コンソールログを確認したら、

app.js?id=d5d0f8c0c937d3505d0a:32856 [Vue warn]: It seems you are using the standalone build of Vue.js in an environment with Content Security Policy that prohibits unsafe-eval. The template compiler cannot work in this environment. Consider relaxing the policy to allow unsafe-eval or pre-compiling your templates into render functions.
warn @ app.js?id=d5d0f8c0c937d3505d0a:32856
12:44:27.872 app.js?id=d5d0f8c0c937d3505d0a:32856 [Vue warn]: Failed to generate render function:

EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "default-src 'self' 'unsafe-inline' data:".
 in

with(this){return _c('div',{attrs:{"id":"app"}},[_c('vue-component')],1)}


(found in <Root>)

というエラーが出ていて、CSPにunsafe-evalが設定されてないぞってことで、
VueとCSPがチョット気になりました。

このエラー自体はどう直すのか

ログに書いてある通りで、unsafe-evalを設定してあげればOK。

CSPの知見的にはGoogleのガイドにまとまっているので、
ここを参考にCSPへ以下を追加。

script-src 'self' 'unsafe-eval';

なぜ起きたのか

デフォルトの設定からCSPの設定を自分で変えているからですね。
CSPが絡んでくるという認識が全く無かったので、特に動作確認とかしてなかったです。

CSPの設定がどうチェックされているか探ってみる

ログを吐いている入口を探す

IntelliJというのは本当に便利で、jsのデバッグが出来るのでどうやって出力しているか簡単に判りますね。
ほんと素晴らしい。

余談ですが、IntelliJでjsのデバッグをしたい場合はLiveEditを有効にする必要があるので、
興味あれば公式を参照してやってみてください。

以下のとおり

new Function('return 1');

の処理で例外が発生したときにログ出力されています。

function createCompileToFunctionFn (compile) {
  var cache = Object.create(null);

  return function compileToFunctions (
    template,
    options,
    vm
  ) {
    options = extend({}, options);
    var warn$$1 = options.warn || warn;
    delete options.warn;

    /* istanbul ignore if */
    if (true) {
      // detect possible CSP restriction
      try {
        new Function('return 1');
      } catch (e) {
        if (e.toString().match(/unsafe-eval|CSP/)) {
          warn$$1(
            'It seems you are using the standalone build of Vue.js in an ' +
            'environment with Content Security Policy that prohibits unsafe-eval. ' +
            'The template compiler cannot work in this environment. Consider ' +
            'relaxing the policy to allow unsafe-eval or pre-compiling your ' +
            'templates into render functions.'
          );
        }
      }
    }

    // check cache
    var key = options.delimiters
      ? String(options.delimiters) + template
      : template;
    if (cache[key]) {
      return cache[key]
    }

    // compile
    var compiled = compile(template, options);

    // check compilation errors/tips
    if (true) {
      if (compiled.errors && compiled.errors.length) {
        warn$$1(
          "Error compiling template:\n\n" + template + "\n\n" +
          compiled.errors.map(function (e) { return ("- " + e); }).join('\n') + '\n',
          vm
        );
      }
      if (compiled.tips && compiled.tips.length) {
        compiled.tips.forEach(function (msg) { return tip(msg, vm); });
      }
    }

    // turn code into functions
    var res = {};
    var fnGenErrors = [];
    res.render = createFunction(compiled.render, fnGenErrors);
    res.staticRenderFns = compiled.staticRenderFns.map(function (code) {
      return createFunction(code, fnGenErrors)
    });

    // check function generation errors.
    // this should only happen if there is a bug in the compiler itself.
    // mostly for codegen development use
    /* istanbul ignore if */
    if (true) {
      if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) {
        warn$$1(
          "Failed to generate render function:\n\n" +
          fnGenErrors.map(function (ref) {
            var err = ref.err;
            var code = ref.code;

            return ((err.toString()) + " in\n\n" + code + "\n");
        }).join('\n'),
          vm
        );
      }
    }

    return (cache[key] = res)
  }
}

この処理、CSPに必要な設定があるかどうかだけをチェックするためだけにやっているみたいです。

こんなので調べられるの??ってことで調べてみたら、Chromeのガイド

The policy against eval() and its relatives like setTimeout(String), setInterval(String), and new Function(String) can be relaxed by adding 'unsafe-eval' to your policy:

とか書かれていますね。

てことはこの辺はブラウザ依存か?
ってことで非モダンブラウザの代表であるIE11で試してみたらエラーにならなかったです。

これだけでかなり面倒くさいな・・・って印象をかなり持った。

ちなみにガイドのすぐ下には、

However, we strongly recommend against doing this. These functions are notorious XSS attack vectors.

と書かれていて、動的にメソッド作ってるので間違いなくまぁそうなんですけど、
JSのフレームワークって潜在的にセキュリティあんま良くない?とか思ったのでした。
フロントは詳しくないから強い人に教えて欲しいです・・・。

Angularとかもチョロっとコード覗いてきたけど、new Functionとかやってるので、
この問題はVueに限らないみたいですね。

まとめ

Vueに絞って簡単にまとめておくと、

  • フレームワーク自身がCSPにunsafe-evalを求めてきている(ガイドに書いといて欲しいな、書いてあったらすみません)
  • モダンブラウザじゃないと、CSP意識してないからそのまま動く
  • 上記より、IE -> Chromeとかに乗り換えると何で?ってことが起きそう(Vue使うようなシステムがIEベースでやってるとは思わないけど)
7
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
sh-ogawa
最近マネジメント業が増えてるけど、アプリケーションアーキテクト。 私の発言は個人的な考えのもので、所属組織を代表するものではないです。
visor
自治体・官公庁向けに地域への情報発信サービスの提供・運用をするサービス会社です。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
7
Help us understand the problem. What is going on with this article?