mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
CLOUDSTACK-7063, CLOUDSTACK-7064: Add security headers on HTTP response
- Adds X-XSS-Protection header - Adds X-Content-Type-Options header - Fixes to use json content type defined from global settings - Uses secure cookie if enabled in global settings Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com> (cherry picked from commit b6b3494782d8bc1033941b802380ba1d5ebd464c) Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
843c0f891b
commit
20bcb4b673
@ -181,7 +181,8 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||
private static final Logger s_accessLogger = Logger.getLogger("apiserver." + ApiServer.class.getName());
|
||||
|
||||
public static boolean encodeApiResponse = false;
|
||||
public static String jsonContentType = "text/javascript";
|
||||
public static boolean s_enableSecureCookie = false;
|
||||
public static String s_jsonContentType = HttpUtils.JSON_CONTENT_TYPE;
|
||||
|
||||
/**
|
||||
* Non-printable ASCII characters - numbers 0 to 31 and 127 decimal
|
||||
@ -362,9 +363,13 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||
}
|
||||
|
||||
setEncodeApiResponse(Boolean.valueOf(_configDao.getValue(Config.EncodeApiResponse.key())));
|
||||
final String jsonType = _configDao.getValue(Config.JavaScriptDefaultContentType.key());
|
||||
final String jsonType = _configDao.getValue(Config.JSONDefaultContentType.key());
|
||||
if (jsonType != null) {
|
||||
jsonContentType = jsonType;
|
||||
s_jsonContentType = jsonType;
|
||||
}
|
||||
final Boolean enableSecureSessionCookie = Boolean.valueOf(_configDao.getValue(Config.EnableSecureSessionCookie.key()));
|
||||
if (enableSecureSessionCookie != null) {
|
||||
s_enableSecureCookie = enableSecureSessionCookie;
|
||||
}
|
||||
|
||||
if (apiPort != null) {
|
||||
@ -1136,7 +1141,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||
final BasicHttpEntity body = new BasicHttpEntity();
|
||||
if (HttpUtils.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) {
|
||||
// JSON response
|
||||
body.setContentType(jsonContentType);
|
||||
body.setContentType(getJSONContentType());
|
||||
if (responseText == null) {
|
||||
body.setContent(new ByteArrayInputStream("{ \"error\" : { \"description\" : \"Internal Server Error\" } }".getBytes(HttpUtils.UTF_8)));
|
||||
}
|
||||
@ -1367,7 +1372,11 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||
ApiServer.encodeApiResponse = encodeApiResponse;
|
||||
}
|
||||
|
||||
public static String getJsonContentType() {
|
||||
return jsonContentType;
|
||||
public static boolean isSecureSessionCookieEnabled() {
|
||||
return s_enableSecureCookie;
|
||||
}
|
||||
|
||||
public static String getJSONContentType() {
|
||||
return s_jsonContentType;
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,12 +155,20 @@ public class ApiServlet extends HttpServlet {
|
||||
try {
|
||||
|
||||
if (HttpUtils.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) {
|
||||
resp.setContentType(HttpUtils.JSON_CONTENT_TYPE);
|
||||
resp.setContentType(ApiServer.getJSONContentType());
|
||||
} else if (HttpUtils.RESPONSE_TYPE_XML.equalsIgnoreCase(responseType)){
|
||||
resp.setContentType(HttpUtils.XML_CONTENT_TYPE);
|
||||
}
|
||||
|
||||
HttpSession session = req.getSession(false);
|
||||
if (ApiServer.isSecureSessionCookieEnabled()) {
|
||||
resp.setHeader("SET-COOKIE", "JSESSIONID=" + session.getId() + ";Secure;Path=/client");
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Session cookie is marked secure!");
|
||||
}
|
||||
}
|
||||
}
|
||||
final Object[] responseTypeParam = params.get(ApiConstants.RESPONSE);
|
||||
if (responseTypeParam != null) {
|
||||
responseType = (String)responseTypeParam[0];
|
||||
@ -213,7 +221,7 @@ public class ApiServlet extends HttpServlet {
|
||||
}
|
||||
}
|
||||
}
|
||||
HttpUtils.writeHttpResponse(resp, responseString, httpResponseCode, responseType);
|
||||
HttpUtils.writeHttpResponse(resp, responseString, httpResponseCode, responseType, ApiServer.getJSONContentType());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -240,7 +248,7 @@ public class ApiServlet extends HttpServlet {
|
||||
auditTrailSb.append(" " + HttpServletResponse.SC_UNAUTHORIZED + " " + "unable to verify user credentials");
|
||||
final String serializedResponse =
|
||||
_apiServer.getSerializedApiError(HttpServletResponse.SC_UNAUTHORIZED, "unable to verify user credentials", params, responseType);
|
||||
HttpUtils.writeHttpResponse(resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType);
|
||||
HttpUtils.writeHttpResponse(resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType, ApiServer.getJSONContentType());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -251,7 +259,7 @@ public class ApiServlet extends HttpServlet {
|
||||
s_logger.info("missing command, ignoring request...");
|
||||
auditTrailSb.append(" " + HttpServletResponse.SC_BAD_REQUEST + " " + "no command specified");
|
||||
final String serializedResponse = _apiServer.getSerializedApiError(HttpServletResponse.SC_BAD_REQUEST, "no command specified", params, responseType);
|
||||
HttpUtils.writeHttpResponse(resp, serializedResponse, HttpServletResponse.SC_BAD_REQUEST, responseType);
|
||||
HttpUtils.writeHttpResponse(resp, serializedResponse, HttpServletResponse.SC_BAD_REQUEST, responseType, ApiServer.getJSONContentType());
|
||||
return;
|
||||
}
|
||||
final User user = _entityMgr.findById(User.class, userId);
|
||||
@ -267,7 +275,7 @@ public class ApiServlet extends HttpServlet {
|
||||
auditTrailSb.append(" " + HttpServletResponse.SC_UNAUTHORIZED + " " + "unable to verify user credentials");
|
||||
final String serializedResponse =
|
||||
_apiServer.getSerializedApiError(HttpServletResponse.SC_UNAUTHORIZED, "unable to verify user credentials", params, responseType);
|
||||
HttpUtils.writeHttpResponse(resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType);
|
||||
HttpUtils.writeHttpResponse(resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType, ApiServer.getJSONContentType());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -281,7 +289,7 @@ public class ApiServlet extends HttpServlet {
|
||||
// Add the HTTP method (GET/POST/PUT/DELETE) as well into the params map.
|
||||
params.put("httpmethod", new String[] {req.getMethod()});
|
||||
final String response = _apiServer.handleRequest(params, responseType, auditTrailSb);
|
||||
HttpUtils.writeHttpResponse(resp, response != null ? response : "", HttpServletResponse.SC_OK, responseType);
|
||||
HttpUtils.writeHttpResponse(resp, response != null ? response : "", HttpServletResponse.SC_OK, responseType, ApiServer.getJSONContentType());
|
||||
} else {
|
||||
if (session != null) {
|
||||
try {
|
||||
@ -294,13 +302,13 @@ public class ApiServlet extends HttpServlet {
|
||||
final String serializedResponse =
|
||||
_apiServer.getSerializedApiError(HttpServletResponse.SC_UNAUTHORIZED, "unable to verify user credentials and/or request signature", params,
|
||||
responseType);
|
||||
HttpUtils.writeHttpResponse(resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType);
|
||||
HttpUtils.writeHttpResponse(resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType, ApiServer.getJSONContentType());
|
||||
|
||||
}
|
||||
} catch (final ServerApiException se) {
|
||||
final String serializedResponseText = _apiServer.getSerializedApiError(se, params, responseType);
|
||||
resp.setHeader("X-Description", se.getDescription());
|
||||
HttpUtils.writeHttpResponse(resp, serializedResponseText, se.getErrorCode().getHttpCode(), responseType);
|
||||
HttpUtils.writeHttpResponse(resp, serializedResponseText, se.getErrorCode().getHttpCode(), responseType, ApiServer.getJSONContentType());
|
||||
auditTrailSb.append(" " + se.getErrorCode() + " " + se.getDescription());
|
||||
} catch (final Exception ex) {
|
||||
s_logger.error("unknown exception writing api response", ex);
|
||||
|
||||
@ -1592,13 +1592,22 @@ public enum Config {
|
||||
"Percentage (as a value between 0 and 1) of connected agents after which agent load balancing will start happening",
|
||||
null),
|
||||
|
||||
JavaScriptDefaultContentType(
|
||||
JSONDefaultContentType(
|
||||
"Advanced",
|
||||
ManagementServer.class,
|
||||
String.class,
|
||||
"json.content.type",
|
||||
"text/javascript",
|
||||
"Http response content type for .js files (default is text/javascript)",
|
||||
"application/json; charset=UTF-8",
|
||||
"Http response content type for JSON",
|
||||
null),
|
||||
|
||||
EnableSecureSessionCookie(
|
||||
"Advanced",
|
||||
ManagementServer.class,
|
||||
Boolean.class,
|
||||
"enable.secure.session.cookie",
|
||||
"false",
|
||||
"Session cookie's secure flag is enabled if true. Use this only when using HTTPS",
|
||||
null),
|
||||
|
||||
DefaultMaxDomainUserVms("Domain Defaults", ManagementServer.class, Long.class, "max.domain.user.vms", "40", "The default maximum number of user VMs that can be deployed for a domain", null),
|
||||
|
||||
@ -31,20 +31,40 @@ public class HttpUtils {
|
||||
public static final String UTF_8 = "UTF-8";
|
||||
public static final String RESPONSE_TYPE_JSON = "json";
|
||||
public static final String RESPONSE_TYPE_XML = "xml";
|
||||
public static final String JSON_CONTENT_TYPE = "text/javascript; charset=UTF-8";
|
||||
public static final String JSON_CONTENT_TYPE = "application/json; charset=UTF-8";
|
||||
public static final String XML_CONTENT_TYPE = "text/xml; charset=UTF-8";
|
||||
|
||||
public static void addSecurityHeaders(final HttpServletResponse resp) {
|
||||
if (resp.containsHeader("X-Content-Type-Options")) {
|
||||
resp.setHeader("X-Content-Type-Options", "nosniff");
|
||||
}
|
||||
else {
|
||||
resp.addHeader("X-Content-Type-Options", "nosniff");
|
||||
}
|
||||
if (resp.containsHeader("X-XSS-Protection")) {
|
||||
resp.setHeader("X-XSS-Protection", "1;mode=block");
|
||||
}
|
||||
else {
|
||||
resp.addHeader("X-XSS-Protection", "1;mode=block");
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeHttpResponse(final HttpServletResponse resp, final String response,
|
||||
final Integer responseCode, final String responseType) {
|
||||
final Integer responseCode, final String responseType, final String jsonContentType) {
|
||||
try {
|
||||
if (RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) {
|
||||
resp.setContentType(JSON_CONTENT_TYPE);
|
||||
if (jsonContentType != null && !jsonContentType.isEmpty()) {
|
||||
resp.setContentType(jsonContentType);
|
||||
} else {
|
||||
resp.setContentType(JSON_CONTENT_TYPE);
|
||||
}
|
||||
} else if (RESPONSE_TYPE_XML.equalsIgnoreCase(responseType)){
|
||||
resp.setContentType(XML_CONTENT_TYPE);
|
||||
}
|
||||
if (responseCode != null) {
|
||||
resp.setStatus(responseCode);
|
||||
}
|
||||
addSecurityHeaders(resp);
|
||||
resp.getWriter().print(response);
|
||||
} catch (final IOException ioex) {
|
||||
if (s_logger.isTraceEnabled()) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user