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?

Hello Julian,

Sorry, I allow myself to refresh this thread because I believe this is what I am looking for. Was there any progress regarding your security proposal/implementation for uibuilder v2 since January?

The reason I am asking that is that I need to implement some (simple & secured) user access management to my data dashboard and I know this is not possible with the standard Node-Red dashboard.

Also, due to my distribution and build system (Yocto) I can only use Node-Red v0.18 - will it be compatible with your proposal?

Thanks and best regards

Hi Kilian,

Not really much progress I'm afraid and currently I'm rather pre-occupied with UK NHS IT COVID related issues that I'm trying to help manage so both time and remaining energy levels are very low.

Please to have a look at the security branch on GitHub for the latest code and documentation. Maybe give it a go on a test system to see if it is getting anywhere near what you want. I'd also be happy to take PR's if you are able to work on anything.

1 Like

Thanks a lot for your quick reply despite the current situation Julian.

I understand of course. Those are terrible times indeed... The UK and Europe in general are taking quite a heavy blow. Stay safe!

I downloaded and installed your security branch on my device, but I do not see any changes in the options, html or js files compared to the standard uibuilder - do I need to delete the default templates in order to make it work? Is there something I am missing?

If I manage to make something out of it I'll let you know. As of now, I'm assuming that token processing, session management and security related events are still not included, is that correct?

Thanks again for your time.

Hello Julian,

I managed to install the security branch of Uibuilder and I've been playing with it for the past few days. Overall, what a tremendous work you did! It is really great and I'm learning plenty of new stuff in addition to my previous html/css/javascript+bootstrap knowledge.

I am still trying to understand how everything works but there is one particular thing that bugs me. Assuming very few users will access to the UI (10 users at most), how would you recommand storing their credentials locally? I can't afford a cloud-based/external DB solution for this project, everything must be accessible on site (within the local network). No data should be leaving the local network.

I was thinking to simply rely on a CSV file with user ID, password, and roles (admin, write, read) or this sort of simple things but would that be a no-go regarding uibuilder? I am having issues to parse the CSV file with user data in the "userValidate" part of the security.js file.

Really this use case needs very simple user management where I would filter some elements on Vue.js depending on the role of the user. And also keep a history of the actions the users did during their session (that should be doable easily in Node-Red I assume).

Any advice for a uibuilder newbie? :slight_smile:

Thanks and have a good day

Haha, let me know if you find out! Sadly I've no time or energy at the mo to do anything because I'm full-on supporting COVID related NHS stuff. My brain is fried at the end of each day :slightly_frowning_face:

I would almost certainly use a simple JSON file. It should be more than performant enough. You could also use a SQLite db if you are into SQL. Depending on your local setup, you might also have access to a db server or could run one up easily enough if you are familiar with these things. MySQL/MariaDB is really easy to set up and doesn't require too much in the way of resources.

Overall though, for 10 users a JSON file would be ideal. Just take care when doing updates - remembering that you have to take responsibility for writing the file to disk.

CSV file would also work but JSON probably easier to manage since you can serialise any JavaScript object to it really easily. Also very easy to read using Node.js

There is very little that is no-go for uibuilder :slight_smile:

Sounds great. And certainly within scope of uibuilder and Node-RED. I would probably handle history in your security js function file though. Keeping things together. Should be easy enough as things like logins and verifications go through that file anyway.

Just keep plugging away at things bit-by-bit so that you build up both experience and skills. An agile approach is generally best with these things. Choose a small chunk of capability and focus on building that, once it is working only then move on.

I'm keen to know if anything in the security build isn't working for you or isn't working how you need it. It is very much a work in progress I'm afraid and progress has well and truly stalled until I can get a bit more of my life back from work. But it will be easier if I have something to focus on so please do feed things back.

Hi guys

I will be frank, I advise you use stunnel to handle any security ( as well as introduction a protocole rupture and access code impossibility, you can protect the config file on a NAS and the RAII with POC ) and then don't need to handle DDRDOS from nodejs at all nor tampering attack nor mim and trust loop.
As nodejs turn on V8 and provide only a single thread appartment, most hacker know that at a current time, nodejs affect your code into a single thread. So there is no graph dependancies to rebuilt the code and a simple hammering dump is enought to fully hack, and worst you might actually give all oauth for external ressources.

Just do another tool to secure you access that is transparent for nodejs and node-red.

hi,

great geat node!
now trying to implement a simple user/pass login to access a vue dashboard using redmatic on a ccu3, so actually i have to get the security branch installed first.

Is there already a simplistic login example out there for quick solution ( keeping kids from changing stuff on a kiosk browser ) ?

Sorry Simon, not had any chance or energy to work on the security branch since COVID started. Since this is a work in progress, there aren't any other examples other than the fact that there is something simple set up in the template security js file. Should be easy enough to extend for simple things though.