Include header when chunks are fetched with code splitting - javascript

I've used code splitting to seprate restricted parts of my app into different chunks. This is working great so far, now I would like to ensure that the files themselves don't get served unless authenticated. I was thinking of using ngx_http_auth_request_module
http://nginx.org/en/docs/http/ngx_http_auth_request_module.html#auth_request
Which allows to send a sub-request before serving certain files. How can I ensure that certain headers are always send as part of the HTTP request when React wants to fetch the necessary chunks?

I have trouble understanding why you would need to prevent unauthenticated malicious users to have access to your static chunks.
Dynamic imports and code splitting are mainly used to reduce the bundle size for large applications as users won't necessarily need everything.
In order to secure your app you need to prevent users from seeing or tampering with data they do not have access to. This means the security lies with the API your app is talking to.
What I do:
Reject unauthenticated requests to the API
Keep a token client-side on authentication
Pass and check the token on all requests
Burn the token when obsolete and redirect to login
Notify, redirect users when they do not have access to some data or better not displaying content they do not have access to
I'm sure you already did what I wrote above, what I want to emphasize is that chunks are basically empty UI filled with data from the secured API.
Let's say I have bad intentions and I bypass client-side routing in order to have access to the restricted chunk. It will be an empty UI with secured API routes, I won't be able to do anything with it.
In case you have a very specific need, you might need to write a webpack plugin.

about the ensure request
One of webpack 's properties is that it can fetch only necessary chunks when loading pages.You can just use like require.ensurn to query chunks when necessary,so there is no need to ensure the certain headers.
ngx_http_auth_request_module
Ngx_http_auth_request_module and sub-request are always used to fetch web file in server.It's always used as backend authentication module.Here is the data flow direction in nginx.
When you download file, the download request will be passed to the server, then server return the override Http Request to Nginx,then Nginx will find the exact file.
The ngx_http_auth_request_module allows to send request to back server(like php .tomcat), and based on the request to pass or not, if pass, you will be able to fetch file in the back server.
nginx-----load speed
The nginx always fetch static file, like index.html.If have to validate the permission for every js/css everytime,then fetch it throw,thd loading speed for page will be very slow.
about how to authenticate
Since you have separated app.Here is a little suggestions.You can get the authenticated request by only import restricted parts in the authenticated file.And the webpack will automatically handle the rest.
fetch data from the server in the non-restricted part with information to authenticate like this:
http://.../api/auth?info=...
based on the infos in server to authenticate, and pass other infos like type back to the frontend
based on the type information to view .
if (this.props.type === "restrict"){
<restrict component/>
} else {
<non-restrict component/>
}

Related

Authenticate requests from frontend that doesn't have tokens

Not sure if the title summarises my question well.
Basically, I am trying to authenticate routes such as checking if user exists etc. I only want to allow
requests coming from my frontend application to be approved, but, since no user is signed in there is no token to send.
Api request -
mywebiste/checkUser/email
This route is unprotected on my backend because no user is logged in.
BUT I want to protect this route, in such a way that it's accessible only from the frontend.
Some ideas I came up with were adding specific headers tag from the frontend and check them on the backend, but that could be easily replicated, is there something more secure like using tokens etc.
I am using React and Node.js
Same origin policy is going to give you some basic protection, but basically if an API endpoint is exposed publicly, it's exposed publicly. If you don't want that route to be publicly accessible you need to add access control.
If you use that route to check if a user is already registered, you could, for example, merge it with the user registration route and send a different error code if the user already exists (which is not a great idea because it leaks which emails are registered on your system).
You can verify that a request was originated by a user (by authenticating him) but you cannot verify that a request comes from a particular client because of these two reasons :
If you include some API key in your client (web page or other), it's easily retrievable by everyone (the best thing you could do is offuscate it which makes things slightly harder but still possible)
If you send an API key over the network it's easily retrievable as well
The only thing you could do is prevent other web pages from calling your backend on behalf of the user, by using CORS (which is actually active by default if you dont specify an Access-Control-Allow-Origin header)
I ended up creating a kind of working solution, so basically, I create a new base64 string on my frontend and attach that to the header while making a request to the backend. The base64 string is different every minute, so even if the header is copied, it differs every minute and is combined with your secret key.
I have made a package so that people can use it if they want - https://github.com/dhiraj1site/ncrypter
You can use it like so
var ncrypter = require('ncrypter');
//use encode on your frontend with number of seconds and secret key
var encodedString = ncrypter.encrypt(2, 'mysecret1')
//use decode on your backend with same seconds and secret
var decodedString = ncrypter.decrypt(encodedString, 2, 'mysecret1');
console.log('permission granted -->', decodedString);

Nextjs global api calls

I am developing react based web application using NEXT.js,. As specified in NEXT.js, documentation to fetch the data before page loads, i am putting required action dispatch code in getInitialProps of the specific page. but some data fetching calls(action dispatch) like fetching authenticated user's data will be common to all the pages, so is there any way to dispatch such actions from single place before page load.
Thanks!
update, now you can use getServerSideProps and nextjs provides an easy way to get user cookie : How to use cookie inside `getServerSideProps` method in Next.js?
--------- old answer ---------
simple answer: use cookie, code refer https://github.com/nextjs-boilerplate/next.js-boilerplate https://github.com/nextjs-boilerplate/next-fetch
-- details --
I had the same question when I first adopt next.js, as it worked in react, people prefer use a token to tag authed user and fetch always run in front-end. But as next.js made ssr a build in feature, I tried and find auth by cookie is possible, and I start https://github.com/nextjs-boilerplate/next.js-boilerplate and split out a fetch based on cookie https://github.com/nextjs-boilerplate/next-fetch
-- how it works --
1.client side fetch: use fetch option option.credentials = 'include' and option.headers.Cookie=document.cookie will patch cookie into your request. Bnd when fetch back result, this become wired, you cannot access cookie header, so you have to use another header and additional logic needed in backend logic like res.header('custom-set-cookie', res.getHeader('set-cookie'))
2.server side fetch: first you need the express request object, and get cookie like req.headers.cookie, then pack it into fetch request option. when fetch back, get cookie like r.headers._headers[cookieHeaderName] and pack into response res.header('set-cookie', setCookie)
then after you pack this transfer up, you can simply call a json api and cookie will automatically transfered. And if you don't need to change cookie through header (you can through js), you can ommit the extra handle like res.header('custom-set-cookie', res.getHeader('set-cookie')) in api
you can try my ssr login here http://nextjs.i18ntech.com/login

App API design advice specifically around security

I'm building an app and would like some feedback on my approach to building the data sync process and API that supports it. For context, these are the guiding principles for my app/API:
Free: I do not want to charge people at all to use the app/API.
Open source: the source code for both the app and API are available to the public to use as they wish.
Decentralised: the API service that supports the app can be run by anyone on any server, and made available for use to users of the app.
Anonymous: the user should not have to sign up for the service, or submit any personal identifying information that will be stored alongside their data.
Secure: the user's data should be encrypted before being sent to the server, anyone with access to the server should have no ability to read the user's data.
I will implement an instance of the API on a public server which will be selected in the app by default. That way initial users of the app can sync their data straight away without needing to find or set up an instance of the API service. Over time, if the app is popular then users will hopefully set up other instances of the API service either for themselves or to make available to other users of the app should they wish to use a different instance (or if the primary instance runs out of space, goes down, etc). They may even access the API in their own apps. Essentially, I want them to be able to have the choice to be self sufficient and not have to necessarily rely on other's providing an instance on the service for them, for reasons of privacy, resilience, cost-saving, etc. Note: the data in question is not sensitive (i.e. financial, etc), but it is personal.
The user's sync journey works like this:
User downloads the app, and creates their data in the process of using the app.
When the user is ready to initially sync, they enter a "password" in the password field, which is used to create a complex key with which to encrypt their data. Their password is stored locally in plain text but is never sent to the server.
User clicks the "Sync" button, their data is encrypted (using their password) and sent to the specified (or default) API instance and responds by giving them a unique ID which is saved by the app.
For future syncs, their data is encrypted locally using their saved password before being sent to the API along with their unique ID which updates their synced data on the server.
When retrieving synced data, their unique ID is sent to the API which responds with their encrypted data. Their locally stored password is then used to decrypt the data for use by the app.
I've implemented the app in javascript, and the API in Node.js (restify) with MongoDB as a backend, so in practice a sync requests to the server looks like this:
1. Initial sync
POST /api/data
Post body:
{
"data":"DWCx6wR9ggPqPRrhU4O4oLN5P09onApoAULX4Xt+ckxswtFNH/QQ+Y/RgxdU+8+8/muo4jo/jKnHssSezvjq6aPvYK+EAzAoRmXenAgUwHOjbiAXFqF8gScbbuLRlF0MsTKn/puIyFnvJd..."
}
Response:
{
"id":"507f191e810c19729de860ea",
"lastUpdated":"2016-07-06T12:43:16.866Z"
}
2. Get sync data
GET /api/data/507f191e810c19729de860ea
Response:
{
"data":"DWCx6wR9ggPqPRrhU4O4oLN5P09onApoAULX4Xt+ckxswtFNH/QQ+Y/RgxdU+8+8/muo4jo/jKnHssSezvjq6aPvYK+EAzAoRmXenAgUwHOjbiAXFqF8gScbbuLRlF0MsTKn/puIyFnvJd...",
"lastUpdated":"2016-07-06T12:43:16.866Z"
}
3. Update synced data
POST /api/data/507f191e810c19729de860ea
Post body:
{
"data":"DWCx6wR9ggPqPRrhU4O4oLN5P09onApoAULX4Xt+ckxswtFNH/QQ+Y/RgxdU+8+8/muo4jo/jKnHssSezvjq6aPvYK+EAzAoRmXenAgUwHOjbiAXFqF8gScbbuLRlF0MsTKn/puIyFnvJd..."
}
Response:
{
"lastUpdated":"2016-07-06T13:21:23.837Z"
}
Their data in MongoDB will look like this:
{
"id":"507f191e810c19729de860ea",
"data":"DWCx6wR9ggPqPRrhU4O4oLN5P09onApoAULX4Xt+ckxswtFNH/QQ+Y/RgxdU+8+8/muo4jo/jKnHssSezvjq6aPvYK+EAzAoRmXenAgUwHOjbiAXFqF8gScbbuLRlF0MsTKn/puIyFnvJd...",
"lastUpdated":"2016-07-06T13:21:23.837Z"
}
Encryption is currently implemented using CryptoJS's AES implementation. As the app provides the user's password as a passphrase to the AES "encrypt" function, it generates a 256-bit key which which to encrypt the user's data, before being sent to the API.
That about sums up the sync process, it's fairly simple but obviously it needs to be secure and reliable. My concerns are:
As the MongoDB ObjectID is fairly easy to guess, it is possible that a malicious user could request someone else's data (as per step 2. Get sync data) by guessing their ID. However, if they are successful they will only retrieve encrypted data and will not have the key with which to decrypt it. The same applies for anyone who has access to the database on the server.
Given the above, is the CryptoJS AES implementation secure enough so that in the real possibility that a user's encrypted data is retrieved by a malicious user, they will not realistically be able to decrypt the data?
Since the API is open to anyone and doesn't audit or check the submitted data, anyone could potentially submit any data they wish to be stored in the service, for example:
Post body:
{
"data":"This is my anyold data..."
}
Is there anything practical I can do to guard against this whilst adhering to the guiding principles above?
General abuse of the service such as users spamming initial syncs (step 1 above) over and over to fill up the space on the server; or some user's using disproportionately large amounts of server space. I've implemented some features to guard against this, such as logging IPs for initial syncs for one day (not kept any longer than that) in order to limit a single IP to a set number of initial syncs per day. Also I'm limiting the post body size for syncs. These options are configurable in the API however, so if a user doesn't like these limitations on a public API instance, they can host their own instance and tweak the settings to their liking.
So that's it, I would appreciate anyone who has any thoughts or feedback regarding this approach given my guiding principles. I couldn't find any examples where other apps have attempted a similar approach, so if anyone knows of any and can link to them I'd be grateful.
I can't really comment on whether specific AES algorithms/keys are secure or not, but assuming they are (and the keys are generated properly), it should not be a problem if other users can access the encrypted data.
You can maybe protect against abuse, without requiring other accounts, by using captchas or similar guards against automatic usage. If you require a catcha on new accounts, and set limits to all accounts on data volume and call frequency, you should be ok.
To guard against accidental clear-text data, you might generate a secondary key for each account, and then check on the server with the public secondary key whether the messages can be decrypted. Something like this:
data = secondary_key(user_private_key(cleartext))
This way the data will always be encrypted, and in worst case the server will be able to read it, but others wouldn't.
A few comments to your API :) If you're already using HTTP and POST, you don't really need an id. The POST usually returns a URI that points to the created data. You can then GET that URI, or PUT it to change:
POST /api/data
{"data": "..."}
Response:
Location: /api/data/12345
{"data": "...", "lastmodified": "..." }
To change it:
PUT /api/data/12345
{"data": "..."}
You don't have to do it this way, but it might be easier to implement on the client side, and maybe even help with caching and cache invalidation.

Get an access token without being connected to facebook

I need to retrieve a facebook page's list of posts (feed) using their javascript SDK, just like they explain in their docs: https://developers.facebook.com/docs/graph-api/reference/v2.4/page/feed
/* make the API call */
FB.api(
"/{page-id}/posts",
function (response) {
if (response && !response.error) {
/* handle the result */
}
}
);
I need it to be my website's "news section", so users should see it even if they are not connected to facebook.
The problem
Cool, but there is a problem... It returns: An access token is required to request this resource.
Holy cow... I'd like to get some access token for you #facebook, but my app doesn't make use of your authentication tools/plugins.
ANYWAY, I tried with FB.getLoginStatus(); but doesn't work, because the only way it can return an access_token is if the user is actually connected to the application. My users may not even be logged to facebook!
So, ¿How can I get an access_token to be stored into a variable, and later be used to get /{my-page}/posts?
I've already payed a look to this SO question, but it doesn't solves my problem, simply because there are no such "generic tokens".
I've also read https://developers.facebook.com/docs/facebook-login/access-tokens/ and that also relies on tokens generated through facebook login methods... So, can't I display a list of fb page's posts in my website, without being connected into facebook, hence an application?
ADD: My app is build with angularJS, I'm not dealing with server-side code. I shall rely purely on javascript methods.
You could either use an page or an app access token, but as you'd be using them on the client-side, neither of them are an option.
See
https://developers.facebook.com/docs/facebook-login/access-tokens#apptokens
https://developers.facebook.com/docs/facebook-login/access-tokens#pagetokens
Note that because this request uses your app secret, it must never be made in client-side code or in an app binary that could be decompiled. It is important that your app secret is never shared with anyone. Therefore, this API call should only be made using server-side code.
I'd strongly recommend to build a simple server-side script (PHP for example) to proxy the request to the Graph API. You could then call this via AJAX for example and load the posts asynchronously (and alse get rid of the FB JS SDK!). There is NO way to handle this in a secure manner if you don't want to use FB Login for all of your users (which also doesn't make much sense IMHO).
I think it's straightforward :)
Since pages' posts are always public, it only needs a valid access token to retrieve page posts.
Quoting what you've written:
So, ¿How can I get an access_token to be stored into a variable, and later be used to get /{my-page}/posts?
You only require an access token.
My suggestion would be;
- Generate an access token for yourself (no extra permission needed)
- request page-id/posts
This way you don't require other users to be connected to facebook, you can simply requests page-id/posts to retrieve posts with access token you generated for yourself.
I hope it solves your problem :D
TIP: As long as posts are public, you only require a valid access token, it doesn't need to be user or page specific.

Where to place remote hooks (beforeRemote, afterRemote)code in yo loopback generated code

I'm using loopback generator to generate models and rest APIs service. Now I wanted to modify a rest api such that everytime the api is called, some spcific logging/actions are taken.
I've come to know that by using remote hooks(beforeRemote, afterRemote), we can specify actions to be taken for different remote method calls. But what I don't know is that where to place the code of remote hooks. In which file this code will go when the project has been created using 'yo loopback'.
You would add code to the files under /common/models.
If you are using a Person model. You would add the following code in /common/models/person.js:
If you want to protect the REST API from a non logged in user or anonymous user you should use ACL. Have a look here:
Define access control from the intermediate tutorial
Authentication, authorization, and permissions
The REST API will respond with codes if someone unauthorized tries to get access (depending on what you define), for example 401. Then in the app if you receive that code, you should redirect to the login.
Now, every time you create a new model with slc loopback:model, it will generate 2 files in the common/models folder. One is a .js and the ohter a .json. You have to add the code in the .js file for the remote hooks.
Have a look to this link that explains how to add remote methods.

Categories