Storing API Keys in react-native app with expo library - javascript

I have a question, please, if possible explain in simple terms. Im new to react native, what's the best way to store API keys in a secure way where they can't reverse engineer it and get the keys right away. Can I just retrieve it from the server side using a restapi to get the apikey only if user is signed in? I'm trying to upload pictures to aws storage, but I want to store the APIKey somewhere where it's difficult to retrieve at least by hackers. Also, is there a way to send images trough the server express.js(from react native to express app) how can I do it, so I can upload it to aws storage or even if it is possible on mongodb instead of aws storage.
For example:
const express = require("express");
const requireAuth = require("../middlewares/requireAuth");
const router = express.Router();
router.use(requireAuth); //make sure they are signed in
/**
* * GET: Api key for the amazon s3 bucket storage
*/
router.get("/apikey/amazonstorage", (req, res) => {
const APIKEY = process.env.APIKEY;
if (APIKEY) {
res.status(200).send(APIKEY);
} else {
res.status(502).send(null);
}
});
Thank you in advance

In general, the safest way to handle API secret keys is to store them on your backend server and have the server make those requests to the third party APIs for the client (and send the results back to the client if necessary).
From the React Native docs:
If you must have an API key or a secret to access some resource from your app, the most secure way to handle this would be to build an orchestration layer between your app and the resource. This could be a serverless function (e.g. using AWS Lambda or Google Cloud Functions) which can forward the request with the required API key or secret. Secrets in server side code cannot be accessed by the API consumers the same way secrets in your app code can.

Related

Allow admin panel of react-firebase web app to only specific IP addresses?

I want the page of website (e.g."https:// website-URL/admin") only accessible specific IP addresses.
I use firebase.google.com & Vercel.com etc. for hosting commonly along with NodeJS & React JS for backend.
Firebase hosting is delivered through Fastly, when using cloud functions for onCall and onRequest, you can access this from the header fastly-client-ip
Otherwise, if it is from the client, you can call a Cloud Function to check it, or do a remote check with a service, but these kinds of checks are purely cosmetic and can be bypassed if someone created a script to override it.
import publicIp from "public-ip";
export const getClientIp = async () => await publicIp.v4({
fallbackUrls: [ "https://ifconfig.co/ip" ]
});
npm package: https://www.npmjs.com/package/public-ip
beyond the above options, this feature is limited and not accessible to you directly within Firebase config.

how to hide nextjs api routes from being directly accessible through url?

Is there any way to make next.js API routes response data hidden when accessing it through URL? I want to hide the routes because there is some data I don't want to be directly accessed by the user.
Probably quick & simple way to protect the API routes is through the stateless session management libraries like iron-session with save / creation and destroy endpoints to validate and invalidate the Next JS api routes
Try this github example by Vercel. This might a be good starting point.
Remember: Always use a best authentication mechanism to protect any direct api route call with appropriate privileges in place. DYOR
There is no way to hide API routes from users through url in nextjs. In fact, nextjs API routes are publically available to anyone when you host the website without exporting and hosting from out folder. I ended making server-side routes using node express and then connected to the frontend in nextjs.
It is extremely unworthy effort to hide API routes. and for protecting essential data in API..there is CORS and Authentication methods can prevent noicy unwanted traffic I found brilliant blog on this
https://dev.to/a7u/how-to-protect-nextjs-api-routes-from-other-browsers-3838
You can set an authorization header that checks auth key everytime user access that API, that way normal user wont be able to access the page without knowing the auth key
In Next.js version 13 you can use middleware to stuck the user from directly checking the route of your api by checking the req.referer then only your app can call and api of your app. Auth token can also be use inside middleware.
https://nextjs.org/blog/next-13-1#nextjs-advanced-middleware
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(req: NextRequest) {
const url = req.nextUrl
const { pathname } = url
if (pathname.startsWith(`/api/`)) {
if (!req.headers.get("referer")?.includes(process.env.APP_URL as string)) {
return NextResponse.json({ message: 'Unauthorized' }, { status: 401 });
}
}
return NextResponse.next()
}
export const config = {
matcher: ['/((?!_next|fonts|examples|svg|[\\w-]+\\.\\w+).*)'],
}
process.env.APP_URL is the url of your app for example : http://localhost:3000

How can I hide S3 keys in a javascript (react native) application from the user?

I have an app that uploads files to an S3 bucket as part of it's functionality. I've gotten the app to work but the keys to the bucket are in the app files so a user can just see the keys if sufficiently motivated. I've already configured the key to have limited permissions but what can I do to prevent the user from seeing the keys at all or at least obfuscate them?
There is little you can do to hide them from your user if the frontend of your application needs to read them.
Depending on your use case there are a few scenarios you could take a look at.
Firstly you could replace the permanent credentials with temporary credentials, to do this you can make use of the service AWS Cognito.
By using this you can generate temporary credentials based on one of the following approaches:
If the user has to login, they can be attached to a cognito user that will have temporary credentials generated for them.
Cognito also supports an anonymous user (a guest user) which also grants temporary credentials.
Another approach you can take if the application is primarily write based is to use a pre signed S3 URL to upload objects. This would combine an API Gateway with a Lambda to generate this for you, therefore no credentials would be stored on the frontend. Take a look at this article for more information on how to handle the approach.
You need presigned URLs to provide upload functionality without exposing the keys. Suppose you have a backend, such as a Lambda function, that has the keys that allow uploading objects.
You need an endpoint on your backend that the app can reach. Then generate a signed URL:
const url = s3.createPresignedPost({
Bucket: <bucket>,
Fields: {
key: <filename>,
},
});
On the app's side, get this signed URL from the backend (I don't know about React native, this code is written for the browser but it should be easy to modify it to other environments if needed):
const url = await (await fetch("<backend url>")).json();
Create a form with the file:
const formData = new FormData();
formData.append("Content-Type", file.type);
Object.entries(data.fields).forEach(([k, v]) => {
formData.append(k, v);
});
formData.append("file", file); // must be the last one
Then send the file:
await fetch(data.url, {
method: "POST",
body: formData,
});
There are a lot of subtle things you can set up to make sure it's secure. See this article for more info and example code.

Sending an email using Azure SDK

I use azure-graph in my Node.js project:
const MsRest = require('ms-rest-azure');
const credentials = await MsRest.loginWithServicePrincipalSecret(keys.appId, keys.pass, keys.tenantId, { tokenAudience: 'graph' });
const GraphkManagementClient = require('azure-graph');
const client = new GraphkManagementClient(credentials, subscriptionId);
return client.users.get(principalID);
I want to use the Azure SDK also to send emails.
I know how to do that in low level using the API directly:
But I want to do it via the SDK like the rest of my project.
My problem is, I have not found any method for sending an email in the docs: azure-graph package. I need a method that allows me (with the proper privileges of course) to send email as any user in the organization.
You can use the Graph JavaScript SDK which is a wrapper around the Microsoft Graph API that can be used server-side and in the browser to send mails to users. Please refer to Graph Javascript SDK to learn more about the same. Also, refer to nodejs-connect-sample to use Microsoft Graph API and the Graph JavaScript SDK to send an email.

Google Cloud Storage change notifications with Node.js

I have Firebase storage bucket and I would like to use Node.js Google-cloud notification API in order to listen to changes in the storage.
What I have so far:
const gcloud = require('google-cloud');
const storage = gcloud.storage({
projectId: 'projectId',
credentials: serviceAccount
});
const storageBucket = storage.bucket('bucketId');
Now from what I understand I have to create a channel in order to listen to storage changes.
So I have:
const storageBucketNotificationChannel = storage.channel('channelId', 'resourceId');
This is the threshold where the docs stop being clear, as I can't figure out what channelId a resourceId stand for.
Nor do I understand how to declare listening to channel changes itself. Are there any lifecycle-type methods to do so?
Can I do something like?
storageBucketNotificationChannel.onMessage(message => { ... })
Based on the existing documentation of the Google Cloud Node.js Client and the feedback from this Github issue, there is presently no way for the node client to create a channel or subscribe to object change notifications.
One of the reasons being that the machine using the client may not necessarily be the machine on which the application runs, and thus a security risk. One can still however, subscribe to object change notifications for a given bucket and have notifications received a Node.js GAE application.
Using Objects: watchAll JSON API
When using gsutil to subscribe, gsutil sends a POST request to https://www.googleapis.com/storage/v1/b/bucket/o/watch where bucket is the name of the bucket to be watched. This is essentially a wrapper around the JSON API Objects: watchAll. Once a desired application/endpoint has been authorized as described in Notification Authorization, one can send the appropriate POST request to said API and provide the desired endpoint URL in address. For instance, address could be https://my-node-app.example.com/change.
The Node/Express application service would then need to listen to POST requests to path /change for notifications resembling this. The application would then act upon that data accordingly. Note, the application should respond to the request as described in Reliable Delivery for Cloud Storage to retry if it failed or stop retrying if it succeeded.

Categories