Request Hash For Validating SEB Config in the LMS/E-Assessment System
The Config Key feature can be used by the quiz module of a learning management system (LMS) or another e-assessment system to ensure that the Safe Exam Browser (SEB) exam client is correctly configured/secured (either client settings or settings for a specific exam).
To achieve this, a key is generated for the settings SEB is using.
To use the Config Key feature, an e-assessment system has to have support for the Config Key feature built in and the following SEB versions (or later versions) must be used:
- SEB 2.1.4 for macOS
- SEB 3.0 for Windows (SEB 2.4 for Windows supports the Config Key, but there might be compatibility issues because of some other limitations of SEB 2.4)
- SEB 2.1.14 for iOS
To configure the Config Key feature, these steps are necessary:
- Enable the option "Use Browser & Config Keys (send in HTTP header)" in Exam preferences pane (settings key sendBrowserExamKey = true).
- If your e-assessment system generates SEB settings itself, then it should generate the according Config Key as well and no manual action should be necessary.
- Otherwise copy this key to the according field in your quiz/exam settings in the LMS/assessment system.
Different than the Browser Exam Key, the Config Key of existing settings doesn't change, when the SEB version is updated (even if this version introduces new setting options). The Config Key is also same in each platform version of SEB, so SEB for Windows and SEB for iOS will generate the same key as SEB for macOS. The Config Key changes when you alter and re-save a SEB config file. But still another version of SEB will calculate the same Config Key for this newly saved config file.
The most important advantage of the Config Key is, that it can be calculated in an exam system, if that system automatically generates SEB settings for an exam (server-side). So as soon as LMS/assessment systems will support the Config Key, no manual copy-paste of keys will be necessary for each different config file and SEB version.
When the option "Use Browser & Config Keys (send in HTTP header)" is activated, SEB adds an additional header to every HTTP request containing the Base16 encoded SHA256 hash of the Config Key concatenated with the requested URL (which protects the key and keeps it secret, since it changes not only for each SEB config but also for each requested URL). The header looks like this:
"X-SafeExamBrowser-ConfigKeyHash" = 81aad4ab9dfd447cc479e6a4a7c9a544e2cafc7f3adeb68b2a21efad68eca4dc;
The LMS then can verify the Config Key received in the HTTP request. For this, the Config Key generated server-side or saved in the quiz/exam settings has to be concatenated with the requested URL, SHA256 hashed and compared with the value received in the "X-SafeExamBrowser-ConfigKeyHash" header.
From SEB 3.0 for macOS and iOS and SEB 3.4 for Windows, it's also possible to query the Config Key and the Browser Exam Key using the new SEB JavaScript API.
Query the Config Key and Browser Exam Key with the SEB JavaScript API
The modern WebKit browser engine (WKWebView) supported in SEB for macOS and iOS 3.0 and newer doesn't support sending the Config Key (CK) and Browser Exam Key (BEK) in HTTP headers. This feature will not be possible with WKWebView at all, so you will need to transition to the SEB JavaScript API at some point, if you want to support SEB for macOS/iOS (also Apple announced to remove support for the classic WebView from at least iOS at some point in the future).
For backward compatibility (in SEB 3.0 for macOS/iOS or newer), if the setting Exam / Use Browser & Config Keys is enabled ( key sendBrowserExamKey=true), the classic WebView is used depending on the new setting Browser Features/"Select Browser Engine Policy" (key browserWindowWebView). "Prefer Modern in New Tab+Different Host" (value 2), ensures that you still can use testing systems/LMS like Moodle which use the Browser Exam/Config Key test for SEB connecting to the exam (as they open in the classic WebView), but webpages opened in new tabs/windows, which have another host than the exam system, open in WKWebView.
After you implement support for the SEB JavaScript API in your exam solution, you can add the setting browserWindowWebView=3 to the SEB settings you generate, then the modern WebView will be used for all pages. Please note: In SEB 3.x for macOS/iOS, URL content filters currently are not yet supported in the modern WebView (only URL filters for links and page loading, enabled with URLFilterEnable = true). Therefore if you use the setting URLFilterEnableContentFilter=true, this still forces the classic WebView being used (regardless of the setting browserWindowWebView) and the CK/BEK cannot be queried with the SEB JavaScript API.
Note: It's not required to set the key sendBrowserExamKey= true for querying the BEK/CK using the SEB JavaScript API.
SEB JavaScript API
- Function void SafeExamBrowser.security.updateKeys(function callback)
In SEB 3.0 for macOS/iOS, this function needs to be invoked first (for example in <body onload="...">). Indicate a callback function (see this demo page) as parameter, which will be called asynchronously by SEB after updating the variables below. In SEB 3.3.2 for Windows and SEB 3.1 for macOS and iOS, calling the updateKeys() function is not necessary, the variables below are already set when the page is loaded (see the second demo page): - Variable SafeExamBrowser.security.browserExamKey
- Variable SafeExamBrowser.security.configKey
which are the BEK and CK hashed with the URL of the page (remove a possible fragment part of the URL and use SHA256, see below). The keys are identical to the ones send in the HTTP request header when the classic WebView is used. Use this new JS API in an assessment web app with BEK/CK and WKWebView. Also set the key browserWindowWebView to the policy "Prefer Modern" (value 3). - Variable SafeExamBrowser.version with format appDisplayName_<OS>_versionString_buildNumber_bundleID
<OS> currently can have the values iOS, macOS or Windows.
This variable is set regardless whether updateKeys() is called.
How to transition to the SEB JavaScript API
The SEB JavaScript API is only supported by SEB for Windows starting SEB 3.3.2. If you need to use older SEB versions, you should support both methods for querying the CK/BEK. Now that SEB 3.3.2 is released, you could only use the SEB JavaScript API, as long as you can require students to only use the required SEB versions (SEB versions for Window before 3.3.2 and macOS/iOS before 3.0 won't support the JS API). Also in SEB 3.x for macOS/iOS, you currently cannot use URL content filters together with the JS API (see above).
- Support both the HTTP header check and the JavaScript API for the ConfigKey (and if required for the BrowserExamKey BEK). As mentioned, the keys will be same for the same web page (URL). So the web application can check for the availability of the SafeExamBrowser.security.configKey/SafeExamBrowser.security.browserExamKey variables or the SafeExamBrowser.security.updateKeys() function (or the SafeExamBrowser.version variable). If it‘s available, then the JavaScript API is supported and you can get the ConfigKey/BEK from it. Otherwise get the ConfigKey/BEK from the HTTP headers.
- In the SEB configuration generated by Moodle, set the SEB settings key browserWindowWebView to the policy "Prefer Modern" (integer value 3), which will force the use of the modern WebView and the Javascript API in SEB versions where it is available.
- If you need to support only the SEB JS API (no support for older SEB versions), you can set sendBrowserExamKey to false. Then SEB for Windows won't add the keys to HTTP headers anymore, in addition to setting the SEB JS API variables.
How the Config Key is generated in SEB
Unlike the Browser Exam Key, the Config Key uses a standardized method to generate a checksum of all setting key/values of a SEB config file. As long as the same method is used in all SEB versions and in e-assessment systems (server-side), then the generated Config Key will be identical. There is another important difference in how the Config Key is generated: The SEB client only uses setting key/values to calculate the checksum, which are actually contained in an opened config file. This is important in case the SEB exam client is updated and this new version introduces new setting options. If the new keys and their (default) values would be included to calculate the Config Key checksum, the key would change, even though the config file didn't change. That's why when calculating the Config Key, the SEB client skips newly introduced keys (as long as these have their default values, which are always defined as "safe" values). The exam system uses all key/values (exceptions see below) it generates for calculating the Config Key.
When an existing config file is modified in the SEB preferences window or SEB Config Tool and then saved, the Config Key is re-calculated and then uses all keys the according SEB version supports. This new config file will then have another Config Key value, but when opened for an exam in an older SEB version, that version will still calculate this same new value for the Config Key (even though the older SEB version doesn't support the newly added setting keys).
The standardized method to calculate the Config Key hash (checksum) converts SEB settings into JSON objects, where the key/values in the root-level and all higher-level objects (dictionaries) are alphabetically sorted by their key names. This is necessary, because the order of elements when applying a hash function matters (if the order isn't same, the hash function will return a different value). As key/values in standard JSON objects aren't ordered by definition, you might have to implement a custom converter for transforming the SEB property list (plist) XML into this sorted JSON-like ("SEB-JSON" object. It's also important to use the proper format for the single elements (key names, values) of the SEB-JSON object, see below.
Summary Config Key Generation
- Convert the plist XML of a decrypted/unencrypted SEB config file to a ordered JSON-like "SEB-JSON" object. Consider following special formatting details:
- Remove the key "originatorVersion" first. This key is exempted from the SEB-JSON hash (it's a special key which doesn't have any functionality, it's just meta data indicating which SEB version saved the config file)
- Don't add any whitespace or line formatting to the SEB-JSON string.
- Don't add character escaping (also back shlashes "\" as found in URL filter rules should not be escaped).
- All <dict> elements from the plist XML must be ordered (alphabetically sorted) by their key names. Use a recursive method to apply ordering also to nested dictionaries contained in the root-level dictionary and in arrays. Use non-localized (culture invariant), ideally non-ASCII value based case insensitive ordering (non-ASCII value based case insensitive ordering: for example the key <key>allowWlan</key> comes before <key>allowWLAN</key>). Note: We removed the requirement for using non-ASCII value based case insensitive ordering, as APIs in operating systems often don't support non-ASCII value based case insensitive ordering reliably (it's undefined if lower or upper case letters are ordered higher). Instead same key names with different lower/uppercase letters should not be used.
- Remove empty <dict> elements (key/value). Current versions of SEB clients should anyways not generate empty dictionaries, but this was possible with outdated versions. If config files have been generated that time, such elements might still be around.
- All string elements must be UTF8 encoded.
- Base16 strings should use lower-case a-f characters, even though this isn't relevant in the current implementation of the Config Key calculation.
- <data> plist XML elements must be converted to Base64 strings.
- <date> plist XML elements must be converted to ISO 8601 formatted strings.
- Generate a SHA256 hash from the SEB-JSON string.
- Encode the hash value as a Base16 string. The result should be a 32 Bytes, 64 chars string.
Checking the Config Key which SEB clients send with HTTP requests
- Create a SHA256 hash value from the absolute URL without Fragment part with appended Config Key hash string. The absolute URL (as a UTF8 encoded string) is created by resolving the relative URL against its base according to the algorithm given in RFC 1808. If the absolute URL contains a Fragment (the last part of an URL starting with #, when using page anchors), the fragment needs to be removed from the URL. Then concatenate this URL string with the Config Key hash string and apply SHA256. The result is again a SHA256 hash value (32 Bytes, 64 chars string when encoded Base16).
- Compare the resulting hash string with the one received in the custom header in each HTTP request:
"X-SafeExamBrowser-ConfigKeyHash" = <must be equal with the hash value created in 1.>;
- Refuse to start or abort the exam if the hash value in each HTTP request isn't equal with the calculated one. When the exam is being started and the hash values don't match, then display an error message and don't start the exam. If the exam was already running, then save all results entered until now, stop the exam and inform the user. It should not be possible to leave the quiz module (in case of an LMS) until an exam supervisor/administrator enters some special unlocking password or that user's blocked quiz is reset in the LMS backend. Otherwise the user could possibly enter into his LMS account, check some resources prepared beforehand and then inform the exam supervisor that there was some technical problem and ask for getting it unlocked and continue the exam.
Example SEB-JSON String
{"additionalResources":[],"allowAirPlay":false,"allowBrowsingBackForward":false,"allowDictation":false,"allowDictionaryLookup":false,"allowDisplayMirroring":false,"allowDownUploads":false,"allowedDisplayBuiltin":true,"allowedDisplaysMaxNumber":1,"allowFlashFullscreen":false,"allowPDFPlugIn":false,"allowPreferencesWindow":true,"allowQuit":true,"allowScreenSharing":false,"allowSiri":false,"allowSpellCheck":false,"allowSwitchToApplications":true,"allowUserAppFolderInstall":false,"allowUserSwitching":true,"allowVideoCapture":false,"allowVirtualMachine":false,"allowWlan":false,"blacklistURLFilter":"","blockPopUpWindows":false,"browserMessagingPingTime":120000,"browserMessagingSocket":"ws:\localhost:8706","browserScreenKeyboard":false,"browserURLSalt":true,"browserUserAgent":"","browserUserAgentMac":0,"browserUserAgentMacCustom":"","browserUserAgentWinDesktopMode":0,"browserUserAgentWinDesktopModeCustom":"","browserUserAgentWinTouchMode":0,"browserUserAgentWinTouchModeCustom":"","browserViewMode":0,"browserWindowAllowReload":true,"browserWindowShowURL":0,"chooseFileToUploadPolicy":0,"configKeySalt":"","createNewDesktop":true,"detectStoppedProcess":true,"downloadAndOpenSebConfig":true,"downloadDirectoryOSX":"/Users/drs/Downloads","downloadDirectoryWin":"Desktop","downloadPDFFiles":false,"embeddedCertificates":[],"enableAltEsc":false,"enableAltF4":false,"enableAltMouseWheel":false,"enableAltTab":true,"enableAppSwitcherCheck":true,"enableBrowserWindowToolbar":false,"enableCtrlEsc":false,"enableEsc":false,"enableF1":false,"enableF10":false,"enableF11":false,"enableF12":false,"enableF2":false,"enableF3":false,"enableF4":false,"enableF5":true,"enableF6":false,"enableF7":false,"enableF8":false,"enableF9":false,"enableJava":false,"enableJavaScript":true,"enableLogging":true,"enablePlugIns":true,"enablePrintScreen":false,"enablePrivateClipboard":true,"enableRightMouse":false,"enableSebBrowser":true,"enableStartMenu":false,"enableTouchExit":0,"enableZoomPage":true,"enableZoomText":true,"examKeySalt":"fOwye5rcju6okk1489PNuKKm5VLTObxNAIn35vZNNrc=","exitKey1":2,"exitKey2":10,"exitKey3":5,"forceAppFolderInstall":true,"hashedAdminPassword":"155290511d5c4bfb1369217d6846c8eef1ed6a564579516eaf36cf5598ac92de","hashedQuitPassword":"8577da2ea54085708b3b851bc50315a36bb740ba5135e747cfb12457b5d3060f","hideBrowserWindowToolbar":false,"hookKeys":true,"ignoreExitKeys":true,"ignoreQuitPassword":false,"insideSebEnableChangeAPassword":false,"insideSebEnableEaseOfAccess":false,"insideSebEnableLockThisComputer":false,"insideSebEnableLogOff":false,"insideSebEnableNetworkConnectionSelector":false,"insideSebEnableShutDown":false,"insideSebEnableStartTaskManager":false,"insideSebEnableSwitchUser":false,"insideSebEnableVmWareClientShade":false,"killExplorerShell":false,"logDirectoryOSX":"","logDirectoryWin":"","logLevel":4,"mainBrowserWindowHeight":"100%","mainBrowserWindowPositioning":1,"mainBrowserWindowWidth":"100%","minMacOSVersion":0,"monitorProcesses":true,"newBrowserWindowAllowReload":true,"newBrowserWindowByLinkBlockForeign":false,"newBrowserWindowByLinkHeight":"100%","newBrowserWindowByLinkPolicy":2,"newBrowserWindowByLinkPositioning":2,"newBrowserWindowByLinkWidth":"1000","newBrowserWindowByScriptBlockForeign":false,"newBrowserWindowByScriptPolicy":2,"newBrowserWindowNavigation":true,"newBrowserWindowShowReloadWarning":false,"openDownloads":false,"oskBehavior":2,"permittedProcesses":[{"active":true,"allowedExecutables":"","allowUserToChooseApp":false,"arguments":[],"autostart":true,"description":"","executable":"xulrunner.exe","iconInTaskbar":true,"identifier":"XULRunner","os":1,"path":"../xulrunner/","runInBackground":false,"strongKill":true,"title":"SEB","windowHandlingProcess":""}],"pinEmbeddedCertificates":false,"prohibitedProcesses":[],"proxies":{"AutoConfigurationEnabled":false,"AutoConfigurationJavaScript":"","AutoConfigurationURL":"","AutoDiscoveryEnabled":false,"ExceptionsList":[],"ExcludeSimpleHostnames":false,"FTPEnable":false,"FTPPassive":true,"FTPPassword":"","FTPPort":21,"FTPProxy":"","FTPRequiresPassword":false,"FTPUsername":"","HTTPEnable":false,"HTTPPassword":"","HTTPPort":80,"HTTPProxy":"","HTTPRequiresPassword":false,"HTTPSEnable":false,"HTTPSPassword":"","HTTPSPort":443,"HTTPSProxy":"","HTTPSRequiresPassword":false,"HTTPSUsername":"","HTTPUsername":"","RTSPEnable":false,"RTSPPassword":"","RTSPPort":554,"RTSPProxy":"","RTSPRequiresPassword":false,"RTSPUsername":"","SOCKSEnable":false,"SOCKSPassword":"","SOCKSPort":1080,"SOCKSProxy":"","SOCKSRequiresPassword":false,"SOCKSUsername":""},"proxySettingsPolicy":0,"quitURL":"","removeBrowserProfile":true,"removeLocalStorage":false,"restartExamPasswordProtected":true,"restartExamText":"","restartExamURL":"","restartExamUseStartURL":false,"sebConfigPurpose":1,"sebMode":0,"sebServerFallback":false,"sebServerURL":"","sebServicePolicy":2,"sendBrowserExamKey":true,"showInputLanguage":false,"showMenuBar":true,"showReloadButton":true,"showReloadWarning":false,"showTaskBar":true,"showTime":true,"startURL":"http://www.safeexambrowser.org/exams","taskBarHeight":40,"touchOptimized":false,"URLFilterEnable":true,"URLFilterEnableContentFilter":true,"URLFilterIgnoreList":[],"URLFilterMessage":1,"urlFilterRegex":true,"URLFilterRules":[{"action":1,"active":true,"expression":"safeexambrowser.org/exams","regex":false},{"action":1,"active":true,"expression":"safeexambrowser.org/css/*","regex":false},{"action":1,"active":true,"expression":"safeexambrowser.org/font-awesome/css/*","regex":false},{"action":1,"active":true,"expression":"safeexambrowser.org/images/*","regex":false},{"action":1,"active":true,"expression":"safeexambrowser.org/js/*","regex":false},{"action":1,"active":true,"expression":"safeexambrowser.org/exams/*","regex":false}],"urlFilterTrustedContent":true,"whitelistURLFilter":"^.*?:\/\/((safeexambrowser\.org)|(.*?\.safeexambrowser\.org))\/exams(()|(?.*?))$;^.*?:\/\/((safeexambrowser\.org)|(.*?\.safeexambrowser\.org))\/((css\/.*?)|(css))(()|(?.*?))$;^.*?:\/\/((safeexambrowser\.org)|(.*?\.safeexambrowser\.org))\/((font-awesome\/css\/.*?)|(font-awesome\/css))(()|(?.*?))$;^.*?:\/\/((safeexambrowser\.org)|(.*?\.safeexambrowser\.org))\/((images\/.*?)|(images))(()|(?.*?))$;^.*?:\/\/((safeexambrowser\.org)|(.*?\.safeexambrowser\.org))\/((js\/.*?)|(js))(()|(?.*?))$;^.*?:\/\/((safeexambrowser\.org)|(.*?\.safeexambrowser\.org))\/((exams\/.*?)|(exams))(()|(?.*?))$","zoomMode":0}