2

How do I protect this gun server? I only want traffic from one domain with many sub-domains.

In my use-case, I made a game for my kids on iPad. When they played for the first time - I realized it would be better if some of the game controls could be moved to their phone. So they use the ipad and the phone simultaneously to control the game. It works great. Data is sync'd between devices via gun. However, there's this problem of security. Anyone could use my gun server to share state between devices in real-time. I'd like to restrict it to my app

const ARGS = process.argv.slice(2);

var fs = require('fs');
var protocol = ARGS[0];
var port = ARGS[1];

var express = require('express');
var cors = require('cors')

var Gun = require('gun');
require('gun/axe');

var app = express();
var allowedOrigins = [
    'localhost:8080',
];

app.use(cors({
    origin: function (origin, callback) {
        // allow requests with no origin
        // (like mobile apps or curl requests)
        // if (!origin) return callback(null, true);

        if (allowedOrigins.indexOf(origin) === -1) {
            var msg = 'The CORS policy for this site does not ' +
                'allow access from the specified Origin.';
            return callback(new Error(msg), false);
        }

        return callback(null, true);
    }
}));


app.use(Gun.serve);
app.use(express.static('/gun'));

server = require(protocol);

if (protocol == 'https') {
    var privateKey = fs.readFileSync('../json-data.ssl/key.pem', 'utf8');
    var certificate = fs.readFileSync('../json-data.ssl/cert.pem', 'utf8');
    var credentials = {key: privateKey, cert: certificate};
    var server = server.createServer(credentials, app);
} else {
    var server = server.createServer(app);
}

server.listen(port, () => {
    console.log('listening on *:'.concat(port));
});

var gunDev = Gun({web: server, file: 'testingDB'});

Tarek Adam
  • 3,387
  • 3
  • 27
  • 52
  • By default, browsers implement CORs protection for you so requests can only be made to the domain of the web page that is running the script. That comes for free without implementing anything on the server. All CORS protection applies ONLY to webpages run in a browser. Can you provide a specific example of a request from a web page with the domain of that web page that you do and don't want to be able to access your server? It's not clear what problem you're really trying to solve here without more specific examples. – jfriend00 Jan 15 '22 at 04:41
  • FYI, you use CORS code on your server to enable MORE web page domains to access your server, not less. – jfriend00 Jan 15 '22 at 04:43
  • @jfriend00 The problem is that the gun.js service behaves in a wide-open fashion. It doesn't care where I connect from and I can seemingly just pound in data from anywhere. I don't know how to protect it. In my opinion it should come protected by default rather than wide open. More like socket.io would be better. – Tarek Adam Jan 15 '22 at 04:50
  • Protected from what? A server on the internet is open to ANYONE using any level of scripting or programming. Same for socket.io. Same for a web server. Same for a Gun server (whatever that is). CORs protection is applied and enforced only by browsers and ONLY protects Javascript requests made from within a browser webpage. Require credentials upon connection if you want to control who can access your server. – jfriend00 Jan 15 '22 at 05:00
  • Hi @jfriend00. gun is a write/sync data distribution system. At the moment, anyone with a need for a gun server could just ... use mine to write data for sync across devices. – Tarek Adam Jan 15 '22 at 05:07
  • What kind of client uses a gun server? What kind of a client do you want to allow? What kind of client do you not want to allow? – jfriend00 Jan 15 '22 at 05:08
  • @jfriend00 In my use-case, I made a game for my kids on iPad. When they played for the first time - I realized it would be better if some of the game controls could be moved to their phone. So they use the ipad and the phone simultaneously to control the game. It works great. Data is sync'd between devices via gun. However, there's this problem of security. Anyone could use my gun server to share state between devices in real-time. I'd like to restrict it to my app. – Tarek Adam Jan 15 '22 at 05:12
  • @jfriend00 - I have updated my question/info as per your comments. Thanks for your help. – Tarek Adam Jan 15 '22 at 05:15
  • If the server needs to be on the public internet, then you will need to issue individual credentials to the clients that are allowed to use your server and check those credentials upon connection. Your phone/ipad apps can retain those credentials once they've been issued so the user doesn't have to repeatedly enter them. – jfriend00 Jan 15 '22 at 05:29
  • You can think of this like "having an account on the game server" where an account means some sort of username and password that you've issued and authorized on your server. Or, if it's not something the user will ever need to see or enter, it can be a cryptographically secure token that is issued by the server, saved in the client and sent with each connection to your server (over https so it can't be stolen). – jfriend00 Jan 15 '22 at 05:32
  • @marknadal - would you be willing to chime in on this? – Tarek Adam Feb 28 '22 at 14:56

2 Answers2

2

Step 1 - Create cors.js file

const cors = require('cors');
const whitelist = ['localhost:8080'];

const corsOptions = (req, callback) => {
  let options = {
    origin: whitelist.includes(req.header('Origin') ? true : false;
  }
  callback(null, options);
};

exports.corsWithOptions = cors(corsOptions);

Step 2- Import and apply corsWithOptions

const { corsWithOptions } = require('./cors');
...
app.use('/*', corsWithOptions, (req, res) => { res.sendStatus(200); })
app.use(Gun.serve);
app.use(express.static('/gun'));
NeNaD
  • 18,172
  • 8
  • 47
  • 89
  • Note, this doesn't ADD protection to the server. This allows you to enable access from more webpage domains, not restrict access to fewer. – jfriend00 Jan 15 '22 at 08:26
1

A web server on the internet is, by default, open to anyone who has any sort of programming skills.

CORs protection is something that only applies to Javascript running in a web page. CORs protection itself is implemented in the browser and when any sort of code is running outside a browser (any simple script), there is no CORs protection at all. So, CORs really doesn't apply from any other type of client besides a browser.

So, to protect your server from access by the general public, you have these various options:

  1. Keep your server off the internet. Make it only accessible on some local network where the clients reside. For example, I have a raspberry Pi server used for some home automation tasks running at my house and it's behind my home firewall and only accessible from my home network (e.g. not on the internet). Any access to this server (such as changing its configuration) is done from on the home network. Thus, it doesn't have to be secured against random internet access.

  2. Restrict access to only certain client IP addresses. In general, this is usually not very practical as any sort of mobile device may very well change its IP address regularly and also not entirely secure.

  3. Require some sort of credential upon connection to your server. This is the main mechanism used. Depending upon how the client will acquire and enter the credential, this may be either a typical end-user account credential such as username and password or if everything is programmatic, it can be a cryptographically secure key. In either case, a client gets issued (by your server) a credential that can then be used to authenticate on future connections to your server. This will deny access to any clients or any type that you haven't issued a credential to. Your server would require the credential upon connection and reject any requests that don't pass the auth check. It goes without saying that credentials should only be sent over https to prevent theft of the credentials by network snoopers (like public WiFi).

For your particular game use, #1 would be possible only if you restrict use to the home network and require both devices to be on home WiFi, not on the cellular network.

For your used, #2 is probably not feasible.

So, that means that #3 is probably what makes the most sense. If this is a one-off deployment, then you could manually configure each client with a security token and manually add those tokens to some configuration storage (like a JSON file) on the server. If you're attempting to make a more scalable system that lots of other gaming users can use, then you would have to develop some sort of system for registering legit clients, issuing them credentials and storing those credentials in the clients. Since your clients may come in pairs that need to be "connected", you will have to bake that into your credential issuing process too.

jfriend00
  • 683,504
  • 96
  • 985
  • 979