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);
|
||||
}
|
||||
|
||||
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…
Reference in a new issue