Security API for Building Block Developers

Blackboard has integrated with a best practices open source security library from the Open Web Application Project's (OWASP) Enterprise Security API (ESAPI). This security library ships by default installed on Blackboard Learn through a Building Block called "ESAPI Security Module" and is required for system operation. Blackboard strongly recommends all Building Block developers leverage this new Security API based on OWASP ESAPI for Java and ESAPI for JavaScript. These Security API changes not only include best practice implementations, but also increase the ease of using the methods through consistent nomenclature.

In later releases, the ESAPI Security Module Building Block API is part of Blackboard Learn’s core code and is available by default.

As part of secure coding practices, input that may be influenced by users, whether trusted or not, should be validated on the server-side before processing (input validation) as well as prior to display (output validation or escaping). This helps ensure system resiliency and prevents security issues such as cross-site scripting.


Input validation

When receiving input from the request, always validate and always validate server-side. Blackboard has implemented several popular use cases requiring validation. A few examples are provided below.

  • blackboard.platform.security.ValidationUtility.isValidDirectoryPath( String )
  • blackboard.platform.security.ValidationUtility.isValidGuid( String )
  • blackboard.platform.security.ValidationUtility.isValidEnumeratedType( Enum, String )

Output validation/encoding/escaping

When displaying any input, always ensure it is displayed in the correct context that it will be embedded in:

Java methods

  • blackboard.platform.security.EscapeUtility.escapeForHTML ( String )
  • blackboard.platform.security.EscapeUtility.escapeForHTMLAttribute ( String )
  • blackboard.platform.security.EscapeUtility.escapeForJavascript ( String )
  • blackboard.platform.security.EscapeUtility.escapeForUrl ( String )
  • blackboard.platform.security.EscapeUtility.escapeForCSS ( String )
  • blackboard.platform.security.EscapeUtility.escapeForXML ( String )
  • blackboard.platform.security.EscapeUtility.escapeForXMLAttribute ( String )

JSP methods

  • ${bbNG:EscapeForHTML( String )}
  • ${bbNG:EscapeForJavascript( String )}
  • ${bbNG:EscapeForURL( String )}
  • ${bbNG:EscapeForCSS( String )}
  • ${bbNG:EscapeForXML( String )}
  • ${bbNG:EscapeForXMLAttribute( String )}

JavaScript methods

All ESAPI for JavaScript methods are available for use. A list of more commonly used methods:

  • $ESAPI.encoder().canonicalize( String )
  • $ESAPI.encoder().encodeForHTML( String )
  • $ESAPI.encoder().encodeForHTMLAttribute( String )
  • $ESAPI.encoder().encodeForCSS( String )
  • $ESAPI.encoder().encodeForJavaScript( String )
  • $ESAPI.encoder().encodeForURL( String )

Decipher encrypted text

Decipher Encrypted Context

To encrypt data during context passing, Blackboard Learn and the external URL must have access to the same context encryption key. The key must be created from the Manage Context Encryption Key feature available on the Administrator Panel. Once the key has been created it must be downloaded and distributed to external servers that will accept context.


Code example

After downloading a context encryption key it must be made available to the URL that will receive encrypted data through context-passing. The code example below shows how to programmatically decipher encrypted context data on the external URL when it is passed.

The object indicated by the target URL (in this case, index.jsp) could decrypt the context as follows (importing blackboard.client.decryption.*):

String context = request.getParameter("context");//if isEncryptionEnabled = false, base 64 encoding will be used instead //of encryptionboolean isEncryptionEnabled = true;ContextDecryptor bfd = ContextDecryptorFactory.getContextDecryptor(isEncryptionEnabled ); // retrieve the Blackboard encryption key as a File or InputStream File key = new File( strKeyLocation ); // or InputStream key = // implementation detail...// to simply decrypt the context string String decryptedContext = bfd.decrypt( context, key );// or, to get a HashMap of all key-value pairsHashMap map = bfd.parseEncryptedContext( context, key );// then search the HashMap for an expected value, and continue.if (map.containsKey( "user" ){ // execute...}


Resolving multiple keys

The code example above is useful when there is a one-to-one relationship between the external URL and Blackboard Learn. For instances where an external URL is supporting a Building Block for multiple instances of Blackboard Learn, the hostname of the instance can be used to handle multiple keys.

In the example below, the key is found through association with the hostname for the instance or virtual installation of Blackboard Learn. In this case the hostname is physics.yourinstitution.com. Since the client decryption code has the ability to pass in a decryption key to the ContextDecryptor object, the client server must be able to map a hostname to its appropriate key (usually accessed as a file, but can be an InputStream).

Building Block server developers can write a simple wrapper that:

  1. Pulls the hostname off the request.
  2. Looks up the encryption key file in the hostname-encryptionKey map.
  3. Passes the encryption key to the decrypt() method.

Pseudocode wrapper implemented by a client server utility object:

/**

 * Utility Pseudocode

 */

decryptByHostname( HttpServletRequest request)

{     

// get context parameter from the request

        String context = request.getParameter("context");

       // determine if the context is encrypted

 String strEncryptInd = request.getParameter("encrypt");

 if ( (strEncryptInd != null) && (strEncryptInd.equalsIgnoreCase("y")) )

 {    

        isEncrypted = true;

       }

       // if isEncrypted, look up the key

       key = null;

       if (isEncrypted)

       {

        // get hostname from HttpUtils.getRequestURL().getHost();

        // get key map, possibly stored as a property file in the format

        // physics.yourinstitution.com= /key/file/location/physics_yourinstitution_com/key.sec

        // get encryption key from map as either a File or an InputStream,

        // depending on client implementation detail (key is passed as null if

 // isEncrypted= false, and Base64Encoding is used instead)

       }       

ContextDecryptor decryptor =

 ContextDecryptorFactory.getContextDecryptor(isEncrypted);

       // then either return the values passed in the context as either

// a HashMap of key-value pairs

HashMap values = decryptor.parseEncryptedContext(context, key);

// or a String

String values = decryptor.decrypt(context, key);

       return values;

}