Skip to content
Julian Knight edited this page Jan 3, 2020 · 12 revisions

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.

Caveats

  • 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.

Requirements

Internal to uibuilder back end

  • 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).
  • 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).
  • 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.

Internal to the uibuilder front end

  • 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.

External to uibuilder

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.

Implementation Outline

FE Client Processing

  • 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
  • On receipt of jwt_auth_fail control msg
    • Redirect page to login url OR Make XHR call to login API

Login Resources

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.

Login Page

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.

Login API

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.

To Do

Node

  • 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.

Front End

  • Functions to store/retrieve the token from local shared storage.
  • (maybe) Function to call login API, receive token and store.
Clone this wiki locally