[security] CSP level2  對於 inline 程式碼的 hash 與 nonce 處理方法

之前介紹過 CSP 1.0 時,有提到目前部分瀏覽器有支援 CSP 2.0

支援度如下圖:

在 CSP level2 新增了部分的 directives 及屬性(如 web worker 針對 外部資源載入的 child-src) ,更多關於 CSP level2 的詳細內容

這篇僅介紹 CSP level2 針對 inline 程式碼所規範的 hashnonce,讓 inline 程式碼能較安全且彈性的執行.


我們先來看一個普通的 CSP demo 頁面

header(“Content-Security-Policy: script-src *;”);
print “<h1>CSP demo</h1>
<script src=’https://code.jquery.com/jquery-3.2.1.slim.min.js’></script>
<script>
    var my_script = document.createElement(‘script’);
    my_script.setAttribute(‘src’,’https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js’);
    document.head.appendChild(my_script);
</script>”;

實際執行的畫面:

可以看到設置了 CSP script-src * ,預設還是會擋掉 inline script ,在錯誤的訊息中,透露出可以設定 unsafe-inline, hash 及 nonce 來解決這個問題.

  1. unsafe-inline 解法:
    若設置 unsafe-inline 就變成允許所有的 inline 程式碼,這也意味著可以注入 inline 程式碼至頁面上面執行,可能會有 XSS 的疑慮,實務上就會變得較不安全. unsafe-inline 的寫法可以參考這篇
  2. hash:利用一串產生的 hash 值,類似簽證.來指定符合此 hash  值的外部資源才可以載入.
    支援 SHA-256 SHA-384 SHA-512 演算法所產生的值,經過 base64 編碼後結果
    來看看實際使用 php 的例子吧~

    <?php
    $script = "alert('test1') ;";
    $hash = hash('sha256', $script, true);
    $hash = base64_encode($hash);
    header("Content-Security-Policy: script-src 'sha256-".$hash."'");
    print "
    <h1>CSP demo</h1>
    
    <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3E%22.%24script.%22%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
    <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3Ealert('test2')%3B%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />";
    ?>
    

    可以看到上面利用 php 將 alert('test1'); 先做 sha256 hash(記得要輸出 binary data,script 中的空白也都要列入計算),再將 hash 值做一次 base64 編碼.將以上結果輸出至 header CSP policy 中.
    以上例子執行後會得到 alert('test1'); ,然而 alert('test2'); 則會被瀏覽器阻擋載

  3. nonce:用法與 hash 類似,但比較簡單,利用一段字串決定是否載入此 inline script 程式碼,來看看以下 php 的例子吧~
    <?php
    $hash = hash('sha256', rand(0, 1000));
    header("Content-Security-Policy: script-src 'nonce-".$hash."'");
    print "
    <h1>CSP demo</h1>
    
    <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%C2%A0nonce%3D%5C%22%22.%24hash.%22%5C%22%3Ealert('test1')%3B%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
    <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3Ealert('test2')%3B%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />";
    ?>
    

    上面這個例子,每次會產生不同的識別字串(簡單利用隨機值做 hash),讓惡意程式碼不能輕易仿造此 nonce 執行惡意程式碼.

 

以上介紹針對 hashnonce 這兩個在 CSP level2 所新增對於 inline script 的進階過濾,讓防止 XSS 之餘也能進一步過濾惡意 inline 程式碼.

ps. 若瀏覽器支援 CSP level2,當設置 hashnonce 時會忽略 unsafe-inline 的設置

參考資料:

https://imququ.com/post/content-security-policy-level-2.html

Leave a Reply