1
0
Fork 0

Prevent guessing of Database names (#5748)

This commit is contained in:
Simon 2018-07-03 16:24:50 +02:00 committed by Jan
parent b88fde9659
commit 28c3de87e7
6 changed files with 104 additions and 31 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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) {

View File

@ -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");
}
}
};
}