Dear community and vitaly-t hopefully,
I am building a website / server with pg-promise.
I use postgre role/group login for authentification.
I don't know if I am doing the things correctly but I would like that each user use their own postgres connection to query the database.
So in practice, I create a connection for each user when they connect (if it is not already existing).
To do so, I have created a Pool object with an ugly 'fake promise' and a pgUser object:
var pgPool = function(pg){
var _this=this;
var fakePromise = function(err){
var _this=this;
_this.err=err
_this.then=function(cb){if(!err){cb();return _this}else{return _this};};
_this.catch=function(cb){if(err){cb(_this.err)}else{return _this};};
return _this;
};
_this.check= function(user){
if (_this[user]){
return _this[user].check();
}else{
return new fakePromise({error:'Echec de connection à la base de
données'})
}
}
_this.add = function(user,password){
var c={};
c.host = 'localhost';
c.port = 5432;
c.database = 'pfe';
c.poolSize = 10;
c.poolIdleTimeout = 30000;
c.user=user;
c.password=password
if (!_this[user]){
_this[user] = new pgUser(c,pg);
return _this[user].check();
}else{
_this[user].config.password=password;
return _this[user].check();
};
};
return _this;
};
var pgUser = function(c,pg){
var _this=this
_this.config = c
_this.db = new pg(_this.config)
_this.check = function(){
return _this.db.connect();
};
return _this;
};
And here is how I add a user to the 'pool' during the login POST handling
pool.add(req.body.user,req.body.password).then(function(obj){
obj.done();
req.session.user = req.body.user;
req.session.password = req.body.password;
res.redirect("/");
return;
}).catch(function(err){
options.error='incorect password/login';
res.render('login', options);
return;
});
I am sure it could irritate pro developpers and you would be kind if you could explain me the best way :
is that a good idea to have one connection to the database per user
(it seems legit to have a good security)?
how can I use the pg-promise library better to avoid this ugly custom 'pool' object?
Sincerly thank you.
I have contacted the security responsible of my project, doing research as associate profressor in security (CITI lab)...here is his comment :
====================
Since it is my fault, I will try to explain ;-). First, to be clear, I
work on the security side (notably access control and RDBMS security)
but am not very familiar with JS or promises.
Our aim is to implement the principle of least privilege with a defense
in depth approach. In this particular case, this means that a query sent
by an unprivileged user should not have admin rights on the database
side. RDBMS such as PostgreSQL provide very powerful, expressive and
well-tested access control mechanisms : RBAC, row-level security,
parametrized views, etc. These controls, indeed, are usually totally
ignored in web applications which use the paradigm "1 application == 1
user", this user has thus admin role. But heavy clients often use
several different users on the database side (either one per final user
or one per specific role) and thus benefit from the access control of
the database.
Access control from the DB is an addition to access control in the web
application. AC in the webapp will be more precise but may probably
suffer from some bugs ; AC in the DB will be a bit more laxist but
better enforced, limiting damages in case of an application bug.
So in our case, we want to create a DB user for every application user.
Then, the connection to the database belongs to this specific user and
the database can thus enforce that a simple user cannot execute admin
operations. An intermediate possibility would be to drop some privileges
before executing a query, but our preferred way is to connect to the
database as the currently logged-in user. The login-password is sent by
the user when he authenticates and we just pass it to the DBMS.
Scalability is not (yet) an issue for our application, we can sacrifice
some scalability for this type of security.
Would you have any hints to help us achieve this ?
==================
Related
I am developing a Google Workspace Addon (standalone script) which will make REST API calls to external service and for that purpose it needs to provide an API key.
I request the API key input from a user and then store it in PropertiesService in the following way:
function onSheets(e) {
const userProperties = PropertiesService.getUserProperties();
const saved_api_key = userProperties.getProperty('api_key');
const api_key: string = saved_api_key ? saved_api_key : "";
const builder = CardService.newCardBuilder();
const apiKeyInput = CardService.newTextInput().setTitle('API Key')
.setFieldName('api_key')
.setHint('Enter your API Key')
.setValue(api_key);
const saveApiKey = CardService.newAction().setFunctionName('saveApiKeyFn');
const button = CardService.newTextButton().setText('Save').setOnClickAction(saveApiKey);
const optionsSection = CardService.newCardSection()
.addWidget(apiKeyInput)
.addWidget(button)
builder.addSection(optionsSection);
return builder.build();
}
function saveApiKeyFn(e) {
const api_key = e.formInput.api_key;
const userProperties = PropertiesService.getUserProperties();
userProperties.setProperty('api_key', api_key);
return CardService.newActionResponseBuilder()
.setNotification(CardService.newNotification()
.setText("API Key saved"))
.build();
}
Since userProperties are scoped to a current user it seems fine. But I have serveral problems with this solution:
Is this really safe? I mean it is stored in plain text so maybe there are ways to retrive it by other mailcious user?
The idea that by mistake I would use getScriptProperties() and thus leak one user's API key to all other users gives me nightmares. It is highly sensitive API key. It would cost a user tons of money if abused.
I read that some user's suggest https://cloud.google.com/secret-manager but I am not sure it's fit for this particular scenario. It would require one more external API call. It is not free. And lastly from what I underestand I would be sort of an owner of all of these secrets since I will be the owner of the Google Cloud project in which this API runs.
All I want is for the users to be able to store their keys safely, so that no one else including me can never access them.
What would you suggest? Thanks!
Is this really safe? I mean it is stored in plain text so maybe there are ways to retrive it by other mailcious user?
Security is relative. There's no such thing as absolute secrecy. Here are some attack scenarios:
Google employees or support may have unrestricted access
If a particular user installed a trigger, that trigger runs as that user and other users, if they can trigger the script and have edit access to the script, will be able to access the keys. A common scenario would be a installed edit trigger in a sheet. User B can access user A, if he can make a edit as well as edit the script. As mentioned in the comments by doubleunary, this is less of a problem in a published add on, as the source code is not accessible or editable.
Encrypting keys is a possibility. But, where would you store the decrypting key? You could ask every user to have a custom password for decrypting the key. But how many times are you going to make a API call? Would they have to enter the key every time? At what point does convenience overtake the need for secrecy?
The idea that by mistake I would use getScriptProperties() and thus leak one user's API key to all other users gives me nightmares. It is highly sensitive API key. It would cost a user tons of money if abused.
That is a possibility, but one that's easily avoidable by careful code review by yourself and your peers.
Those are the scenarios I could think of.
Related:
Securely Storing API Secrets used in Google Apps Script - Published Library
So I'm currently learning/building a REST API backend server for my web application using NodeJS, ExpressJS, and MySQL as the database. My question is in regards to the best way to implement authorisation to ensure User A does not access or edit the data belonging to another User. Please note that I understand there are a lot of examples for implementation of role based authorisation (ie user groups vs admin groups, etc) but this is not what I'm asking. Instead, how do I authorise a user against the data they are accessing?
It is possible that I'm overthinking this and this is not even necessary; that I should just check whether the data belongs to the user in every SQL query, but I thought I'd ask if there's a middleware or policy architecture that takes care of this, or maybe even authorise through caching.
The only solution I can think of is that every SQL query returns the the user id with the result, then I just create a service that checks every result if the id matches or not. If yes, then proceed. If not rollback the query and return unauthorised error. Is this ok?
I very much appreciate your advice, help, and if you can point me in the right direction.
Many thanks in advance.
Save the userId (or ownerId) in every table, and create a middleware where each db access method requires the userId as a parameter, for example:
readOne(id, userId) {
// implements SELECT * FROM example WHERE id = id AND userId = userId
}
updateOne(id, data, userId) {
// implements UPDATE example SET data = data WHERE id = id AND userId = userId
}
...
For security reasons, never send as a response "Requested data exist by you aren't the owner".
The simplest things usually work best. You wouldn't have to have a special service for checking authorization rights for every entity and you can do it at data access level eg. SELECT * FROM foo WHERE user_id = :currentUser or UPDATE foo SET foo = bar WHERE user_id = :currentUser
It also depends whether you want to notify the user about unallowed access via HTTP401 or not to reveal that such a resource even exists for different user HTTP404.
For HTTP401 the scenario would be:
const entity = loadFromDB(id);
if(entity.userId !== currentUserId) {
res.send(401);
return;
}
... update entity logic ...
So I have made a meteor app and I have the autopublish and insecure packages removed, now in order to receive data from my collections I have to subscribe to them in the client. I also have a python program that communicates with my meteor server over ddp using the python-meteor package, in it I simply subscribe to my collections and have complete access to all my data, I can also make Meteor.calls to call functions on the server. This is nice but I can't help but feel like this is a major security hole, anyone can write a client and subscribe to my collections and grab all my data on a whim, if they guess the collection names right.
Is there a way to only let certain clients subscribe to collections and perform server calls?
Yes, you should add security checks to all publishers and methods.
Here's an example publisher that ensures the user is logged in and is a member of the group before receiving any posts related to the group:
Meteor.publish('postsForGroup', function(groupId) {
check(groupId, String);
// make sure the user is a member of the group
var group = Groups.findOne(groupId);
if (!_.contains(group.members, this.userId))
throw new Meteor.Error(403, 'You must be a member of the group!');
return Posts.find({groupId: groupId});
});
Here's an example method that ensures the user is logged in and an admin of the group before being allowed to change the group's name:
Meteor.methods({
'groups.update.name': function(groupId, name) {
check(groupId, String);
check(name, String);
// make sure the user is an admin of the group
var group = Groups.findOne(groupId);
if (!_.contains(group.admins, this.userId))
throw new Meteor.Error(403, 'You must be an admin of the group!');
// make sure the name isn't empty
if (!name.length)
throw new Meteor.Error(403, 'Name can not be empty!');
return Groups.update(groupId, {$set: {name: name}});
}
});
One detail to watch out for: If you are using iron router, be careful not to cause any errors in your publishers. Doing so, will cause waitOn to never return. If you think that throwing an error is possible under normal operation, then I'd recommend return this.ready() instead of throw new Meteor.Error in your publisher.
I have been reading the documentation at https://docs.truevault.com/ but I am a little confused. I read this on the true vault site:
If you plan on using any of the server-side libraries, please ensure
any hosting environment you use is HIPAA compliant.
I took this to mean that TrueValut could support a standalone (client side only) mobile application architecture. Where the TrueVault API was the only server side interaction.
However my understanding of the documentation is that:
An API_KEY is required to register a new user.
Any API_KEY provides full access to all data vaults and JSON documents stored in TrueVault.
If both of these assumptions are correct that would mean it would be impossible to register new users directly from the client side app, forcing me to use a costly and resource intensive HIPPA compliment web server. The only way to get around this would be top hard code the API_KEY into the app, an obvious no go if that API_KEY can access all of my TrueVault data.
For my use case I have the following requirements for TrueVault for me to be able to consider using it (I would imagine these requirements are the same for anyone looking to develop a client side only healthcare application):
A user can sign up via the API directly from my client side app without requiring any sensitive keys or root auth data.
A user can authenticate using only the data they provided to sign up (username/email/password). My app is multi platform I cant ask them to remember their API keys to log in.
A user can Read/Write/Update/Delete data linked to their profile. They can not access any data from another user using their credentials.
Is TrueVault able to deliver these three basic requirements?
If the answer to this is "No" I would recommend you update this text on your website as there are not going to me any viable HIPPA compliment applications that can be supported by TrueVault without an independent server side interface.
I'm currently using AWS Lambda as a solution. Lambda is HIPPA compliant, more info here. Lambda is also a low cost solution.
Here is an example of the code I'm running on Lambda using Node.js.
var request = require('request-promise');
var _ = require('lodash');
function encodeBase64(str) {
return (new Buffer(str)).toString('base64');
}
var baseUrl = 'https://api.truevault.com/v1/';
var headers = {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
};
var req = request.defaults({
baseUrl: baseUrl,
headers: _.extend({
Authorization: 'Basic ' + encodeBase64('your api key:')
}, headers),
transform: function(body) {
return JSON.parse(body);
}
});
exports.handler = function(event, context) {
req.post('users', {
form: {
username: event.email,
password: event.password,
attributes: encodeBase64(JSON.stringify({
name: event.name
}))
}
}).then(function() {
context.succeed({user: user});
}).catch(context.fail);
}
In general, you are correct - if you include zero server-side processing between user and TrueVault, then the API keys will be public. At least, I don't know of any way to avoid this.
That being said, it is incorrect to jump to "any API_KEY provides full access to all data vaults and JSON documents stored in TrueVault." - that's not the case if setup properly.
TrueVault API keys are able to be narrowed in scope quite a lot. Limiting a key to only Write permission on {Vault#1}, a second key to only Read permission on {Vault#2}, a third key to allow uploading Blogs in {Vault#1}, quite a few variations, a forth for deleting information from {Vault#2}, and on as needed. You can also limit permissions specifically to content "owned" by the API key (e.g. user-specific keys) Full documentation here.
There are also limited scope keys (set expiry time, usage count, limit to any of the prior permission scopes). Docs here.
TrueVault also offers user logins separate from API keys which may be better suited if your user are using login credentials. Docs here.
I'm still rather figuring out TrueVault myself (at time of writing at least) so be sure to research and review more for your needs. I'm still torn if the limited scoping is "good enough" for my needs - I'm leaning towards using AWS Lambda (or similar) to be a HIPAA compliant middle man, if only to better hide my access token generation and hide that my data is going to TrueVault and add some "serverless-server-side" data validation of sorts.
I have a question of how to better organize the implementation of the following functionality.
Suppose a user needs to be registered into the system by unique email and password (first step) and then he should confirm registration (second step). I have several choices of structuring implementation of first step (registration) between application services/domain services/user entity and I'm not sure which one is better.
First option:
AppService:
var existingUser = UserRepository.GetUserByEmail(email);
if (existingUser != null)
{
throw new ValidationException(...);
}
var newUser = UserFactory.CreateUser();
newUser.Register(email, password);
UserRepository.Save(newUser);
// commit
So here, we do not use any domain service. The thing which I personally don't feel confortable is that Email uniqueness business rule is checked in the Application Service, this being a business rule.
Second option:
AppService:
var user = UserRegistrationDomainService.RegisterUser(email, password);
UserRepository.Save(user);
// commit
UserRegistrationDomainService:
User RegisterUser(email, password)
{
var existingUser = UserRepository.GetUserByEmail(email);
if (existingUser != null)
{
throw new ValidationException(...);
}
var newUser = UserFactory.CreateUser();
newUser.Register(email, password);
return newUser;
}
What I don't like here, is that this solution is not quite symmetric with the implementation of second step, where we just get the user from repository and call User.ConfirmRegistration(). So for registration confirmation we do not need any domain service whereas for registration, in second option, we use such service.
Which option is better? Can the application service from first option contain email uniqueness validation?
Personally I think the Validation for that lives in the Domain (either the Entity of the service). The rule after all, is required due to a business rule.
It would be preferable in option 2 for the application services not to be responsible for saving the user, this is blurring the lines of responsibilities and it would be nicer if the domain service handled it. And the application service would simply call UserRegistrationDomainService.RegisterUser(email, password)
Option 1 means that the unique email rule is application-specific. In other words, if you take the Domain dll (or jar, module, etc.) to reuse it in another application, the rule won't be there any more.
Since we can reasonably consider that rule to be application-agnostic, I'd choose option 2.
Another solution could be to implement it in the Factory instead. After all, this is where you'll typically put the validation logic upon creation of your User (null/empty name checking, email format verification, and so on) so why not centralize all creation rules in the same place ?