Meteor Security Permissions on Uploaded Images - node.js

I'm creating a Meteor chat app. I want users to be able to send images to each other, but users not in the group/conversation shouldn't be able to see the image. My first thought was to give an image a unique ID and store the image with this ID in a public folder in my or a 3rd party server.
For example, if the user uploads an image called "name.jpg", it could be stored in Amazon S3 as A3eedAcRCqCa32451.jpg. That way, anyone with the ID can access the image, but the only people with access to the ID would be those in the group chat, since I can ensure secure access using Meteor's publish and subscribe rules. However, this doesn't feel safe to me. Is my intuition right?
If yes, how else would I do it? I searched online and on StackOverflow and couldn't find another simple way to achieve this.

You usually have two things you can do when considering granting access to resources:
Authentication and access control
Indirect object references with sufficient entropy to make brute force attacks hopeless.
Point 1 is more or less obvious.
Point 2 is actually what you have already on your mind. To reason about the security of the second approach let's consider following:
The entropy of A3eedAcRCqCa32451.jpg is about 80 bits.
The count of the domain is 2^80 = 1208925819614629174706176.
An attacker can try to guess the secret.
Let's say, he can make 10 guesses a second and on average he will guess after |domain|/2 tries.
It would take the attacker ~2 mln years to guess.
Now the domain of 80 bits is a bit small from the security perspective. Make it 128 bits by using type 4 UUIDs. I believe you see where this is going.

It depends on the security level you want...
A more secure solution would be to store in a collection the imageId and userId of people that can access the image. When someone wants to access it, you just have to check if he's in the list of allowed users.
Then as you said you can use a 3rd party storage (personally I'm using ostrio:files with Dropbox integration, the docs about it aren't up to date but I made a pull request which was accepted on the dev branch with a working example, you can take a look at it here.)
The nice thing with ostrio:files is that it offers built-in functions like onAfterUpload or interceptDownload in which you can store data about access for the first and check if access is allowed for the second.

Related

Encrypt all user data in my web application

This is not a typical StackOverflow question as it is quite specific and bound to my current project. Given my project (GitHub link), I would like to encrypt or handle all user data in a way that impedes me as a service provider to view data of specific users. This would probably not be feasible in a typical webapp with a rational SQL database. I am using Redis with data that is basically structured as follows:
Users can view their data filtered by two dimensions: A time range and a domain. These are further grouped by another dimension, which are multiple charts. So there is data for countries, top landing pages, etc (It's a web analytics app). Internally of course I also need to have the user baked in as dimension in the key that holds data for a chart and of course there is some indexing stuff going on.
Now here is the idea: I could hash the access key for this single charts - I am only doing direct key access anyway and no scanning (filtering over keys). Furthermore I would only save the hashed username in the database so the username becomes the missing information I don't have to retrieve the payloads.
This would leave me with the cleartext payloads, which represent specific charts given by specific user selections (Yes, I only save the user data in an aggregated form btw) but I would have no reasonable way to map a single chart to a specific user or domain. Given I have ~70 integrated users at the moment, it would be not feasible to try to manually map data points to specific users (But I could still see all domains a "user" uses).
Of course this is relying on the username being somewhat a secret and I would only save the hashed username to the database and only handle the cleartext username in ram. I can still greet the user since the cleartext username is saved in a cookie :-)
With usernames being too short and having almost no entropy of course I could brute force my own database in order to regain the missing links and access to all data individual users have. But before doing this the more obvious way to "cheat" would be to just run another software (without that hashing) on the server but still stating everything is encrypted. So my point is that the presented solution is good enough for a hosted service.
Does this sound plausible? Would such an approach add an additional layer of security or be meaningless because it is too easy to circumvent?
In my opinion I could compare this with locking a bicycle with a very cheap lock. Even if the lock is easily breakable it does have a strong symbolic meaning that someone that breaks the lock is doing something worse than stealing a bicycle that has no lock at all. So even it is not possible to protect user data from a hosting provider, it is possible to make the work to do so more "dirty" and such socially and legally less acceptable. Does this makes sense? :-)
So my question is: security by obscurity or sound approach?
Cheers!

Security of public S3 objects with random keys

I have an S3 bucket containing objects that I want to share with users of a website. I know I can use something like Query String Authentication to provide secure access to the objects, but what if I instead make each object publicly-readable yet "hidden" behind a complex key (i.e. URL) containing a cryptographically-strong random number? If the containing bucket disallows listing of objects, there wouldn't be a way to guess or discover the URLs, correct? Or is there some security hole I'm overlooking?
Side note: my first thought was to use UUIDs in the keys, but I read that they can apparently be predicted, given a few previous instances. That said, I don't have an understanding of how easily that can be done. If it's non-trivial, I probably wouldn't worry too much about using them instead of a strong random number...
The problem is if the once shared URL gets into the hands of another user (say via sharing). If you ensure the URL is kept sufficiently secret, it is ok with this approach (say you return the URL to a user via https, and this user dont share it).
Any loophole here will cause a security hole - and here is where the query string based signature scheme is helpful, since the signatures are made to expire after a fixed time and so any re-sharing wont also harm you.
You can use UUIDs (ensure they dont end up duplicating, by regenerating another one if the new one collides). They are probably as difficult (or more) to guess as any other 8-letter password.
The standard way to do what you want is to generate pre-signed URLs for each of the objects you want to share. If you make them with a short lifetime, then they cannot be shared outside that time period. All of the AWS-provided SDKs have support for this feature.

Possible solutions for keeping track of anonymous users

I'm currently developing a web application that has one feature while allows input from anonymous users (No authorization required). I realize that this may prove to have security risks such as repeated arbitrary inputs (ex. spam), or users posting malicious content. So to remedy this I'm trying to create a sort of system that keeps track of what each anonymous user has posted.
So far all I can think of is tracking by IP, but it seems as though it may not be viable due to dynamic IPs, are there any other solutions for anonymous user tracking?
I would recommend requiring them to answer a captcha before posting, or after an unusual number of posts from a single ip address.
"A CAPTCHA is a program that protects websites against bots by generating and grading tests >that humans can pass but current computer programs cannot. For example, humans can read >distorted text as the one shown below, but current computer programs can't"
That way the spammers are actual humans. That will slow the firehose to a level where you can weed out any that does get through.
http://www.captcha.net/
There's two main ways: clientside and serverside. Tracking IP is all that I can think of serverside; clientside there's more accurate options, but they are all under user's control, and he can reanonymise himself (it's his machine, after all): cookies and storage come to mind.
Drop a cookie with an ID on it. Sure, cookies can be deleted, but this at least gives you something.
My suggestion is:
Use cookies for tracking of user identity. As you yourself have said, due to dynamic IP addresses, you can't reliably use them for tracking user identity.
To detect and curb spam, use IP + user browser agent combination.

What is a simple and secure way to transmit a login key from one website to another while redirecting a user?

I want to create a portal website for log-in, news and user management. And another web site for a web app that the portal redirects to after login.
One of my goals is to be able to host the portal and web-app on different servers. The portal would transmit the user's id to the web-app, once the user had successfully logged in and been redirected to the web app. But I don't want people to be able to just bypass the login, or access other users accounts, by transmitting user ids straight to the web app.
My first thought is to transmit the user id encrypted as a post variable or query string value. Using some kind of public/private key scenario, and adding a DateTime stamp to key to make it vary everytime.
But I haven't done this kind of thing before, so I'm wondering if there aren't better ways to do this.
(I could potentially communicate via database, by having the portal store the user id with a key in a database and passing that key to the web app which uses it to get the user id from that database. But that seems crazy.)
Can anyone give a way to do this or advice? Or is this a bad idea all-together?
Thanks for your time.
Basically, you are asking for a single-sign-on solution. What you describe sounds a lot like SAML, although SAML is a bit more advanced ;-)
It depends on how secure you want this entire thing to be. Generating an encrypted token with embedded timestamp still leaves you open to spoofing - if somebody steals the token (i.e. through a network sniffing) he will be able to submit his own request with the stolen token. Depending on the time to live you will give your token this time can be limited, but a determined hacker will be able to do this. Besides you cannot make time to live to small - you will be rejecting valid requests.
Another approach is to generate "use once" tokens. This is 'bullet proof' in terms of spoofing, but it requires coordination among all the servers within the server farm servicing your app, so that if one of them processed the token the other ones would reject it.
To make it really secure for the failover scenarios, etc. it would require some additional steps, so it all boils down to how secure you need it to be and how much you want to invest in building it up
I suggest looking at SAML
PGP would work but it might get slow on a high-traffic site
One thing I've done in the past is used a shared secret method. Some token that only myself and the other website operator knows concatenated to something identifying the user (like their user name), then hash that with a checksum algorithm such as SHA256 (you can use MD5 or SHA1 which usually are more available but they are much easier to break)
The other end should do the same thing as above. Take the passed identifying information and checksum it. Compare that to the passed checksum, if they match the login is valid.
For added security you could also concat the date or some other rotating key. Helps to run SSL on both sides as well.
In general, the answer resides somewhere in SHA256 / MD5 / SHA1 plus shared secret based on human actually has to think. If there is money somewhere, we may assume there are no limits to what some persons will do - I ran with [ a person ] in High School for a few months to observe what those ilks will do in practice. After a few months, I learned not to be running with those kind. Tediously avoiding work, suddenly at 4 AM on Saturday Morning the level of effort and analytical functioning could only be described as "Expertise" ( note capitalization ) There has to be a solution else sites like Google and this one would not stand the chance of a dandelion in lightning bolt.
There is a study in the mathematical works of cryptography whereby an institution ( with reputable goals ) can issue information - digital cash - that can exist on the open wire but does not reveal any information. Who would break them? My experience with [ person ]
shows that it is a study in socialization, depends on who you want to run with. What's the defense against sniffers if the code is already available more easily just using a browser?
<form type="hidden" value="myreallysecretid">
vis a vis
<form type="hidden" value="weoi938389wiwdfu0789we394">
So which one is valuable against attack? Neither, if someone wants to snag some Snake Oil from you, maybe you get the 2:59 am phone call that begins: "I'm an investor, we sunk thousands into your website. I just got a call from our security pro ....." all you can do to prepare for that moment is use established, known tools like SHA - of which the 256 variety is the acknowledged "next thing" - and have trace controls such that the security pro can put in on insurance and bonding.
Let alone trying to find one who knows how those tools work, their first line of defense is not talking to you ... then they have their own literature - they will want you to use their tools.
Then you don't get to code anything.

Best way to limit (and record) login attempts

Obviously some sort of mechanism for limiting login attempts is a security requisite. While I like the concept of an exponentially increasing time between attempts, what I'm not sure of storing the information. I'm also interested in alternative solutions, preferrably not including captchas.
I'm guessing a cookie wouldn't work due to blocking cookies or clearing them automatically, but would sessions work? Or does it have to be stored in a database? Being unaware of what methods can/are being used so I simply don't know what's practical.
Use some columns in your users table 'failed_login_attempts' and 'failed_login_time'. The first one increments per failed login, and resets on successful login. The second one allows you to compare the current time with the last failed time.
Your code can use this data in the db to determine how long it waits to lock out users, time between allowed logins etc
Assuming google has done the necessary usability testing (not an unfair assumption) and decided to use captchas , I'd suggest going along with them.
Increasing timeouts is frustrating when I'm a genuine user and have forgotten my password (with so many websites and their associated passwords that happens a lot , especially to me)
Storing attempts in the database is the best solution IMHO since it gives you the auditing records of the security breach attempts. Depending on your application this may or may not be a legal requirement.
By recording all bad attempts you can also gather higher level information, such as if the requests are coming from one IP address (i.e. someone / thing is attempting a brute force attack) so you can block the IP address. This can be VERY usefull information.
Once you have determined a threshold, why not force them to request the email to be sent to their email address (i.e. similar to 'I have forgotten my password'), or you can go for the CAPCHA approach.
Answers in this post prioritize database centered solutions because they provide a structure of records that make auditing and lockout logic convenient.
While the answers here address guessing attacks on individual users, a major concern with this approach is that it leaves the system open to Denial of Service attacks. Any and every request from the world should not trigger database work.
An alternative (or additional) layer of security should be implemented earlier in the req/ res cycle to protect the application and database from performing lock out operations that can be expensive and are unnecessary.
Express-Brute is an excellent example that utilizes Redis caching to filter out malicious requests while allowing honest ones.
You know which userid is being hit, keep a flag and when it reaches a threshold value simply stop accepting anything for that user. But that means you store an extra data value for every user.
I like the concept of an exponentially increasing time between attempts, [...]
Instead of using exponentially increasing time, you could actually have a randomized lag between successive attempts.
Maybe if you explain what technology you are using people here will be able to help with more specific examples.
Lock out Policy is all well and good but there is a balance.
One consideration is to think about the consruction of usernames - guessable? Can they be enumerated at all?
I was on an External App Pen Test for a dotcom with an Employee Portal that served Outlook Web Access /Intranet Services, certain Apps. It was easy to enumerate users (the Exec /Managament Team on the web site itself, and through the likes of Google, Facebook, LinkedIn etc). Once you got the format of the username logon (firstname then surname entered as a single string) I had the capability to shut 100's of users out due to their 3 strikes and out policy.
Store the information server-side. This would allow you to also defend against distributed attacks (coming from multiple machines).
You may like to say block the login for some time say for example, 10 minutes after 3 failure attempts for example. Exponentially increasing time sounds good to me. And yes, store the information at the server side session or database. Database is better. No cookies business as it is easy to manipulate by the user.
You may also want to map such attempts against the client IP adrress as it is quite possible that valid user might get a blocked message while someone else is trying to guess valid user's password with failure attempts.

Resources