Add option for validating content-type header
For systems implementing to the OWASP security guidelines[1] (of which all
should ideally) we should be checking the content-type header all times
that we parse a request as JSON.
This commit adds an option for parsing content-type, and sets a default
of "must get content-type". Ideally this would not be a breaking
change, but given the number of guides and scripts that omit the content
type, it seems worthwhile to add a trapdoor, such that people can opt
into their own model on how they would like to see this checking work.
Tested:
```
curl --insecure -H "Content-Type: application/json" -X POST -D headers.txt https://${bmc}/redfish/v1/SessionService/Sessions -d '{"UserName":"root", "Password":"0penBmc"}'
```
Succeeds.
Removing Content-Type argument causes bmc to return
Base.1.13.0.UnrecognizedRequestBody.
[1] cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html
Change-Id: Iaa47dd563b40036ff2fc2cacb70d941fd8853038
Signed-off-by: Ed Tanous <edtanous@google.com>
diff --git a/include/openbmc_dbus_rest.hpp b/include/openbmc_dbus_rest.hpp
index 2665b2e..9bf968a 100644
--- a/include/openbmc_dbus_rest.hpp
+++ b/include/openbmc_dbus_rest.hpp
@@ -20,6 +20,7 @@
#include "http_request.hpp"
#include "http_response.hpp"
#include "logging.hpp"
+#include "parsing.hpp"
#include "routing.hpp"
#include "str_utility.hpp"
@@ -80,6 +81,7 @@
const constexpr char* badReqMsg = "400 Bad Request";
const constexpr char* methodNotAllowedMsg = "405 Method Not Allowed";
const constexpr char* forbiddenMsg = "403 Forbidden";
+const constexpr char* unsupportedMediaMsg = "415 Unsupported Media Type";
const constexpr char* methodFailedMsg = "500 Method Call Failed";
const constexpr char* methodOutputFailedMsg = "500 Method Output Error";
const constexpr char* notFoundDesc =
@@ -87,6 +89,8 @@
const constexpr char* propNotFoundDesc =
"The specified property cannot be found";
const constexpr char* noJsonDesc = "No JSON object could be decoded";
+const constexpr char* invalidContentType =
+ "Content-type header is missing or invalid";
const constexpr char* methodNotFoundDesc =
"The specified method cannot be found";
const constexpr char* methodNotAllowedDesc = "Method not allowed";
@@ -1538,10 +1542,17 @@
{
BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
<< methodName;
- nlohmann::json requestDbusData =
- nlohmann::json::parse(req.body, nullptr, false);
+ nlohmann::json requestDbusData;
- if (requestDbusData.is_discarded())
+ JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
+ if (ret == JsonParseResult::BadContentType)
+ {
+ setErrorResponse(asyncResp->res,
+ boost::beast::http::status::unsupported_media_type,
+ invalidContentType, unsupportedMediaMsg);
+ return;
+ }
+ if (ret != JsonParseResult::Success)
{
setErrorResponse(asyncResp->res,
boost::beast::http::status::bad_request, noJsonDesc,
@@ -1838,11 +1849,18 @@
forbiddenResDesc, forbiddenMsg);
return;
}
+ nlohmann::json requestDbusData;
- nlohmann::json requestDbusData =
- nlohmann::json::parse(req.body, nullptr, false);
+ JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
+ if (ret == JsonParseResult::BadContentType)
+ {
+ setErrorResponse(asyncResp->res,
+ boost::beast::http::status::unsupported_media_type,
+ invalidContentType, unsupportedMediaMsg);
+ return;
+ }
- if (requestDbusData.is_discarded())
+ if (ret != JsonParseResult::Success)
{
setErrorResponse(asyncResp->res,
boost::beast::http::status::bad_request, noJsonDesc,
@@ -2376,14 +2394,23 @@
return;
}
- nlohmann::json requestDbusData =
- nlohmann::json::parse(req.body, nullptr, false);
-
- if (requestDbusData.is_discarded())
+ nlohmann::json requestDbusData;
+ JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
+ if (ret == JsonParseResult::BadContentType)
{
- asyncResp->res.result(boost::beast::http::status::bad_request);
+ setErrorResponse(asyncResp->res,
+ boost::beast::http::status::unsupported_media_type,
+ invalidContentType, unsupportedMediaMsg);
return;
}
+ if (ret != JsonParseResult::Success)
+ {
+ setErrorResponse(asyncResp->res,
+ boost::beast::http::status::bad_request,
+ noJsonDesc, badReqMsg);
+ return;
+ }
+
if (!requestDbusData.is_array())
{
asyncResp->res.result(boost::beast::http::status::bad_request);