Uibuilder: Security Proposal

Hi all and once again welcome to a New Year.

At the moment, I am trying to work out the best approach to securing uibuilder web apps while trying to keep a decent measure of simplicity.

I'd like to share my current thoughts with anyone who may be interested - hence this post.

The master for this post is in the WIKI and I will update that as I go along. Please use this thread however, for discussion and ideas.


A proposal for uibuilder security (v2)

This is a write-up of my latest proposal for securing user interfaces built with uibuilder and Node-RED. It is still subject to change and I'm happy to take feedback.

There is a separate code branch for the security build - boringly called the Security branch. You are welcome to try it out but please note that it may not always work as expected and will contain all sorts of weird debugging. I will update the forum when the code reaches beta quality.

Requirements

  • Should be as simple as possible to use - uibuilder itself should hide much of the complexity.
  • Must be optional (not everyone requires UI security).
  • Must be independent of Node-RED core security.
  • Must allow for custom user validation. User data must be outside of uibuilder and outside of Node-RED for security.
  • Must allow for custom session creation/validation/destruction.
  • Must allow for timed session validity.
  • Must warn if not using TLS. (If in production, should error out if not using TLS).
  • Must provide security for websocket communications.

Assumptions

  • YOU are responsible for the security of your data, users and systems. Not me. Sorry. No guarantees or warranties are given or implied. Get your systems audited, tested and certified by professionals.
  • There is no point using logins if you don't secure communications using TLS. If you try (for testing maybe?), you will get continual warnings in your Node-RED logs.
  • Only you know how to validate whether someone is who they claim to be. I can't know that. So you have to write the code for that part. I will provide a simplistic and basic method as a starting point.
  • Your UI should not include any sensitive data and therefore should not need a login before delivery of the core resources to a client.
  • uibuilder will use websocket-based login, not HTTP(s) or an API. (I may create an API method at some point if enough people feel strongly enough about this).
  • Websocket broadcasts and uibuilder control messages will be permitted (from server to clients) without authentication. uibuilder control messages will be permitted from the clients to the server without authentication.
  • I will use JavaScript Web Tokens (JWT) to make life easier. However, JWT is NOT a security mechanism (amazing how many people get that wrong), it is a convenience mechanism. JWT is liable to session hijack and man-in-the-middle attacks. So token lifespan should be short and ongoing session validation is important.
  • Tokens should be cryptographically secure. To make this easy, uibuilder will take care of it for you.
  • uibuilder relies heavily on websocket communication. Unfortunately, ws does not support custom headers except on initial handshake so standard methods for session validation don't work. Therefore, uibuilder will provide a custom method.
  • User & session validation will use a custom Node.js module (written by you though I will provide something basic as a starter), changes to the module will require reloading Node-RED. See bullet 2 above.
  • Session management requires a session per client. A single user might have many clients open (multiple desktop browser tabs, mobile browser tabs, etc). So this can mount up. Be aware of resource utilisation.
  • Session management requires a check for each client for each message sent/received. That's a LOT of checks, session checks need to be efficient and short.
  • Session management will be memory based by default. Restarting Node-RED will invalidate all sessions. This may change.

Proposal

Authentication checks will happen via a call to uibuilder.logon(userData) from your front-end code. You may pass an object containing additional user information as the single parameter. At minimum, you need to pass some kind of identifier. Typically, you will also give a password.

That data will be collected from your user via a form typically, an internal variable tells you whether the current user is authenticated (uibuilder.get('isAuthorised')). If they are authorised, another internal variable holds the token and a 3rd holds the token expiry date/time. The token will be passed back to the server every time the client sends a message so that it can be validated.

If the server receives an invalid token, it will invalidate the session (and may log the user out of all their sessions).

When the server receives a logon control msg from a client, it will call a logon function which will reference your external module to validate the user information. Assuming the user data is valid, the external function either returns true or an object with a defined schema (which allows you to return information back to the user such as messages to pay dues, reminders to change passwords, or anything else that you like).

Upon validation of the user, uibuilder will create a session entry for that connection/user. Sessions may (and should) have a timeout. Session creation, destruction and validation are also done by your external module. Session creation and optionally validation functions may return an object which will be folded into the JWT which uibuilder will create.

As yet undecided

I've not quite worked out the best approaches for these as yet.

  • It is yet possible that I'll hand over session management to a standard library. However, I suspect that none are flexible enough & not many properly handle websocket sessions.
  • There may be a need for 2 levels of session validation? One that will refresh the token on a short time period (typically 30 seconds as this is how often your client will send a ping message to the server). The second will typically be on a longer time period that you will choose, typically a few days.
  • Invalid tokens received by the server might cause a user to be logged out of all their sessions?

Settings

  • Security (Boolean} will be optional via a setting for each uibuilder node instance.
  • Session quick refresh {Boolean} will enable a refresh/quick validation function to be called for every 'packet' (e.g. any kind of message including pings which happen every 30 seconds). This helps reduce the risk of using JWT and ensures that sessions continue to get extended even if the user isn't interacting.
  • Session expiry {Number} will be controllable for each instance of the uibuilder node. This is the maximum session length. After this period, the session quick refresh will stop and the user must log in again (unless you do something clever on the front-end to resend details automatically - that has some risk of course in retaining sensitive data in the browser).
  • User & session validation {Module} will use a custom Node.js module, changes to the module will require reloading Node-RED. This will support the use of a central module that will be found in <uibRoot>/.config/security.js or alternatively, in the instances root <uibRoot>/<url>/security.js. This allows for different instances of uibuilder to have different authentication and session validation functions. A template example file will be provided in <uibRoot>/.config/security.js that can be adjusted or replaced as needed.

Other Info

Also see the original security design page.

3 Likes

Go Julian!
I'll be glad to test and report if needed.

Many thanks. It isn't ready for testing yet as all I have in there is some fixed dummy code and a load of debug output. I will update this thread when the code is ready for beta testing.

In the meantime, if you can think of anything I've missed, please drop a note in this thread as well.

For anyone following along. I've pushed a lot of updates to the "security" branch.

If you want to install this version, you can do so on a test device if you like. Just note that it is still changing - I do try to only push versions that don't crash - I don't always succeed! :rofl:

cd ~/.node-red
npm install TotallyInformation/node-red-contrib-uibuilder#security --save

As always, please take a look and let me know what I've got wrong or missed off! Is this going to work for you? If not, why?

What works?

  • Security is turned on in code and cannot be turned off at the moment. If you aren't using TLS security, you will get warnings about that and if node is not operating in development mode (e.g. in production mode), the security turns itself off without TLS (since there is no point in using logins without an encrypted channel).

  • You can manually send a login using uibuilder.logon({id:'test'}) on the front-end client. This will result in a successful login, anything else will fail. It will let you see the messages used. Change the security.js file to use your own user validation.

  • As long as you are using a development mode environment, you will get a new file in the uibuilder .config folder called security.js with a really noddy process in it but a fair bit of information about how things are expected to work.

    Note that this file is undergoing rapid change still. To get the latest template, delete or rename your copy and restart Node-RED then restart again after applying your own edits.

    This file is the core of how uibuilder security will work. It will contain functions that validate users and create/validate/destroy sessions.

    You can also have different security for each uibuilder instance if you use more than one. You can place a security.js file in the root folder of any instance. This will take preference over the central file.

What doesn't work yet

  • Token processing still not there yet - a manual token is applied to everything and the token expiry is just 1 minute.
  • There is no session management as yet. That comes next and it will result in more changes to the security.js file.
  • There are no security related events active as yet. There will be events that cover logon, logoff and token expiry as a minimum.
2 Likes

Last update pushed for tonight.

I've updated the template index.html/.js files to include a very simplistic logon/logoff UI. Also added event handling for when the isAuthorised flag changes.

I've also updated a number of documents in the /docs folder of the package. The security and uibuilderfe docs may be of particular use to anyone working with this branch.

1 Like

Sorry, was out of my computer :slight_smile:
At install I get:

npm ERR! code ENOENT
npm ERR! syscall spawn git
npm ERR! path git
npm ERR! errno -2
npm ERR! enoent Error while executing:
npm ERR! enoent undefined ls-remote -h -t ssh://git@github.com/TotallyInformation/node-red-contrib-uibuilder.git
npm ERR! enoent
npm ERR! enoent
npm ERR! enoent spawn git ENOENT
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent

You don’t have git installed on your machine that’s the error.

1 Like

Opps, I forget it on this machine (raspy). Thanks!

I have issues with let's encrypt. Can't make it work with node-red so I'm stuck. I really hope to test it later on.

You should be able to get it working with self-signed certs too. In addition, the current code works without TLS as long as you are running in development mode (which is the default).

What problems do you have with Let's Encrypt?