-
-
Notifications
You must be signed in to change notification settings - Fork 86
Security Design
Note 2: See the Security Design v2 page for the latest ideas.
Note: This page is now out of date. uibuilder v2 has made some fundamental changes, some of which will support better approaches to security but I've yet to fully explore them and come up with examples. I will update this page when I have.
This page aims to set out the requirements and outline design for security for uibuilder.
The aim is to enable JWT capability for both resources (e.g. pages) and Socket.IO connections.
- I am not a full-time developer and have limited resources for this project. No assurance of security can be provided by me I'm afraid. You use this software at your own risk.
- You MUST provide your own risk assessment, security testing and assurance.
- The use of JWT and logins makes no sense at all unless you are running Node-RED with TLS or have Node-RED secured behind a reverse proxy that is providing TLS (e.g. using HTTPS for everything). Failure to do so results in the possibility of tokens being intercepted and reused by 3rd-parties and in login details being passed in clear text.
-
MUST log a warning if token processing is used without also requiring TLS (https)
- MUST log to uibuilder log
- MUST log to Node-RED log
- MUST log to Admin debug sidebar
-
MUST log a warning if page security is used without socket security (but not if socket security is used without page security)
- MUST log to uibuilder log
- MUST log to Node-RED log
- MUST log to Admin debug sidebar
-
MUST provide a capability to validate tokens on requests for web resources (html, jk, css, etc):
- MAY provide the capability via an ExpressJS middleware function that is common to all instances of uibuilder.
- MAY provide the capability by extending the node's admin UI to include a JavaScript function (individual to each node instance and able to access global/flow variables).
-
MUST provide a capability to validate tokens on incoming Socket.IO messages:
- MUST provide a hook (external function capability, contained in Node-RED's
settings.js
file and so called for all instances of uibuilder) for token validation on receipt of all Socket.IO messages from the front-end client. The hooked function MUST return a boolean (true/false). - MAY provide a hook by extending the node's admin UI to include a JavaScript function (individual to each node instance and able to access global/flow variables).
- If the hook is null or undefined, uibuilder MUST not process any tokens and MUST assume that all traffic is valid.
- MUST EITHER process the returned hook function boolean such that TRUE will pass the incoming msg to the downstream flow as normal. FALSE will drop the incoming msg but will return a control msg back to the client indicating that the token is invalid.
- OR allow the validation hook function to return either the boolean or a token. If a token is returned, the process MUST treat this as a success but MUST also return the new token to the client via a Socket.IO control message (for token refresh processing).
- MUST provide a hook (external function capability, contained in Node-RED's
-
MAY provide a capability to validate tokens on outgoing Socket.IO messages (server to client):
- MUST provide a hook (external function capability, contained in Node-RED's
settings.js
file and so called for all instances of uibuilder) for client validation. The hooked function MUST return a boolean (true/false). - MAY provide a hook by extending the node's admin UI to include a JavaScript function (individual to each node instance and able to access global/flow variables).
- If the hook is null or undefined, uibuilder MUST not process any tokens and MUST assume that all traffic is valid.
- MUST EITHER process the returned hook function boolean such that TRUE will pass the outgoing msg to the client as normal. FALSE will drop the incoming msg but will return a control msg back to the client indicating that the token is invalid.
- OR allow the validation hook function to return either the boolean or a token. If a token is returned, the process MUST treat this as a success but MUST also return the new token to the client via a Socket.IO control message (for token refresh processing).
- MUST provide a hook (external function capability, contained in Node-RED's
-
MAY provide functions to create and validate tokens based on a 2 known sets of data (1 for login, 1 for encrypting into the token and later validation).
Note that, in the initial release, the creation and validation of tokens MAY be done externally. Later releases MAY provide the capabilities internally to simplify processing for flow authors. Also note that login processing MAY take 2 forms - a login API and a login page.
- MUST provide a function that can save a token to local storage.
- MUST provide a function that can retrieve a token from local storage and insert it to every Socket.IO msg to the back-end.
- MUST provide a function to delete a token from local storage.
- MUST provide a function to process an incoming invalid token control message from the back-end.
- SHOULD provide a way to receive and save URL's for processing logins.
- SHOULD provide a function to make a call to an API for login processing (API will take any properties to process for login, will return an error string if login fails or a valid token that will be put into local storage.
- SHOULD provide a function to redirect the client page to a login page.
- MUST be able to work with cookies disabled.
If these capabilities are not or until they are provided within uibuilder ...
- MUST use JWT.
- MAY provide both page and API login capability. MUST provide at least one of these.
- Login processing MUST either save the token to local storage (on successful page login), or return the token as text (on successful API login).
- tokens MAY have 2 timestamps: #1 is token expiry and is auto-extended whenever the token is validated (new token sent back to the client on the control channel). #2 is a login expiry that isn't typically auto-extended.
- MUST provide a Token Validation hook function:
- SHOULD validate that each token comes from the client it was issued to (MVP: IP address check, would have to be optional since not all clients will have stable IP addresses).
- MUST return at least a true/false (see above).
- MAY instead return a new token (for token refresh processing).
- MAY validate token on both page load and receipt of Socket.IO msg from client, MAY validate token on Socket.IO client connection. (via a common hook function perhaps).
- MUST timeout token validity as per best practice for JWT/Token auth.
- MAY provide a Client Validation hook function (for authorising outbound messages if required).
NB: "MVP" = Minimum Viable Product.
- Decode cookie: JWT switch status (default to false if cookie not available) & login URL's
- Check for token
- If received
- Add to local storage
- Set token var
- Add to socket query parameter
- If received
- On receipt of jwt_auth_fail control msg
- Redirect page to login url OR Make XHR call to login API
In order to create a token, we have to go through some kind of login process. There are two approaches, both of which need to be supported.
A login page is required where uibuilder is being used to provide a multi-page app. Both the node and the front-end may redirect to the login page. The login page is responsible for capturing the login details and sending them to Node-RED. Node-RED must then combine the data and any other required data such as expiry timestamp, client IP address and encrypt using a given secret. The resulting token must be passed back to the login page. The login page must then put that data into shared storage to allow the uibuilder FE library to access it and return it with every Socket.IO message & page change.
The login API supports a Single Page App (SPA) model similar to Node-RED Dashboard where all resources are in a single HTML page and associated scripts/styles. In this model, no page reloads happen so we do not want to redirect the page to a login page. Instead we need to present a login modal box or a login pseudo-page. This has to be the responsibility of the app designer/developer. The app will call a login REST API in this case, the API call should be standardised and therefore included in the uibuilder FE library.
The API itself should be defined in Node-RED. As an MVP, the API could be external to uibuilder but in that case, the validation of the token also has to be external or the token secret has to be made available to uibuilder.
The API must return a valid token that the client page must store in shared storage. This should be standardised in the uibuilder FE library.
- Decide if Socket.IO connection check is really needed (
io.use()
) - probably not if we want to allow a channel to stay open for control messages. - Move JWT Secret out of node settings - Current location is not secure - probably needs to be external for now (MVP) along with validation code to support an external flow for login processing.
- Functions to store/retrieve the token from local shared storage.
- (maybe) Function to call login API, receive token and store.
Please feel free to add comments to the page (clearly mark with your initials & please add a commit msg so we know what has changed). You can contact me in the Discourse forum, or raise an issue here in GitHub! I will make sure all comments & suggestions are represented here.
-
Walkthrough 🔗 Getting started
-
In Progress and To Do 🔗 What's coming up for uibuilder?
-
Awesome uibuilder Examples, tutorials, templates and references.
-
How To
- How to send data when a client connects or reloads the page
- Send messages to a specific client
- Cache & Replay Messages
- Cache without a helper node
- Use webpack to optimise front-end libraries and code
- How to contribute & coding standards
- How to use NGINX as a proxy for Node-RED
- How to manage packages manually
- How to upload a file from the browser to Node-RED
-
Vanilla HTML/JavaScript examples
-
VueJS general hints, tips and examples
- Load Vue (v2 or v3) components without a build step (modern browsers only)
- How to use webpack with VueJS (or other frameworks)
- Awesome VueJS - Tips, info & libraries for working with Vue
- Components that work
-
VueJS v3 hints, tips and examples
-
VueJS v2 hints, tips and examples
- Dynamically load .vue files without a build step (Vue v2)
- Really Simple Example (Quote of the Day)
- Example charts using Chartkick, Chart.js, Google
- Example Gauge using vue-svg-gauge
- Example charts using ApexCharts
- Example chart using Vue-ECharts
- Example: debug messages using uibuilder & Vue
- Example: knob/gauge widget for uibuilder & Vue
- Example: Embedded video player using VideoJS
- Simple Button Acknowledgement Example Thanks to ringmybell
- Using Vue-Router without a build step Thanks to AFelix
- Vue Canvas Knob Component Thanks to Klaus Zerbe
-
Examples for other frameworks (check version before trying)
- Basic jQuery example - Updated for uibuilder v6.1
- ReactJS with no build - updated for uibuilder v5/6
-
Examples for other frameworks (may not work, out-of-date)
-
Outdated Pages (Historic only)
- v1 Examples (these need updating to uibuilder v2/v3/v4/v5)