Question about security in an electron app using express in production - reactjs

I have converted a react/django application into an electron project. All data is stored externally, identical to the web app, however, one third-party token must be stored on the users machine.
To do this, I start an express server on localhost:4001 that exposes url's that read/write a few pieces of data to a file in appData.
My question is, is this safe? Essentially, is hiding node functions behind http request okay to do in electron?
I've read the security documentation, but most is in regards to using ipcMain/ipcRender communications. I'll refactor to this if I must, but what I have works and for what the app is I'm happy with that as long as it's just as secure.
Also, this SO question has made me feel better, and ive configured the server as the question suggestion.
Express localhost URLs in production for Electron app
I guess I'm just seeking validation of my current configuration, as this is not the part to be wrong on.
Here are my browser window settings. In production I will be loading the main window from a public url belonging to the original web app.
function createWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
dev_tools: isDev,
//worldSafeExecuteJavaScript: true,
//preload: path.join(__dirname, 'preload.js')
}
})
const startURL = isDev ? 'http://localhost:3000/dashboard/' : `www.appurl.com/dashboard`}`
server.js -
as you can see CORS has been disabled and its http. For validation I'm using host-validation, and I have a jwt token already in the authorization header that I was going to send to my server for additional verification. Is this enough to offset disabling CORS?
require('dotenv').config()
const express = require('express');
const cors = require('cors');
const fs = require('fs');
const hostValidation = require('host-validation')
const app = express();
const PORT = process.env.EXPRESS_PORT || 4001
app.use(cors())
app.use(hostValidation({hosts: ['localhost:3000', 'www.appurl.com']}))
app.use(express.json())
app.post('/upload_api_token/', (req, res) => {
fs.writeFile('filname.txt', 'Hello world')
res.send(200)
})
app.listen(PORT, () => console.log(`Server running on ${PORT}`))
And here are relevant parts of my package.json
{
"scripts": {
"start": "fuser -k 3000/tcp; concurrently \"nodemon --watch server --inspect ./server/index.js\" \"react-scripts start\"",
"build": "python prebuild.py; react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"dev": "fuser -k 4001/tcp; concurrently \"yarn start\" \"wait-on http://localhost:3000 && electron .\""
},
"dependencies": {
...,
"react": "^17.0.2",
"electron": "^18.0.4",
"electron-builder": "^23.0.3",
"electron-is-dev": "^2.0.0",
"electron-reload": "^2.0.0-alpha.1",
"express": "^4.17.3",
}

Related

HTTP 500 error upon deploying application to Google App Engine

I am using Google App Engine to deploy a test application. The deployment works fine and when I point my browser to the following URL, I receive an HTTP 500 error:
https://test-gae-365706.uc.r.appspot.com/
My application code is as follows:
const http = require('http');
const port = 8080;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Do not have an inflated sense of yourself. You are just food for worms.');
});
server.listen(port, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
My app.yaml file is as follows:
runtime: nodejs10
My package.json file is as follows:
{
"name": "app-engine",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "node app.js"
},
"engines": {
"node": "10.x.x"
},
"author": "",
"license": "ISC"
}
When I run glcoud app deploy, I do not get any errors, but when I point my browser to the URL, I get the HTTP 500 error:
Error: Server Error The server encountered an error and could not complete your request. Please try again in 30 seconds.
In the navigation pane, when I go to the Instances page, I see that no instance has been created. Could this be the problem?
Please let me know what is it that I am missing here.
You need a start script in package.json. Based on your test script, it would be something like -
"start": "node app.js"
See a sample Node App from Google
You're using the variable ${hostname} but you haven't defined it. You need to define it and provide a value like you did for port

How Do You Configure Create React App to Work with Netlify Lambda Functions

I am trying to use netlify lambda functions with create react app, and it is breaking my site.
The repo was made by running npx create-react-app my-app-name, and is the standard create react app boilerplate.
File structure:
root-directory/package.json
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"lambda": "netlify-lambda serve src/lambda"
},
"devDependencies": {
"netlify-lambda": "^2.0.15"
}
root-directory/netlify.toml:
[build]
command = "npm build"
functions = "lambda"
publish = "build"
src/setupProxy.js:
const proxy = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
proxy("/.netlify/functions/", {
target: "http://localhost:9000/",
pathRewrite: {
"^/\\.netlify/functions": "",
},
})
);
};
src/lambda/dictionary.js:
exports.handler = (event, context, callback) => {
callback(null, {
statusCode: 200,
body: "hello world",
});
};
Now, when I try to run npm run start, the app will not load.
The browser displays the error:
This site can’t be reachedlocalhost refused to connect.
When you run npm run lambda and to to the url http://localhost:9000/.netlify/functions/dictionary in the browser, the browser does display "hello, world" as expected.
Also, I am not able to use the netlify cli because when I try to install it, I get permission errors/ access denied, even when I use sudo. So, trying to get this non globally installed way to work.
I just had the same issue with the same approach with your setupProxy.js.
Then I modified the setupProxy.js to below and it worked for me
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(createProxyMiddleware('/functions/', {
target: 'http://localhost:9000/',
pathRewrite: {
"^\\.netlify/functions": ""
}
}));
};
I fixed it , please see below - its stripped down all the way to just keeping it as simple as possible with examples for even using node
https://github.com/Kirbyasdf/netlify-react-lambda-node-boilerplate

Building a React-Electron app using electron-builder, index.js loads inside pre tags

I have an app that I'm now trying to build to distribute for testing.
I'm using React and Electron with electron-builder to build the app itself. I'm not a web developer so I've been trying to keep things basic and just get something to work.
After about five hours I was finally able to get the app to build somewhat properly and launch, but when it loads index.js (the first page in the app) it displays the source for index.js instead of rendering the content. In the devtools everything is inside a pre tag.
I've already looked at this thread and tried that but it didn't change anything, and I'm not using service workers as far as I can tell.
What the actual Electron window displays after launching with the devtools alongside.
Here's the createWindow function from main.js.
I've tried doing all kinds of things to the pathname with no effect.
function createWindow() {
const startUrl = process.env.ELECTRON_START_URL || url.format({
pathname: path.join(__dirname, '../src/index.js'),
protocol: 'file:',
slashes: true,
});
mainWindow = new BrowserWindow({
width: 800, height: 600, title: "Electron App", webPreferences: {
nodeIntegration: true
}
});
mainWindow.loadURL(startUrl);
mainWindow.on('closed', function () {
mainWindow = null;
});
}
Here are my scripts from package.json
"scripts": {
"start": "nf start -p 3000",
"start-electron": "set ELECTRON_START_URL=http://localhost:3000 && electron .",
"react-start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"build-electron": "npm run build && electron-builder build --win"
}
Here's the build part too. To be honest, I don't really understand what this is or does but after a few hours of trial and error this is what gets me to the point I am now.
"build": {
"appId": "Test",
"extends": null,
"files": [
"./build/**/*",
"./electron/main.js",
"./src/**/*"
]
}
As far as I can tell, it has something to do with the Electron start URL, because when I removed that from const startUrl in createWindow, running the app using npm start did the same thing as the built Electron app, whereas before using npm would launch the app normally every time.
EDIT after solution:
Modified build in package.json to
"build": {
"appId": "Test",
"extends": null,
"files": [
"./build/**/*",
"./electron/main.js",
"./src/**/*"
],
"directories": {
"buildResources": "./public"
}
}
I haven't tested it without this modification so I'm not sure that it's actually necessary.
Start URL was changed to
const startUrl = process.env.ELECTRON_START_URL || url.format({
pathname: path.join(__dirname, '../build/index.html'),
protocol: 'file:',
slashes: true,
});
You're supposed to set it up with an html file.
const startUrl = process.env.ELECTRON_START_URL || url.format({
pathname: path.join(__dirname, '../src/index.html'),
protocol: 'file:',
slashes: true,
});
Your browser window should load the build/index.html on production mode
const isDev = require("electron-is-dev");
if (isDev) {
mainWindow.loadURL(process.env.ELECTRON_START_URL);
} else {
mainWindow.loadFile(path.join("build", "index.html"));
}

electron-builder not working for production due to path

everyone
I created a React app in order to build an electron app. I'm trying to use electron-builder to create the installers. I'm working on Ubuntu trying to generate a .deb.
I didn't use create-react-app, I used a boilerplate I already had, but I don't know if this is the problem.
I can make it work as long as I edit the index.html the webpack generates in the build folder. I have to change the path to main.css and build.js from /main.css and /build.js to ./main.css and ./build.js.
I read that I have to use the "homepage" property in package.json, but it's not working, seems to be ignored.
My folder structure is like:
package.json
src/
config/
main_electron_file.js
build/ #generated by webpack when I run the 'build:prd' command
public/ #empty
package.json
{
"name": "frontend",
"version": "1.0.0",
"description": "",
"main": "./main_electron_file.js",
"scripts": {
"dev": "./node_modules/.bin/webpack-dev-server --config ./config/webpack.config.dev.js --mode development --open --hot",
"build:prd": "./node_modules/webpack/bin/webpack.js --mode production --config ./config/webpack.config.prd.js --env.NODE_ENV=production --progress",
"electron": "electron .",
"start": "npm run dev && npm run electron",
"start:dev": "ELECTRON_START_URL=http://localhost:3000 electron .",
"dist": "build"
},
"keywords": [],
"author": {
"name": "name",
"email": "email#mail.com"
},
"license": "ISC",
"dependencies": {
...
},
"devDependencies": {
...
},
"homepage": "./",
"build": {
"appId": "com.myself.myapp",
"linux": {
"target": [
"deb"
]
}
}
}
main_electron_file.js
// Modules to control application life and create native browser window
const { app, BrowserWindow } = require('electron');
const path = require('path');
const url = require('url');
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow
function createWindow() {
const startUrl = process.env.ELECTRON_START_URL || url.format({
pathname: path.join(__dirname, 'build/index.html'),
protocol: 'file:',
slashes: true
});
// Create the browser window.
mainWindow = new BrowserWindow({ width: 800, height: 600, resizeble: false });
mainWindow.setResizable(false);
// and load the index.html of the app.
// win.loadURL('http://localhost:3000');
mainWindow.loadURL(startUrl);
// Open the DevTools.
mainWindow.webContents.openDevTools()
// Emitted when the window is closed.
mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null
})
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)
// Quit when all windows are closed.
app.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', function () {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
createWindow()
}
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
By the way. I'm not building it. I'm running it pointing to the index.html in the dist, as I would do in production mode. So i'm running npm run build:prd and then npm run electron
ANy ideas how I could make this process automatic?
Please try adding this line in your index.html
<base href="./">
And your main.css and build.js directory should be set relative to your index.html in build folder
What worked for me was:
Add to webpack config:
output: {
publicPath: './',
...
}
This tells webpack to have every link between files it creates based at the same directory.
As suggested by carlokid, add to index.html:
< base href="./" />

How to create a proxy in React/Webpack to call an external API

I want to issue a GET request to an external API that I do not control. Because of the security on the API, my react app cannot directly make an ajax request to the endpoint. Therefore I'm trying to create a simple proxy as demonstrated here
My package.json file looks like this:
{
"name": "my-stack",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-router-dom": "^4.2.2",
"react-scripts": "1.0.13"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"proxy": {
"https://gold-feed.com/paid/*": {
"target": "https://gold-feed.com/paid",
"changeOrigin": true
}
}
}
And then my ajax request looks like this:
const apiUrl = 'https://gold-feed.com/paid/<apiID>/all_metals_json_usd.php';
jQuery.ajax({
method: 'GET',
url: apiUrl,
success: (item) => {
this.props.addItem(item);
}
});
But it doesn't appear to be doing anything. I'm still getting the following error:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.
I essentially have the same issue as documented here where he is trying to create a proxy to access the Steam api.
And just a side note, I believe the create-react-app project that I'm using is piggybacking off of webpack.
You probably figured it out by now but for others here is what worked for
me:
"proxy": {
"/proxy": {
"target": "https://mybackend.com",
"pathRewrite": {
"^/proxy" : ""
},
"changeOrigin": true
}
}
so myreact.com/proxy/my/path is redirected to mybackend.com/my/path
I think the error in your case is that you put the destination as a key for your proxy instead of path on your react server.
For my case my api was deployed on AWS, I found that setting
"changeOrigin": true
was necessary (chrome & edge worked, firefox (62.0.3) complained "Invalid CORS request").
In the documentation of webpack http proxy they say this option:
(https://github.com/chimurai/http-proxy-middleware)
Tip: Set the option changeOrigin to true for name-based virtual hosted sites.
notes:
running the api locally on same machine didn't require that for fire fox.
firefox adds header "origin" which when I removed and resent the request worked.
adding "changeOrigin" doesn't have side effects so I will be doing it from now on.
I'm not sure if it's a bug in firefox or what, anyway my final configuration was:
"proxy": {
"/api": {
"target": "<put ip address>",
"changeOrigin" : true
}
}
you should replace /api with the path of your api or rewrite the path as the answer above.
You can proxy external API to localhost, change frontend origin (localhost:3000), target server, update token if needed and run following script in node js
const express = require('express');
const proxy = require('express-http-proxy');
const cors = require("cors");
const app = express();
app.use(
cors({
origin: 'localhost:3000',
credentials: false
})
);
app.use('/', proxy('https://www.targetserver.com/', {
proxyReqPathResolver: function (req) {
return req.url;
},
proxyReqOptDecorator: function (proxyReqOpts, srcReq) {
proxyReqOpts.headers = {'Authorization': 'Replace with JWT'};
return proxyReqOpts;
}
}))
app.get('/*',function(req,res,next){
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.listen(4000, () => console.log(`Server Listening on port ${4000}!`));
Use it in your fontend application like this (with axios or fetch) :
axios.get('http://localhost:4000/api/route).then(x => console.log(x));

Resources