App下載

Spring Security 的內(nèi)容安全策略隨機(jī)數(shù)

雨晨的清風(fēng) 2021-09-18 22:24:30 瀏覽數(shù) (2779)
反饋

?Content-Security-Policy?對(duì)網(wǎng)絡(luò)安全很重要。然而,它還不是主流,它的語法很難,它相當(dāng)令人望而卻步,工具很少對(duì)其提供靈活的支持。

雖然 Spring Security 確實(shí)有一個(gè)內(nèi)置的內(nèi)容安全策略 (CSP) 配置,但它允許您指定策略字符串,而不是動(dòng)態(tài)構(gòu)建它。在某些情況下,您需要的不止這些。

特別是,CSP 不鼓勵(lì)用戶使用內(nèi)聯(lián) javascript,因?yàn)樗肓寺┒?。如果你真的需要它,你可以使?unsafe-inline?,但這是一個(gè)糟糕的方法,因?yàn)樗穸?CSP 的全部意義。該頁面上顯示的替代方法是使用?hash?或?nonce?。

如果您使用?.and().headers().contentSecurityPolicy(policy). ?策略字符串是靜態(tài)的,因此您無法為每個(gè)請(qǐng)求生成隨機(jī)數(shù)。擁有靜態(tài)隨機(jī)數(shù)是沒有用的。首先,您定義一個(gè) CSP nonce 過濾器:

public class CSPNonceFilter extends GenericFilterBean {
    private static final int NONCE_SIZE = 32; //recommended is at least 128 bits/16 bytes
    private static final String CSP_NONCE_ATTRIBUTE = "cspNonce";
 
    private SecureRandom secureRandom = new SecureRandom();
 
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
 
        byte[] nonceArray = new byte[NONCE_SIZE];
 
        secureRandom.nextBytes(nonceArray);
 
        String nonce = Base64.getEncoder().encodeToString(nonceArray);
        request.setAttribute(CSP_NONCE_ATTRIBUTE, nonce);
 
        chain.doFilter(request, new CSPNonceResponseWrapper(response, nonce));
    }
 
    /**
     * Wrapper to fill the nonce value
     */
    public static class CSPNonceResponseWrapper extends HttpServletResponseWrapper {
        private String nonce;
 
        public CSPNonceResponseWrapper(HttpServletResponse response, String nonce) {
            super(response);
            this.nonce = nonce;
        }
 
        @Override
        public void setHeader(String name, String value) {
            if (name.equals("Content-Security-Policy") && StringUtils.isNotBlank(value)) {
                super.setHeader(name, value.replace("{nonce}", nonce));
            } else {
                super.setHeader(name, value);
            }
        }
 
        @Override
        public void addHeader(String name, String value) {
            if (name.equals("Content-Security-Policy") && StringUtils.isNotBlank(value)) {
                super.addHeader(name, value.replace("{nonce}", nonce));
            } else {
                super.addHeader(name, value);
            }
        }
    }
}

然后使用以下命令使用spring安全性對(duì)其進(jìn)行配置:??.addFilterBefore(new CSPNonceFilter()?, ?HeaderWriterFilter.class)??。

策略字符串?`nonce-{nonce}`?應(yīng)該包含在每個(gè)請(qǐng)求中被隨機(jī)數(shù)替換的字符串。

過濾器設(shè)置在?HeaderWriterFilter?之前,以便它可以包裝響應(yīng)并攔截對(duì)設(shè)置標(biāo)頭的所有調(diào)用。為什么它不能通過在 ?HeaderWriterFiilter ?設(shè)置標(biāo)題后,使用? response.setHeader(..) ?覆蓋標(biāo)題- 因?yàn)轫憫?yīng)已經(jīng)提交并且覆蓋沒有任何作用。

然后在您出于某種原因需要內(nèi)聯(lián)腳本的頁面中,您可以使用:

<script nonce="{{ cspNonce }}">...</script>

(我使用的是 Pebble 模板語法;但您可以使用任何模板來輸出請(qǐng)求屬性csp-nonce?)

再一次,內(nèi)聯(lián) javascript 很少是一個(gè)好主意,但有時(shí)它是必要的,至少是暫時(shí)的。

例如,如果您將 CSP 添加到遺留應(yīng)用程序,并且無法重寫所有內(nèi)容。

我們應(yīng)該到處都有 CSP,但是構(gòu)建策略應(yīng)該得到我們使用的框架的幫助,否則編寫一個(gè)不會(huì)破壞您的應(yīng)用程序并且同時(shí)安全的適當(dāng)策略是相當(dāng)乏味的。


0 人點(diǎn)贊