18
18
*/
19
19
package org .apache .struts2 .interceptor .csp ;
20
20
21
+ import com .opensymphony .xwork2 .inject .Inject ;
22
+ import org .apache .commons .lang3 .StringUtils ;
21
23
import org .apache .logging .log4j .LogManager ;
22
24
import org .apache .logging .log4j .Logger ;
25
+ import org .apache .struts2 .StrutsConstants ;
23
26
import org .apache .struts2 .action .CspSettingsAware ;
24
27
25
28
import javax .servlet .http .HttpServletRequest ;
43
46
*/
44
47
public class DefaultCspSettings implements CspSettings {
45
48
46
- private final static Logger LOG = LogManager .getLogger (DefaultCspSettings .class );
49
+ private static final Logger LOG = LogManager .getLogger (DefaultCspSettings .class );
50
+ private static final String NONCE_KEY = "nonce" ;
47
51
48
52
private final SecureRandom sRand = new SecureRandom ();
49
53
54
+ private CspNonceSource nonceSource = CspNonceSource .SESSION ;
55
+
50
56
protected String reportUri ;
51
57
protected String reportTo ;
52
58
// default to reporting mode
53
59
protected String cspHeader = CSP_REPORT_HEADER ;
54
60
61
+ @ Inject (value = StrutsConstants .STRUTS_CSP_NONCE_SOURCE , required = false )
62
+ public void setNonceSource (String nonceSource ) {
63
+ if (StringUtils .isBlank (nonceSource )) {
64
+ this .nonceSource = CspNonceSource .SESSION ;
65
+ } else {
66
+ this .nonceSource = CspNonceSource .valueOf (nonceSource .toUpperCase ());
67
+ }
68
+ }
69
+
55
70
@ Override
56
71
public void addCspHeaders (HttpServletResponse response ) {
57
72
throw new UnsupportedOperationException ("Unsupported implementation, use #addCspHeaders(HttpServletRequest request, HttpServletResponse response)" );
58
73
}
59
74
60
75
@ Override
61
76
public void addCspHeaders (HttpServletRequest request , HttpServletResponse response ) {
77
+ if (this .nonceSource == CspNonceSource .SESSION ) {
78
+ addCspHeadersWithSession (request , response );
79
+ } else if (this .nonceSource == CspNonceSource .REQUEST ) {
80
+ addCspHeadersWithRequest (request , response );
81
+ } else {
82
+ LOG .warn ("Unknown nonce source: {}, ignoring CSP settings" , nonceSource );
83
+ }
84
+ }
85
+
86
+ private void addCspHeadersWithSession (HttpServletRequest request , HttpServletResponse response ) {
62
87
if (isSessionActive (request )) {
63
88
LOG .trace ("Session is active, applying CSP settings" );
64
- associateNonceWithSession ( request );
89
+ request . getSession (). setAttribute ( NONCE_KEY , generateNonceValue () );
65
90
response .setHeader (cspHeader , createPolicyFormat (request ));
66
91
} else {
67
- LOG .trace ("Session is not active, ignoring CSP settings" );
92
+ LOG .debug ("Session is not active, ignoring CSP settings" );
68
93
}
69
94
}
70
95
96
+ private void addCspHeadersWithRequest (HttpServletRequest request , HttpServletResponse response ) {
97
+ request .setAttribute (NONCE_KEY , generateNonceValue ());
98
+ response .setHeader (cspHeader , createPolicyFormat (request ));
99
+ }
100
+
71
101
private boolean isSessionActive (HttpServletRequest request ) {
72
102
return request .getSession (false ) != null ;
73
103
}
74
104
75
- private void associateNonceWithSession (HttpServletRequest request ) {
76
- String nonceValue = Base64 .getUrlEncoder ().encodeToString (getRandomBytes ());
77
- request .getSession ().setAttribute ("nonce" , nonceValue );
105
+ private String generateNonceValue () {
106
+ return Base64 .getUrlEncoder ().encodeToString (getRandomBytes ());
78
107
}
79
108
80
109
protected String createPolicyFormat (HttpServletRequest request ) {
81
110
StringBuilder policyFormatBuilder = new StringBuilder ()
82
- .append (OBJECT_SRC )
83
- .append (format (" '%s'; " , NONE ))
84
- .append (SCRIPT_SRC )
85
- .append (" 'nonce-%s' " ) // nonce placeholder
86
- .append (format ("'%s' " , STRICT_DYNAMIC ))
87
- .append (format ("%s %s; " , HTTP , HTTPS ))
88
- .append (BASE_URI )
89
- .append (format (" '%s'; " , NONE ));
111
+ .append (OBJECT_SRC )
112
+ .append (format (" '%s'; " , NONE ))
113
+ .append (SCRIPT_SRC )
114
+ .append (" 'nonce-%s' " ) // nonce placeholder
115
+ .append (format ("'%s' " , STRICT_DYNAMIC ))
116
+ .append (format ("%s %s; " , HTTP , HTTPS ))
117
+ .append (BASE_URI )
118
+ .append (format (" '%s'; " , NONE ));
90
119
91
120
if (reportUri != null ) {
92
121
policyFormatBuilder
93
- .append (REPORT_URI )
94
- .append (format (" %s; " , reportUri ));
95
- if (reportTo != null ) {
122
+ .append (REPORT_URI )
123
+ .append (format (" %s; " , reportUri ));
124
+ if (reportTo != null ) {
96
125
policyFormatBuilder
97
126
.append (REPORT_TO )
98
127
.append (format (" %s; " , reportTo ));
@@ -103,7 +132,7 @@ protected String createPolicyFormat(HttpServletRequest request) {
103
132
}
104
133
105
134
protected String getNonceString (HttpServletRequest request ) {
106
- Object nonce = request .getSession ().getAttribute ("nonce" );
135
+ Object nonce = request .getSession ().getAttribute (NONCE_KEY );
107
136
return Objects .toString (nonce );
108
137
}
109
138
@@ -133,10 +162,10 @@ public void setReportTo(String reportTo) {
133
162
@ Override
134
163
public String toString () {
135
164
return "DefaultCspSettings{" +
136
- "reportUri='" + reportUri + '\'' +
137
- ", reportTo='" + reportTo + '\'' +
138
- ", cspHeader='" + cspHeader + '\'' +
139
- '}' ;
165
+ "reportUri='" + reportUri + '\'' +
166
+ ", reportTo='" + reportTo + '\'' +
167
+ ", cspHeader='" + cspHeader + '\'' +
168
+ '}' ;
140
169
}
141
170
142
171
}
0 commit comments