【Wicket】[CSP] Content-Security-Policy & Content-Security-Policy-Report-Only

URL Link //n.sfs.tw/15702

2022-03-24 14:33:41 By 過路君子

哈囉大家好,這裡是又踩進Wildfly大坑裡面的小編過路君子

Wicket 9.x API真的很過分,就放兩個函式在那裡,然後底下全白,誰知道在做什麼的。

 

 

那因為本篇文章高度相關 CSP,還不太了解的人可以參考由其他筆者所撰寫的這篇:Content Security Policy (CSP) — 幫你網站列白名單吧@Hannah Lin

或是直接生咬 W3C 所提供的完整原文文檔:Content Security Policy Level 3@W3C,這兩篇也是小編所參考的文獻資料。

小編這邊要特別提醒:不是使用了 CSP 就可以高枕無憂,CSP 只是我們眾多安全防線中的第一道而已,就算開啟,我們伺服器後端也要做校驗檔案之類的動作,相關的資訊可以參考這篇:前端防禦從入門到棄坑——CSP變遷@程序猿哪些事

 

那至於 Wicket 的 API 也有小小的提及,但是前提是建立在他們認為我們已經具有 CSP 的基本知識了,所以如果完全沒有 CSP 知識點進去會完全看不懂。

關於如何設定 CSP 和 Content-Security-Policy-Report-Only 可以參考這篇:org.apache.wicket.csp.ContentSecurityPolicySettings

關於 Wicket 有什麼 CSP 可以設定,可以參考這篇:org.apache.wicket.csp.CSPDirective

 

小編就是靠上面三篇文檔和硬咬 Wicket 9.x API 才終於搞懂的,不得不說,Wicket 這方面的資料網路上超級缺乏,沒什麼有用的資料可以參考。

其實小編有看到更為簡單粗暴的解法,這個小編下面會提到,簡單來說就是直接關閉 CSP,那樣是可以跑啦,但是一點都不優雅,小編不喜歡,就是喜歡搞自己。

所以才跑去讀了好幾遍 CSP 後好不容易搞懂,然後又要了解 Wicket 如何魔改 CSP,又要如何更訂 Wicket 原始預設值,最後才誕生出這篇筆記的。

 

這篇文章的主要目的是要記錄 Wicket CSP 該如何設定,那事實上 Wicket 有提供我們在這高強度的 CSP 下依舊可以使用外部 css 和 javascript 的(運用隨機 nonce)。

有興趣知道如何新增 css 或是 javascript 到我們當前頁面(直接使用 HTML 標籤,這兩者都會被 Wicket 預設的 CSP 擋下),可以參考這篇:【Wicket】[nonce]如何導入css以及javascript@小編過路君子

那在開始之前照慣例附上目錄結構:

(所有圖片點擊都可以放大、變高清)

 

 

當我們今天部屬好 Wicket 時,很高興地開始撰寫我們的網頁,那既然是網頁,想必 CSS 和 JavaScript 是不可少的吧!

所以我們就很高興的像平常一樣使用了 <link href="..." /> 或 <script scr="...">,結果竟然被 CSP 擋下來了......

然後仔細看了一下檔頭:

好傢伙,竟然 Wicket 預設連同源的 CSS 檔案也會拒絕掉。

不僅如此連 inline 和 <style type="text/css"></style> 也擋掉了,總之只要是 css 一律無效。

那各位可能會看到網路上其他人的解法是使用 <wicket:head></wicket:head> 來解決行內 css 或是 js,但對於外部檔案一樣無解。

 

那接下來我們就要開始對 CSP 動刀了,接下來的所有程式都是寫在 webRoute.java 內,index.html 以及 index.java 等等檔案都不需要做修正。

第一步當然就是要來取得我們整個網站的 CSP 設定了,Wicket 的 CSP 是以一個網站作為單位的,全站統一用一個 CSP。

那為了達成這個任務,我們首先要到我們的 WebApplication 下,以小編的例子來說是 webRoute.java 這個檔案,使用以下函式:

public class webRoute extends WebApplication
{
    @Override
    public void init()
    {
        getCspSettings();
    }
}

 

就這樣,但如果各位有偷偷看過 Wicket 9.x API 的話,應該不難發現我們後面還要接一個函式,根據 API 的解釋:

沒了。

真的是有夠搞笑,至少這樣寫一下吧!

blocking() reporting()
設定 Content-Security-Policy 設定 Content-Security-Policy-Report-Only

那 Wicket 預設不啟用 Content-Security-Policy-Report-Only,如果各位之後有自行啟用的話......記得要設定 report-to,要不然網頁會一直送出警告,就算沒有錯誤也一樣,很討厭。

 

其中 Wicket 對於這兩項的預設值如下,其中 nonce 後面接的數值每次頁面載入都會重新產生,所以這邊僅寫 nonce:

  Content-Security-Policy(CSP) Content-Security-Policy-Report-Only
default-src none -
script-src nonce
strict-dynamic
-
style-src nonce -
img-src self -
connect-src self -
font-src self -
anifest-src self -
child-src self -
frame-src self -
base-uri self -
report-to - -

表註:可能會有人發現,若真的有設定 report-to,實際去抓取檔頭的話會看到 Wicket 用的是 report-uri,那因為 report-uri 即將被淘汰,所以小編評估之後還是決定寫 report-to。

 

所以如果我們今天要設定 Content-Security-Policy 要這樣寫:

public class webRoute extends WebApplication
{
    @Override
    public void init()
    {
        getCspSettings().blocking();
    }
}

反之,如果只是要設定 Content-Security-Policy-Report-Only 要這樣寫:

public class webRoute extends WebApplication
{
    @Override
    public void init()
    {
        getCspSettings().reporting();
    }
}

那如果兩個都要設定就都寫,簡單吧!

 

接下來就是重頭戲了,我們要開始添加、移除或是關閉 CSP 規則了。

那 Wicket 官方也很好心的提供了四個快速建構式給我們,分別是:

  strict() disabled() clear() unsafeInline()
default-src none - - none
script-src nonce
strict-dynamic
- - self
unsafe-eval
unsafe-inline
style-src nonce - - self
unsafe-inline
img-src self - - self
connect-src self - - self
font-src self - - self
anifest-src self - - self
child-src self - - self
frame-src self - - self
base-uri self - - self
report-to - - - -

其中的 strict() 設定有沒有眼熟呢?對,我們的 Wicket 預設值就是 strict()。

 

這也是為什麼現在看到網路上的解法有些人就叫你直接下:

public class webRoute extends WebApplication 
{
    @Override
    public void init()
    {
        getCspSettings().blocking().disabled();
    }
}

然後我們照著下,唉!可以跑了,我的資源正常載入了!

看了上表之後我們就可以理解了,因為我們直接把 CSP 關掉了嘛!俗話說的好,解決不了問題就解決提出問題的人。

所以其實下 getCspSettings().blocking().clear(); 也有同樣的效果。

 

那關於設定 SCP 的方式有以下四種:

add() remove() reportBackAt() setAddLegacyHeaders()
添加 SCP 指定項狀態 完全移除 SCP 指定項設定 設定 report-to 的網址路徑 是否啟用 Legacy 來相容其他瀏覽器,像是 IE

 

那可以設定的 SCP 總共有以下 16 種。

這邊要注意的是:要先 import org.apache.wicket.csp.CSPDirective; 才能使用喔。

Wicket CSP 屬性 對應的 CSP 值
BASE_URI base-uri
CHILD_SRC child-src
CONNECT_SRC connect-src
DEFAULT_SRC default-src
FONT_SRC font-src
FRAME_ANCESTORS frame-ancestors
FRAME_SRC frame-src
IMG_SRC img-src
MANIFEST_SRC manifest-src
MEDIA_SRC media-src
OBJECT_SRC object-src
REPORT_URI report-uri
SANDBOX sandbox
SCRIPT_SRC script-src
STYLE_SRC style-src
WORKER_SRC worker-src

 

那對每個設定可以添加的值有以下七種。

這邊也要注意的是:要先 import org.apache.wicket.csp.CSPDirectiveSrcValue; 才能使用喔。

Wicket CSP 值 對應的 CSP 值
NONCE nonce-xxx
NONE none
SELF self
STRICT_DYNAMIC strict-dynamic
UNSAFE_EVAL unsafe-eval
UNSAFE_INLINE unsafe-inline
WILDCARD *

表註:nonce後面所接的值 Wicket 會自動幫我們產生,我要只要填入 NONCE 即可。

 

那我們一個一個來看,先從最簡單的 setAddLegacyHeaders() 來看好了,我們先看看關於此函式的介紹:

由上圖我們可以知道,此函數接受一個 boolean 值,而且預設為關閉。

所以如果我們今天要打開此項設定,應該要這樣下:

@Override
public void init()
{
    getCspSettings().blocking()
        .setAddLegacyHeaders(true);
}

當我們重新載入頁面之後就可以看到我們的檔頭裡多了 X-Content-Security-Policy 的屬性。

而且相關的設定跟我們目前的 Content-Security-Policy 一模一樣喔。

 

第二個小編緊接著要來介紹的是 reportBackAt(),一樣先來看看有關此函式的說明:

由上圖的函式說明我們可以得知,此函數接受一個字串,而這個字串就是要瀏覽器將使用者違反相關的 CSP 資訊傳送到我們所指定的地方。

所以如果小編想要瀏覽器將相關資訊傳送到 http://192.168.88.128:8080/CSPwarning,那我們應該要這樣下指令:

public class webRoute extends WebApplication
{
    @Override
    public void init()
    {
        getCspSettings().blocking()
            .reportBackAt("http://192.168.88.128:8080/CSPwarning");
    }
}

然後我們再次重新載入網頁。

瞧~現在我們的檔頭的 Content-Security-Policy 這欄是不是出現了剛剛一直都沒有看到的 report-uri 呢?

那各位啟用前要對於處理這些訊息做一些規範,包括短時間內大量的 CSP 警告接收,如果對方真的有心,那這也是一種 DDoS 攻擊。

 

那接下來要來介紹的就是 remove() 囉,照慣例我們先來一起看 API 對於此函式給出的說明吧~

根據上圖我們可以得知,只要將我們想要移除項目填入,就可以成功將其移除掉囉。

import org.apache.wicket.csp.CSPDirective;

public class webRoute extends WebApplication
{
    @Override
    public void init()
    {
        getCspSettings().blocking()
            .remove(CSPDirective.STYLE_SRC)      //移除 style-src
            .remove(CSPDirective.SCRIPT_SRC);    //移除 script-src
    }
}

這邊小編示範移除原預設 CSP 裡的 style-src 和 script-src 屬性,若要移除其他屬性可以參照上表,換成要移除的屬性即可。

那根據 Wicket 9.x API 上所寫的,小編沒有找到只移除某一屬性某個值的函式,所以目前要刪除屬性的某一值的話......只能全部移除再添加回來其他不須刪除的。

 

最後一個函數!沒錯,終於來到了最後一個函式 add() 了。

就像剛剛一樣,來吧。

那不同於 remove() 是移除整個屬性,每次 add() 都只會增加某一屬性的指定值。

import org.apache.wicket.csp.CSPDirective;
import org.apache.wicket.csp.CSPDirectiveSrcValue;

public class webRoute extends WebApplication
{
    @Override
    public void init()
    {
        getCspSettings().blocking()
                /* 在 font-src 裡添加 nonce-xxx */
            .add(CSPDirective.FONT_SRC, CSPDirectiveSrcValue.NONCE)

                /* 在 style-uri 裡添加 self */
            .add(CSPDirective.REPORT_URI, CSPDirectiveSrcValue.SELF);
    }
}

所以我們在使用的時候就要告訴 Wicket 我們要設定什麼屬性的什麼值對不對~

 

 

那這就是 Wicket 的 CSP 全操作了,其中小編有省略掉解釋 CSP 屬性所代表的意義,像是 STYLE_SRC 是用來限定可執行 Css 來源或是 SCRIPT_SRC 是用來限定可執行 JavaScript 來源等等。

那其實 Wicket 已經有內建導入外部 Script 或是 Css 的方法了,各位想到了嗎?那小編這邊也不賣關子了,簡單來說就是使用 nonce 啦!

相關的資訊可以參考小編的這篇文章:【Wicket】[nonce]如何導入css以及javascript@小編過路君子

 

 

 

後記

其實當下小編一得知是因為 CSP 才會造成資源無法加載,就跑去找其他人是怎麼解決這個問題,發現絕大部份就是教你直接關閉 CSP,然後原發問者就很高興的在下面回覆:「它有用,開始運作了!」小編看到真的是額頭三條線冒出來......相當的不以為意,小編就不信邪小編不能在不更動 Wicket 預設 CSP 的狀況下使用小編想要的資源。

然後花了兩天去研讀 CSP,然後又花了一天去了解要怎麼使用 Wicket 來全面控制 CSP,最後又花了一天來寫這篇文;那你問小編最後有沒有解決問題,當然有!就是使用 nonce,而且除了同源之外還可以加載外部的資源檔案喔!

下圖的 nonce 是 Wicket 自己產生的,所以每次載入頁面所顯示的值都不一樣,詳細的步驟小編寫在了此文章末的那篇文章了。