App下載

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

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

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

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

特別是,CSP 不鼓勵用戶使用內(nèi)聯(lián) javascript,因為它引入了漏洞。如果你真的需要它,你可以使用?unsafe-inline?,但這是一個糟糕的方法,因為它否定了 CSP 的全部意義。該頁面上顯示的替代方法是使用?hash?或?nonce?。

如果您使用?.and().headers().contentSecurityPolicy(policy). ?策略字符串是靜態(tài)的,因此您無法為每個請求生成隨機數(shù)。擁有靜態(tài)隨機數(shù)是沒有用的。首先,您定義一個 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安全性對其進行配置:??.addFilterBefore(new CSPNonceFilter()?, ?HeaderWriterFilter.class)??。

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

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

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

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

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

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

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

我們應該到處都有 CSP,但是構建策略應該得到我們使用的框架的幫助,否則編寫一個不會破壞您的應用程序并且同時安全的適當策略是相當乏味的。


0 人點贊