You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by bh...@apache.org on 2015/02/28 13:44:56 UTC
git commit: updated refs/heads/master to 20bcb4b
Repository: cloudstack
Updated Branches:
refs/heads/master 843c0f891 -> 20bcb4b67
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 <ro...@shapeblue.com>
(cherry picked from commit b6b3494782d8bc1033941b802380ba1d5ebd464c)
Signed-off-by: Rohit Yadav <ro...@shapeblue.com>
Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/20bcb4b6
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/20bcb4b6
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/20bcb4b6
Branch: refs/heads/master
Commit: 20bcb4b673ea83dd3d4e301705425632daa226ca
Parents: 843c0f8
Author: Rohit Yadav <ro...@shapeblue.com>
Authored: Sat Feb 28 18:12:37 2015 +0530
Committer: Rohit Yadav <ro...@shapeblue.com>
Committed: Sat Feb 28 18:14:49 2015 +0530
----------------------------------------------------------------------
server/src/com/cloud/api/ApiServer.java | 21 ++++++++++++-----
server/src/com/cloud/api/ApiServlet.java | 24 ++++++++++++-------
server/src/com/cloud/configuration/Config.java | 15 +++++++++---
utils/src/com/cloud/utils/HttpUtils.java | 26 ++++++++++++++++++---
4 files changed, 66 insertions(+), 20 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/20bcb4b6/server/src/com/cloud/api/ApiServer.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java
index daf24ef..cf719c0 100644
--- a/server/src/com/cloud/api/ApiServer.java
+++ b/server/src/com/cloud/api/ApiServer.java
@@ -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;
}
}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/20bcb4b6/server/src/com/cloud/api/ApiServlet.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiServlet.java b/server/src/com/cloud/api/ApiServlet.java
index 3c73375..5628d98 100644
--- a/server/src/com/cloud/api/ApiServlet.java
+++ b/server/src/com/cloud/api/ApiServlet.java
@@ -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);
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/20bcb4b6/server/src/com/cloud/configuration/Config.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java
index dd9bfc0..982958e 100644
--- a/server/src/com/cloud/configuration/Config.java
+++ b/server/src/com/cloud/configuration/Config.java
@@ -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),
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/20bcb4b6/utils/src/com/cloud/utils/HttpUtils.java
----------------------------------------------------------------------
diff --git a/utils/src/com/cloud/utils/HttpUtils.java b/utils/src/com/cloud/utils/HttpUtils.java
index 0ed2afa..58768dc 100644
--- a/utils/src/com/cloud/utils/HttpUtils.java
+++ b/utils/src/com/cloud/utils/HttpUtils.java
@@ -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()) {