Javascript周り の取り扱いが難しい Turbolinks
。
起こったこと
ページ毎に任意のJavascript
を実行したいが
普通に書いたら以下エラー。
ページ遷移でも同様。
ChromeDeveloperConsole
Refused to execute inline script because it violates the following Content Security Policy directive:
"script-src 'self' https: 'unsafe-inline' 'nonce-UiVx2CiP0HHN9jOOSEG43g=='".
Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list.
Content-Security-Policy (CSP)
(Cross Site Scripting (XSS) や data injection 攻撃を防ぐための HTTP の仕様)のために
ブラウザでの Javascript 実行が制限されている。
解決法
Rails 側で nonce を発行し、Javascript を認証する。
(HTTPヘッダに追加)
views/layouts/head.html.slim
= csp_meta_tag
config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy_nonce_generator = -> (request) do
# use the same csp nonce for turbolinks requests
if request.env['HTTP_TURBOLINKS_REFERRER'].present?
request.env['HTTP_X_TURBOLINKS_NONCE']
else
SecureRandom.base64(16)
end
script タグに nonce をつける
some_page.html.slim
= javascript_tag, nonce: true
| console.log('JS here');
Turbolinks のEvent
に合わせて、nonce を入れ替える。
application.js
document.addEventListener('turbolinks:request-start', function(event) {
var nonceTag = document.querySelector("meta[name='csp-nonce']");
if (nonceTag) event.data.xhr.setRequestHeader('X-Turbolinks-Nonce', nonceTag.content);
});
document.addEventListener('turbolinks:before-cache', function() {
Array.prototype.forEach.call(document.querySelectorAll('script[nonce]'), function(element) {
if (element.nonce) element.setAttribute('nonce', element.nonce);
});
});
Turbolinks Event list
参考