Uploaded image for project: 'Agent-based Installer for OpenShift'
  1. Agent-based Installer for OpenShift
  2. AGENT-426 Productize the Assisted UI container
  3. AGENT-1276

T159: Follow best practices for secure error and exception handling

XMLWordPrintable

    • Icon: Sub-task Sub-task
    • Resolution: Done
    • Icon: Major Major
    • None
    • None
    • None
    • False
    • Hide

      None

      Show
      None
    • False
    • Agent Sprint 273, Agent Sprint 274, Agent Sprint 275, Agent Sprint 276, Agent Sprint 277, Agent Sprint 278

      Follow these guidelines to create secure system or user error messages that reveal the minimum necessary amount of information about the cause of an error, and the internal status of a system:

      • Use a global error handler to catch errors or exceptions not explicitly handled by the application.
      • Analyze the return values of the functions when they contain information about the outcome of the operation. Do not ignore such values. For example a file creation object may return the status code of the operation and indicate the success or failure.
      • Always provide remote systems/users with a generic error message that does not reveal internal system details, such as a stack trace, working directory, or core dump.
      • Centralize the process of error message generation.
        - A class or property-file can contain the text of all returned error messages with various parameters.
        
      • Set application and server configurations to provide minimum details in error messages.
        - Especially when an unhandled exception or error occurs that is not explicitly caught by the application.
        - For example, when an application server handles part of the messages before reaching your application.
        
      • In some cases, the server may provide a facility to catch all errors before forwarding to the remote system.
        - In other cases, the application will need to explicitly develop a handler that catches any errors before redirecting them to the remote system.
            - The advantage here is that the application can log the error with a unique ID, create a generic message to the remote system, and include the unique ID in the response.
            - This allows application support personnel to troubleshoot an issue without revealing system details to remote systems.
        
      • Do not explicitly specify the underlying reason for an error when a number of reasons could have caused it.
        - This is especially important when that information could be used for more directed attacks.
        - A typical example is returning different errors for wrong usernames or wrong passwords, which allows for __user enumeration__ attacks.
        
      • Do not include any sensitive data in error messages.
        - This is especially important when error messages are logged.
        
      • Sign and encrypt detailed error messages when they are required by a specific remote system.
        - Use a set of mutual keys to encrypt the information.
        - The receiving system can also request a signature and verify the signature to verify the authenticity of the errors.
        
      Node.js - Error and Exception Handling

      Follow these instructions:

      • Handle the errors (that are normally received as the first argument by callback functions) properly. It is a widely accepted convention that Node.js functions receive the Error object as the first argument. If this object is not null, handle the error based on its type/cause and create a proper response for known scenarios.
      • If the (cause of the) error is not known, do not swallow the error quietly. This will make debugging your application difficult and may lead to instability in the system (depending on the cause). Exit the process (process.exit()) for errors that signify a serious problem.
      • When an error is passed to your callback function, do not throw it, unless you know that it is handled by the caller. If none of the callers catches the exception, an uncaught exceptions will be thrown that will break the main event loop (and therefore the application). Do not throw any errors in your root handler. Use your next callback function (normally third argument after request and response) in your route handler to pass this error (return next(new Error("Error Message"))). Set up an error handling middleware to handle these error.
      • When using an EventEmitter object (called evEm here), make sure that you have written code for handling "error" event (*evEm.on("error", function (err) {...}

        );*). Otherwise the unhandled error will create an uncaught exception and will break your application.

      • Do not continue when an uncaught exception occurs. Some programmers tend to use * process.on("uncaughtException",...) * in order to prevent the application from crashing. Continuing the process when an unknown exception has happened and the system is unstable is wrong. if you want to use process.on("uncaughtException"...), use it to log the error and then exit the process gracefully.
      • Pay attention to synchronous functions (especially *JSON.parse())* that throw exceptions and place them inside a try/catch block.
      • Domains provide an opportunity for developers to make a more informed decision about next action based on the context of the error (compared to process.on). See Domain documentation to learn how to use Domains.
      • Have a process monitor such as mon, upstart or forever to restart the process when it is exited. Alternatively you can start the process using cluster object.
      • In all the above cases make sure that the errors are logged properly.
      Go: Error and Exception Handling

      Follow these instructions:

      • Handle the errors properly. Errors are conventionally received as the last return value which has type error. If its value is not *nil*, handle the error based on its type/cause and create a proper response for known scenarios.
      • If the cause of the error is not known, do not swallow the error quietly. This will make debugging your application difficult and may lead to instability in the system. Exit the process for errors that signify a serious problem: os.Exit(<error code>)
      • When an error occurs in a function, do not panic, unless you know that it is handled by the caller. If none of the caller functions in the current "goroutine" recover the "panic" in one of their "defer" statements, the program crashes.
      • Put clean-up defer statements right after the target function; otherwise, it is never called if another error occurs in the lines of code before the statement.
      connection, err := net.Dial("tcp", "sdelements.com:80")
      if err != nil {
          fmt.Printf("Couldn't create connection: %v\n", err)
          return
      }
      defer connection.Close()
      
      dstFile, err := os.Create(fileName)
      if err != nil {
          fmt.Printf("Couldn't create file: %v\n", err)
          return
      }
      defer dstFile.Close()
      
      In this simple example, `connection.Close()` would be executed even if `os.create()` fails.
      
      • In all the above cases make sure that the errors are logged properly.

      References

      Imported from SD Elements: https://redhat.sdelements.com/bunits/psse-secure-development/group-1-foundational-platform-offering-openshift/assisted-installer-ui/tasks/phase/specifications/359-T159/

      How Tos:

      Go: http.Handler Wrapper for error handling

      To simplify HTTP error handling and responding with a default error page without revealing unnecessary server details:

      Define your own http.Handler wrapper with an error return value:

      type AppHandler func(http.ResponseWriter, *http.Request) error
      

      Make your controller function to return errors:

      func viewHome(w http.ResponseWriter, r *http.Request) error
      

      Implement the ServeHTTP method of the http.Handler interface on AppHandler:

      func (fn AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
          if err := fn(w, r); err != nil {
          	log.Println() // Internally log the error.
              http.Error(w, "Default Error Page", 500) // Externally show the default error page.
          }
      }
      

      Now the default error page is returned if an error occure during an HTTP request processing:

      http.Handle("/home", AppHandler(viewHome))
      

      References

      Training Modules

      OWASP Top 10 2021
      Defending Node.js
      Defending Rust
      Secure Software Design
      Secure Software Coding

              rawagner@redhat.com Rastislav Wagner
              sdelements Jira-SD-Elements-Integration Bot
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

                Created:
                Updated:
                Resolved: