mirror of https://gitee.com/bigwinds/arangodb
Prevent guessing of Database names (#5748)
This commit is contained in:
parent
b88fde9659
commit
28c3de87e7
|
@ -137,9 +137,15 @@ bool resolveRequestContext(GeneralRequest& req) {
|
|||
GeneralCommTask::RequestFlow GeneralCommTask::prepareExecution(
|
||||
GeneralRequest& req) {
|
||||
if (!::resolveRequestContext(req)) {
|
||||
addErrorResponse(rest::ResponseCode::NOT_FOUND, req.contentTypeResponse(),
|
||||
req.messageId(), TRI_ERROR_ARANGO_DATABASE_NOT_FOUND,
|
||||
TRI_errno_string(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND));
|
||||
if (_auth->isActive()) {
|
||||
// prevent guessing of database names (issue #5030)
|
||||
addErrorResponse(rest::ResponseCode::UNAUTHORIZED,
|
||||
req.contentTypeResponse(), req.messageId(),
|
||||
TRI_ERROR_FORBIDDEN);
|
||||
} else {
|
||||
addErrorResponse(rest::ResponseCode::NOT_FOUND, req.contentTypeResponse(),
|
||||
req.messageId(), TRI_ERROR_ARANGO_DATABASE_NOT_FOUND);
|
||||
}
|
||||
return RequestFlow::Abort;
|
||||
}
|
||||
TRI_ASSERT(req.requestContext() != nullptr);
|
||||
|
@ -313,8 +319,7 @@ void GeneralCommTask::executeRequest(
|
|||
} else {
|
||||
addErrorResponse(rest::ResponseCode::SERVICE_UNAVAILABLE,
|
||||
request->contentTypeResponse(), messageId,
|
||||
TRI_ERROR_QUEUE_FULL,
|
||||
TRI_errno_string(TRI_ERROR_QUEUE_FULL));
|
||||
TRI_ERROR_QUEUE_FULL);
|
||||
}
|
||||
} else {
|
||||
// synchronous request
|
||||
|
@ -398,6 +403,12 @@ void GeneralCommTask::addErrorResponse(rest::ResponseCode code,
|
|||
addSimpleResponse(code, respType, messageId, std::move(buffer));
|
||||
}
|
||||
|
||||
void GeneralCommTask::addErrorResponse(rest::ResponseCode code,
|
||||
rest::ContentType respType,
|
||||
uint64_t messageId, int errorNum) {
|
||||
addErrorResponse(code, respType, messageId, errorNum, TRI_errno_string(errorNum));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -462,8 +473,7 @@ bool GeneralCommTask::handleRequestSync(std::shared_ptr<RestHandler> handler) {
|
|||
if (!ok) {
|
||||
addErrorResponse(rest::ResponseCode::SERVICE_UNAVAILABLE,
|
||||
handler->request()->contentTypeResponse(), messageId,
|
||||
TRI_ERROR_QUEUE_FULL,
|
||||
TRI_errno_string(TRI_ERROR_QUEUE_FULL));
|
||||
TRI_ERROR_QUEUE_FULL);
|
||||
}
|
||||
|
||||
return ok;
|
||||
|
|
|
@ -132,7 +132,9 @@ class GeneralCommTask : public SocketTask {
|
|||
|
||||
/// @brief send response including error response body
|
||||
void addErrorResponse(rest::ResponseCode, rest::ContentType,
|
||||
uint64_t messageId, int code, std::string const&);
|
||||
uint64_t messageId, int errorNum, std::string const&);
|
||||
void addErrorResponse(rest::ResponseCode, rest::ContentType,
|
||||
uint64_t messageId, int errorNum);
|
||||
|
||||
protected:
|
||||
GeneralServer* const _server;
|
||||
|
|
|
@ -307,10 +307,10 @@ void VstCommTask::handleAuthHeader(VPackSlice const& header,
|
|||
// mop: hmmm...user should be completely ignored if there is no auth IMHO
|
||||
// obi: user who sends authentication expects a reply
|
||||
addErrorResponse(ResponseCode::OK, rest::ContentType::VPACK, messageId, TRI_ERROR_NO_ERROR,
|
||||
"authentication successful");
|
||||
"auth successful");
|
||||
} else {
|
||||
addErrorResponse(rest::ResponseCode::UNAUTHORIZED, rest::ContentType::VPACK, messageId,
|
||||
TRI_ERROR_HTTP_UNAUTHORIZED, "authentication failed");
|
||||
TRI_ERROR_HTTP_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,12 +44,6 @@ RestStatus RestDatabaseHandler::execute() {
|
|||
if (type == rest::RequestType::GET) {
|
||||
return getDatabases();
|
||||
} else if (type == rest::RequestType::POST) {
|
||||
if (!_vocbase.isSystem()) {
|
||||
generateError(GeneralResponse::responseCode(TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE),
|
||||
TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE);
|
||||
|
||||
return RestStatus::DONE;
|
||||
}
|
||||
return createDatabase();
|
||||
} else if (type == rest::RequestType::DELETE_REQ) {
|
||||
return deleteDatabase();
|
||||
|
@ -71,14 +65,22 @@ RestStatus RestDatabaseHandler::getDatabases() {
|
|||
return RestStatus::DONE;
|
||||
}
|
||||
|
||||
Result res;
|
||||
VPackBuilder builder;
|
||||
|
||||
if (suffixes.empty() || suffixes[0] == "user") {
|
||||
std::vector<std::string> names;
|
||||
if (suffixes.empty()) {
|
||||
names = methods::Databases::list(std::string());
|
||||
if (!_vocbase.isSystem()) {
|
||||
res.reset(TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE);
|
||||
} else {
|
||||
names = methods::Databases::list(std::string());
|
||||
}
|
||||
} else if (suffixes[0] == "user") {
|
||||
names = methods::Databases::list(_request->user());
|
||||
if (!_request->authenticated()) {
|
||||
res.reset(TRI_ERROR_FORBIDDEN);
|
||||
} else {
|
||||
names = methods::Databases::list(_request->user());
|
||||
}
|
||||
}
|
||||
|
||||
builder.openArray();
|
||||
|
@ -87,16 +89,12 @@ RestStatus RestDatabaseHandler::getDatabases() {
|
|||
}
|
||||
builder.close();
|
||||
} else if (suffixes[0] == "current") {
|
||||
Result res = methods::Databases::info(&_vocbase, builder);
|
||||
|
||||
if (!res.ok()) {
|
||||
generateError(rest::ResponseCode::BAD, res.errorNumber());
|
||||
|
||||
return RestStatus::DONE;
|
||||
}
|
||||
res = methods::Databases::info(&_vocbase, builder);
|
||||
}
|
||||
|
||||
if (builder.isEmpty()) {
|
||||
|
||||
if (res.fail()) {
|
||||
generateError(res);
|
||||
} else if (builder.isEmpty()) {
|
||||
generateError(rest::ResponseCode::BAD, TRI_ERROR_BAD_PARAMETER);
|
||||
} else {
|
||||
generateOk(rest::ResponseCode::OK, builder.slice());
|
||||
|
@ -108,6 +106,13 @@ RestStatus RestDatabaseHandler::getDatabases() {
|
|||
// / @brief was docuBlock JSF_get_api_database_create
|
||||
// //////////////////////////////////////////////////////////////////////////////
|
||||
RestStatus RestDatabaseHandler::createDatabase() {
|
||||
if (!_vocbase.isSystem()) {
|
||||
generateError(GeneralResponse::responseCode(TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE),
|
||||
TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE);
|
||||
|
||||
return RestStatus::DONE;
|
||||
}
|
||||
|
||||
std::vector<std::string> const& suffixes = _request->suffixes();
|
||||
bool parseSuccess = false;
|
||||
VPackSlice body = this->parseVPackBody(parseSuccess);
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
desc: false
|
||||
},
|
||||
|
||||
url: arangoHelper.databaseUrl('/_api/database'),
|
||||
url: arangoHelper.databaseUrl('/_api/database/user'),
|
||||
|
||||
comparator: function (item, item2) {
|
||||
var a = item.get('name').toLowerCase();
|
||||
|
@ -53,7 +53,7 @@
|
|||
$.ajax({
|
||||
type: 'GET',
|
||||
cache: false,
|
||||
url: this.url + '/user',
|
||||
url: this.url,
|
||||
contentType: 'application/json',
|
||||
processData: false,
|
||||
success: function (data) {
|
||||
|
@ -104,7 +104,7 @@
|
|||
$.ajax({
|
||||
type: 'GET',
|
||||
cache: false,
|
||||
url: this.url + '/current',
|
||||
url: arangoHelper.databaseUrl('/_api/database/current'),
|
||||
contentType: 'application/json',
|
||||
processData: false,
|
||||
success: function (data) {
|
||||
|
|
|
@ -35,6 +35,7 @@ const users = require("@arangodb/users");
|
|||
const request = require('@arangodb/request');
|
||||
const crypto = require('@arangodb/crypto');
|
||||
const expect = require('chai').expect;
|
||||
const ERRORS = require('internal').errors;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test suite
|
||||
|
@ -46,6 +47,7 @@ function AuthSuite() {
|
|||
return arango.getEndpoint().replace(/^tcp:/, 'http:').replace(/^ssl:/, 'https:');
|
||||
};
|
||||
|
||||
// hardcoded in testsuite
|
||||
const jwtSecret = 'haxxmann';
|
||||
const user = 'hackers@arangodb.com';
|
||||
|
||||
|
@ -487,6 +489,60 @@ function AuthSuite() {
|
|||
expect(res).to.be.an.instanceof(request.Response);
|
||||
expect(res).to.have.property('statusCode', 401);
|
||||
},
|
||||
|
||||
testDatabaseGuessing: function() {
|
||||
let jwt = crypto.jwtEncode(jwtSecret, {
|
||||
"preferred_username": "root",
|
||||
"iss": "arangodb", "exp": Math.floor(Date.now() / 1000) + 3600
|
||||
}, 'HS256');
|
||||
// should respond with unauthorized name guessing
|
||||
var res = request.get({
|
||||
url: baseUrl() + "/_db/nonexisting/_api/version",
|
||||
auth: {
|
||||
bearer: jwt,
|
||||
}
|
||||
});
|
||||
expect(res).to.be.an.instanceof(request.Response);
|
||||
expect(res).to.have.property('statusCode', 401);
|
||||
},
|
||||
|
||||
testDatabaseListNonSystem: function() {
|
||||
let jwt = crypto.jwtEncode(jwtSecret, {
|
||||
"preferred_username": "root",
|
||||
"iss": "arangodb", "exp": Math.floor(Date.now() / 1000) + 3600
|
||||
}, 'HS256');
|
||||
// supported
|
||||
var res = request.get({
|
||||
url: baseUrl() + "/_api/database",
|
||||
auth: {
|
||||
bearer: jwt,
|
||||
}
|
||||
});
|
||||
|
||||
expect(res).to.be.an.instanceof(request.Response);
|
||||
expect(res).to.have.property('statusCode', 200);
|
||||
expect(res).to.have.property('json');
|
||||
expect(res.json).to.have.property('result');
|
||||
expect(res.json.result).to.be.an('array');
|
||||
expect(res.json.result).to.include("_system");
|
||||
|
||||
try {
|
||||
db._createDatabase("other");
|
||||
// not supported on non _system
|
||||
res = request.get({
|
||||
url: baseUrl() + "/_db/other/_api/database",
|
||||
auth: {
|
||||
bearer: jwt,
|
||||
}
|
||||
});
|
||||
expect(res).to.be.an.instanceof(request.Response);
|
||||
expect(res).to.have.property('statusCode', ERRORS.ERROR_ARANGO_USE_SYSTEM_DATABASE.code);
|
||||
} catch(e) {
|
||||
|
||||
} finally {
|
||||
db._dropDatabase("other");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue