SEBWIN-309: Corrected implementation of configuration and browser exam key.
This commit is contained in:
		
							parent
							
								
									ad023853d4
								
							
						
					
					
						commit
						9f8920b410
					
				
					 6 changed files with 76 additions and 46 deletions
				
			
		|  | @ -26,13 +26,14 @@ namespace SafeExamBrowser.Browser.Handlers | |||
| { | ||||
| 	internal class ResourceHandler : CefSharp.Handler.ResourceRequestHandler | ||||
| 	{ | ||||
| 		private AppConfig appConfig; | ||||
| 		private SHA256Managed algorithm; | ||||
| 		private BrowserSettings settings; | ||||
| 		private ILogger logger; | ||||
| 		private IRequestFilter filter; | ||||
| 		private AppConfig appConfig; | ||||
| 		private string browserExamKey; | ||||
| 		private IResourceHandler contentHandler; | ||||
| 		private IRequestFilter filter; | ||||
| 		private ILogger logger; | ||||
| 		private IResourceHandler pageHandler; | ||||
| 		private BrowserSettings settings; | ||||
| 		private IText text; | ||||
| 
 | ||||
| 		internal ResourceHandler(AppConfig appConfig, BrowserSettings settings, IRequestFilter filter, ILogger logger, IText text) | ||||
|  | @ -62,16 +63,7 @@ namespace SafeExamBrowser.Browser.Handlers | |||
| 				return CefReturnValue.Cancel; | ||||
| 			} | ||||
| 
 | ||||
| 			// TODO: CEF does not yet support intercepting requests from service workers, thus the user agent must be statically set at browser | ||||
| 			//       startup for now. Once CEF has full support of service workers, the static user agent should be removed and the method below | ||||
| 			//       reactivated. See https://bitbucket.org/chromiumembedded/cef/issues/2622 for the current status of development. | ||||
| 			// AppendCustomUserAgent(request); | ||||
| 
 | ||||
| 			if (settings.SendCustomHeaders) | ||||
| 			{ | ||||
| 			AppendCustomHeaders(request); | ||||
| 			} | ||||
| 
 | ||||
| 			ReplaceSebScheme(request); | ||||
| 
 | ||||
| 			return base.OnBeforeResourceLoad(webBrowser, browser, frame, request, callback); | ||||
|  | @ -93,28 +85,32 @@ namespace SafeExamBrowser.Browser.Handlers | |||
| 			return abort; | ||||
| 		} | ||||
| 
 | ||||
| 		private void AppendCustomUserAgent(IRequest request) | ||||
| 		{ | ||||
| 			var headers = new NameValueCollection(request.Headers); | ||||
| 			var userAgent = request.Headers["User-Agent"]; | ||||
| 
 | ||||
| 			headers["User-Agent"] = $"{userAgent} SEB/{appConfig.ProgramInformationalVersion}"; | ||||
| 			request.Headers = headers; | ||||
| 		} | ||||
| 
 | ||||
| 		private void AppendCustomHeaders(IRequest request) | ||||
| 		{ | ||||
| 			var headers = new NameValueCollection(request.Headers); | ||||
| 			var urlWithoutFragment = request.Url.Split('#')[0]; | ||||
| 			var configurationBytes = algorithm.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + settings.HashValue)); | ||||
| 			var configurationKey = BitConverter.ToString(configurationBytes).ToLower().Replace("-", string.Empty); | ||||
| 			var browserExamBytes = algorithm.ComputeHash(Encoding.UTF8.GetBytes(appConfig.CodeSignatureHash + appConfig.ProgramBuildVersion + configurationKey)); | ||||
| 			var browserExamKey = BitConverter.ToString(browserExamBytes).ToLower().Replace("-", string.Empty); | ||||
| 			var requestHashBytes = algorithm.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + browserExamKey)); | ||||
| 			var requestHashKey = BitConverter.ToString(requestHashBytes).ToLower().Replace("-", string.Empty); | ||||
| 			var userAgent = request.Headers["User-Agent"]; | ||||
| 
 | ||||
| 			headers["X-SafeExamBrowser-ConfigKeyHash"] = configurationKey; | ||||
| 			headers["X-SafeExamBrowser-RequestHash"] = requestHashKey; | ||||
| 			// TODO: CEF does not yet support intercepting requests from service workers, thus the user agent must be statically set at browser | ||||
| 			//       startup for now. Once CEF has full support of service workers, the static user agent should be removed and the method below | ||||
| 			//       reactivated. See https://bitbucket.org/chromiumembedded/cef/issues/2622 for the current status of development. | ||||
| 			// headers["User-Agent"] = $"{userAgent} SEB/{appConfig.ProgramInformationalVersion}"; | ||||
| 
 | ||||
| 			if (settings.SendConfigurationKey) | ||||
| 			{ | ||||
| 				var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + settings.ConfigurationKey)); | ||||
| 				var key = BitConverter.ToString(hash).ToLower().Replace("-", string.Empty); | ||||
| 
 | ||||
| 				headers["X-SafeExamBrowser-ConfigKeyHash"] = key; | ||||
| 			} | ||||
| 
 | ||||
| 			if (settings.SendExamKey) | ||||
| 			{ | ||||
| 				var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + (browserExamKey ?? ComputeBrowserExamKey()))); | ||||
| 				var key = BitConverter.ToString(hash).ToLower().Replace("-", string.Empty); | ||||
| 
 | ||||
| 				headers["X-SafeExamBrowser-RequestHash"] = key; | ||||
| 			} | ||||
| 
 | ||||
| 			request.Headers = headers; | ||||
| 		} | ||||
|  | @ -137,6 +133,16 @@ namespace SafeExamBrowser.Browser.Handlers | |||
| 			return block; | ||||
| 		} | ||||
| 
 | ||||
| 		private string ComputeBrowserExamKey() | ||||
| 		{ | ||||
| 			var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(settings.ExamKeySalt + appConfig.CodeSignatureHash + appConfig.ProgramBuildVersion + settings.ConfigurationKey)); | ||||
| 			var key = BitConverter.ToString(hash).ToLower().Replace("-", string.Empty); | ||||
| 
 | ||||
| 			browserExamKey = key; | ||||
| 
 | ||||
| 			return browserExamKey; | ||||
| 		} | ||||
| 
 | ||||
| 		private bool IsMailtoUrl(string url) | ||||
| 		{ | ||||
| 			return url.StartsWith(Uri.UriSchemeMailto); | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ namespace SafeExamBrowser.Configuration.UnitTests.ConfigurationData | |||
| 		} | ||||
| 
 | ||||
| 		[TestMethod] | ||||
| 		public void MustCalculateCorrectHashValue() | ||||
| 		public void MustCalculateCorrectConfigurationKey() | ||||
| 		{ | ||||
| 			var formatter = new BinaryFormatter(); | ||||
| 			var path1 = $"{nameof(SafeExamBrowser)}.{nameof(Configuration)}.{nameof(UnitTests)}.{nameof(ConfigurationData)}.TestDictionary1.bin"; | ||||
|  | @ -47,9 +47,9 @@ namespace SafeExamBrowser.Configuration.UnitTests.ConfigurationData | |||
| 			sut.Process(data2, settings2); | ||||
| 			sut.Process(data3, settings3); | ||||
| 
 | ||||
| 			Assert.AreEqual("6063c3351ed1ac878c05072598d5079e30ca763c957d8e04bd45131c08f88d1a", settings1.Browser.HashValue); | ||||
| 			Assert.AreEqual("4fc002d2ae4faf994a14bede54d95ac58a1a2cb9b59bc5b4277ff29559b46e3d", settings2.Browser.HashValue); | ||||
| 			Assert.AreEqual("ab426e25b795c917f1fb40f7ef8e5757ef97d7c7ad6792e655c4421d47329d7a", settings3.Browser.HashValue); | ||||
| 			Assert.AreEqual("6063c3351ed1ac878c05072598d5079e30ca763c957d8e04bd45131c08f88d1a", settings1.Browser.ConfigurationKey); | ||||
| 			Assert.AreEqual("4fc002d2ae4faf994a14bede54d95ac58a1a2cb9b59bc5b4277ff29559b46e3d", settings2.Browser.ConfigurationKey); | ||||
| 			Assert.AreEqual("ab426e25b795c917f1fb40f7ef8e5757ef97d7c7ad6792e655c4421d47329d7a", settings3.Browser.ConfigurationKey); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ | |||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using SafeExamBrowser.Settings; | ||||
| using SafeExamBrowser.Settings.Browser; | ||||
|  | @ -69,6 +70,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping | |||
| 				case Keys.Browser.EnableBrowser: | ||||
| 					MapEnableBrowser(settings, value); | ||||
| 					break; | ||||
| 				case Keys.Browser.ExamKeySalt: | ||||
| 					MapExamKeySalt(settings, value); | ||||
| 					break; | ||||
| 				case Keys.Browser.Filter.FilterRules: | ||||
| 					MapFilterRules(settings, value); | ||||
| 					break; | ||||
|  | @ -251,6 +255,14 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		private void MapExamKeySalt(AppSettings settings, object value) | ||||
| 		{ | ||||
| 			if (value is byte[] salt) | ||||
| 			{ | ||||
| 				settings.Browser.ExamKeySalt = BitConverter.ToString(salt).ToLower().Replace("-", string.Empty); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		private void MapMainWindowMode(AppSettings settings, object value) | ||||
| 		{ | ||||
| 			const int FULLSCREEN = 1; | ||||
|  | @ -338,7 +350,8 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping | |||
| 		{ | ||||
| 			if (value is bool send) | ||||
| 			{ | ||||
| 				settings.Browser.SendCustomHeaders = send; | ||||
| 				settings.Browser.SendConfigurationKey = send; | ||||
| 				settings.Browser.SendExamKey = send; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
|  | @ -34,10 +34,10 @@ namespace SafeExamBrowser.Configuration.ConfigurationData | |||
| 				writer.Flush(); | ||||
| 				stream.Seek(0, SeekOrigin.Begin); | ||||
| 
 | ||||
| 				var hashBytes = algorithm.ComputeHash(stream); | ||||
| 				var hashValue = BitConverter.ToString(hashBytes).ToLower().Replace("-", string.Empty); | ||||
| 				var hash = algorithm.ComputeHash(stream); | ||||
| 				var key = BitConverter.ToString(hash).ToLower().Replace("-", string.Empty); | ||||
| 
 | ||||
| 				settings.Browser.HashValue = hashValue; | ||||
| 				settings.Browser.ConfigurationKey = key; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
|  | @ -52,6 +52,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData | |||
| 			internal const string DownloadDirectory = "downloadDirectoryWin"; | ||||
| 			internal const string DownloadPdfFiles = "downloadPDFFiles"; | ||||
| 			internal const string EnableBrowser = "enableSebBrowser"; | ||||
| 			internal const string ExamKeySalt = "examKeySalt"; | ||||
| 			internal const string PopupPolicy = "newBrowserWindowByLinkPolicy"; | ||||
| 			internal const string PopupBlockForeignHost = "newBrowserWindowByLinkBlockForeign"; | ||||
| 			internal const string QuitUrl = "quitURL"; | ||||
|  |  | |||
|  | @ -56,6 +56,11 @@ namespace SafeExamBrowser.Settings.Browser | |||
| 		/// </summary> | ||||
| 		public bool AllowUploads { get; set; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// The configuration key used for integrity checks with server applications (see also <see cref="SendConfigurationKey"/>). | ||||
| 		/// </summary> | ||||
| 		public string ConfigurationKey { get; set; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Determines whether the user needs to confirm the termination of SEB by <see cref="QuitUrl"/>. | ||||
| 		/// </summary> | ||||
|  | @ -76,16 +81,16 @@ namespace SafeExamBrowser.Settings.Browser | |||
| 		/// </summary> | ||||
| 		public bool EnableBrowser { get; set; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// The salt value for the calculation of the exam key which is used for integrity checks with server applications (see also <see cref="SendExamKey"/>). | ||||
| 		/// </summary> | ||||
| 		public string ExamKeySalt { get; set; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// The settings to be used for the browser request filter. | ||||
| 		/// </summary> | ||||
| 		public FilterSettings Filter { get; set; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// The hash value of the raw settings data, used for integrity checks with server applications (see also <see cref="SendCustomHeaders"/>). | ||||
| 		/// </summary> | ||||
| 		public string HashValue { get; set; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// The settings to be used for the main browser window. | ||||
| 		/// </summary> | ||||
|  | @ -107,9 +112,14 @@ namespace SafeExamBrowser.Settings.Browser | |||
| 		public string QuitUrl { get; set; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Determines whether custom request headers (e.g. for integrity checks) are sent with every HTTP request. | ||||
| 		/// Determines whether the configuration key header is sent with every HTTP request (see also <see cref="ConfigurationKey"/>). | ||||
| 		/// </summary> | ||||
| 		public bool SendCustomHeaders { get; set; } | ||||
| 		public bool SendConfigurationKey { get; set; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Determines whether the exam key header is sent with every HTTP request (see also <see cref="ExamKeySalt"/>). | ||||
| 		/// </summary> | ||||
| 		public bool SendExamKey { get; set; } | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// The URL with which the main browser window will be loaded. | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 dbuechel
						dbuechel