I have a firebase https function:
exports.updateDatabase = functions.https.onRequest((req, res) => {
// ...
});
Which can be called via: https://us-central1-xxx-xxx.cloudfunctions.net/date
Does it mean that anyone with this url is able to update the database?
Is there a way to secure it when calling it from browser?
You can't stop the function from being invoked by anyone who knows the URL.
You can stop the function from doing something harmful by only allowing it to perform its intended action by requiring that an authenticated user call it, assuming you trust that user.
There is an example of requiring authentication in the official code samples here.
Related
I have been trying to use express-openid-connect for the last few days with no success. I am able to get the flow to work when hard coding my params. However, my goal is to be able to dynamically call auth() depending on the user being logged in. My initial attempt included calling
app.use(auth(functionThatGetsParams()));
Using the approach above, express complains that secret is required. For some reason, the auth call is getting called before anything else is resolved.
I also tried doing a few different ways,
app.use((req,res, next)=> process.env.secret = 'hello');
app.use(auth({secret: process.env.secret}));
The example above also returns the secret issue. However, setting process.env.secret outside of app.use, works fine.
My issue seems to be related to the things I do in the app.use block. The approach I am looking to use is have a call that resolves who my user is and based off of that gets the right settings.
app.use(ConnectionResolver.resolve);
I use a call similar to the above which is basically a handler that does some async stuff to get the client info and right settings then ends with next().
I would expect that then calling
app.use(auth(ConnectionManager.getAuthSettings()));
Would return the auth settings I need, but when I debug, it looks like this line gets called before anything else, so then secret is missing as the error says.
One other option I believe I may have seen online is creating a list of auth calls for each client, which I can then use for authentication, but I have not seen any examples of how that works.
Does anyone have any ideas on how this might be possible? The environment I am in is multi tenant. So I need to be able to dynamically use a certain auth config depending on the user making the call.
Any help would be greatly appreciated.
You are misunderstanding the concept of middleware.
the auth function, is a middleware factory function, it gets a set of options and returns a middleware function based on those options.
The function passed to the use method of the express app, will execute only when an incoming request will arrive.
When you do app.use(auth(getParams())) what happens is that when your server is starting, it will call getParams function, pass the result to auth function which in turn will return the auth middleware function that will be passed to the app.use function.
Once a request will arrive, the auth middleware (the one returned by the auth factory function) will execute.
You don't need to use auth conditionally. You should set it up, and then you can use the requiresAuth middleware provided by express-openid-connect package to protect your paths that requires authorization/authentication.
If your secret is loading asynchronically, wrap your entire express app setup in a bootstrap function, load your secret and only then call the server bootstrap function.
async function loadSecret() {
//load secret from external source
}
function bootstrapServer(secret) {
const app = express()
app.use(auth({ ..., secert }))
app.get('protected', requiresAuth(), (req, res) => {
// your protected route, will automatically return 401 if not authenticated
})
app.get('non-protected', (req, res) => {
// This route will be open to all without authentication
})
}
Quick background
I am building an API with Node.js, Express and Mongoose. The authentication I implemented works with the passport-headerapikey package. I search the DB for the user with the api-key and add that user to the req Object. Thus ensuring my knowledge about the identity the whole time until the request ends.
Authorization
Let's get to the issue.
Up until now I called an authorize() function in every endpoint manually before doing anything. Like so:
router.post('/', (req, res) => {
autorize('admin', req.user.role) // method is called at every route manually
.then(() => {
... do stuff here
})
.catch(err => req.status(403).send())
}
My colleague said to me it is not a good solution and, rather than just securing the endpoint itself, I should have a session management that makes the current user available globally so that I can authorize at any point in every function individually.
Meaning:
A Method createUser(obj) could then call inside itself an authorization method or check for a condition like so:
createUser(obj) {
if (currentUser.role !== 'admin') {
return false
}
obj = new User(obj)
return obj.save()
}
That would mean I would return false in every function if a condition is met. Accessing the globally available currentUser for that session. (e.g. globalCurrentUser.role !== admin or something along those lines)
My question(s)
Is it bad practice to just secure endpoints and not functions?
Can't I just require an extra param "auth" with every function, so that when called it needs to receive the currentUser.role like in my authorize() function or it returns false? That means I pass the user manually to every function or it will simply fail
If I should have a session management for global access of the user during the request: Do you have suggestions for frameworks?
Thanks in advance,
Benno
Authentication and authorisation are two different things and should be treated separately. Authentication says "who are you?" and authorisation says "do you have permission?". Baring in mind that Express is designed entirely around middleware, I would do the following.
Abstract your authentication into a single piece of middleware which runs for all your endpoints using app.use() before you mount your router / routes.
Create an authorisation function which can be called from anywhere, it takes a user (or id or whatever you have) and a role, and it then checks if the user has that role.
Think of it like this, your authorisation will never change, it is core to your application. If you decided to ditch Expressjs and use Koa or move from traditional HTTP requests to Web Sockets you wouldn't want to change your authorisation logic. But your authentication may well change, you may wish to no longer use a header to store the token, perhaps you switch to cookies or something else entirely, you may wish to change this.
You'll end up with a piece of global middlware which checks an auth token and attaches the user object to the req. Then you'll have a utility function called something like userHasRole which will be called at any endpoint which requires a specific role within the application. You're then free to check permissions at any point in the application. This may be in very different places across your application, for instance you might check if they're an admin at the beginning of a request to some admin dashboard, but you might check permissions later on if they try to access a particular resource. When accessing a particular resource you might want to let them through and determine at the last minute if they have access to the resource. (It's hard to give a specific example without knowing more about your application).
In some instances it might be suitable to check at the beginning of the business logic, in other places it might make sense to check later on. This shouldn't matter, you should be able to run this check whenever you need to. This will depend entirely on the business logic and placing it in every single function ever may be useless if it's just formatting a string output, but it might be useful when trying to pull out a DB record.
Okay, so atm i have a frontend application built with Nuxt JS using Axios to do requests to my REST API(separate).
If a user does a search on the website the API URL is visible in XMLHttprequests so everyone could use the API if they want to.
What is the best way of making it so that only users that search through my website gets access to the API and people that just directly to the URL gets denied. I suppose using some sort of token system, but what is the best way to do it? JWT? (Users never log in so there is no "authentication")
Thanks!
IMO, you CANNOT block other illegal clients accessing your
backend as you describe that the official client and other illegal have the same knowledge about your backend.
But you can make it harder for illegal clients to accessing your backend through some approach such as POST all requests, special keys in header, 30-minutes-changed token in header and server-side API throttling by client IP.
If the security of the search API is really important, authenticate it by login; if not, just let it go since it is not in your critical path. Let's focus on other important things.
I'm in the same "boat" and my current setup is actually in VueJs but before even come to StackOverflow I developed a way to actually, the frontend calls the server and then the server calls the API, so in the browser, you will only see calls to the server layer that, the only constraint is that the call must come from the same hostname.
backend is handled with expressJs and frontend with VueJs
// protect /api calls to only be originated from 'process.env.API_ALLOW_HOST'
app.use(api.allowOnlySameDomainRequests());
...
const allowHostname = process.env.API_ALLOW_HOST ||'localhost';
exports.api = {
...
allowOnlySameDomainRequests: (req, res, next) => {
if(req.url.startsWith('/api') && req.hostname === allowHostname) {
// an /api call, only if request is the same
return next();
} else if (!req.url.startsWith('/api')) {
// not an /api call
return next();
}
return res.redirect('/error?code=401');
},
...
};
In our case, we use Oauth2 (Google sign through passportJs) to log in the user, I always have a user id that was given by the OAuth2 successful redirect and that user id is passed to the API in a header, together with the apikey... in the server I check for that userid permissions and I allow or not the action to be executed.
But even I was trying to find something better. I've seen several javascript frontend apps using calls to their backend but they use Bearer tokens.
As a curious user, you would see the paths to all the API and how they are composed, but in my case, you only see calls to the expressJs backend, and only there I forward to the real API... I don't know if that's just "more work", but seemed a bit more "secure" to approach the problem this way.
For example, I have a REST api endpoint written in Node.js. It can be accessed from a webpage for non-technical users, and it can also be accessed through command line using the curl command. It doesn't require any credentials to access it because it is intended to be open for anyone to access it. The problem I am trying to solve is how can I prevent someone maliciously access this REST API endpoint, for example pinging this api endpoint over and over again, or how to prevent ddos attacks.
Not necessary a programming question, let me know if there is a better place to ask this.
Setup Rate Limiting if you cant have an auth on it.
You can use this if you are using express https://www.npmjs.com/package/express-rate-limit
Preventing DDOS is not that easy without using solutions like CloudFlare.
To secure your REST api, you can use middleware if you use express
const checkAuth = (req, res, next) => {
// logic for checking auth
if (authorized) {
return next();
}
res.status(401).send('NEED AUTH');
};
router.post('/login', checkAuth, (req, res, next) => {
// actual logic for login
});
Update: regarding #Akarsh's answer,
you can use multiple middleware before actual logic. For example, one for auth check, and one for rate limit
router.post('/logic', checkAuth, rateLimit, (req, res, next) => {});
You say you want it to be open, but then you say you want it to be sort of open!
Throttling / auth tokens. Choose at least one, pref both.
Pinging and DOS attacks are different and have nothing to do with your API as such. Unless your info is valueable / highly competitive, something as simple as IP banning will go a long way.
I am very new to web development and Node.js / Express. I have an Express server. In my client I send a GET request for some data in the DB. The GET request is handled by
app.get( '/pathname', controller.getsomedata );
The problem is, the user can now type in the URL domainname.com/pathname in the browser and get directed to that pathname. If they add in a certain queries domainname.com/pathname?query, they are able to retrieve data from the DB (this is supposed to happen), but I would prefer if GET requests to /pathname only occur from within the client code, not when the user enters it in the browser.
Is there a better way to do my GET request? Or is there a way to restrict users from accessing /pathname.
I apologize for the newbie question, but I don't know how to word it well enough to do a google search for the solution. Thank you!
It's impossible to do that. If your client-side code is able to access something, malicious user can do that as well.
You can mitigate the issue by using custom HTTP header or something like that, but it's better to validate all data on the server-side.
Allow whole client request as DB query may cause security issues. So be sure to validate query parameters and use them as DB query conditions.
If you want to query DB freely from HTTP query parameter, you should prepend authentication/authorization to the route.
app.get( '/pathname', function(req, res, next) {
if (confirmThisRequestIsFromMe(req)) {
next();
} else {
res.send(401);
}
}, controller.getsomedata );