public void UniqueOutput() { // Verify that we generate and use a unique IV for // every encryption run such that encrypting the same // data will return different results. This is an // important security best practice. const int iterations = 1000; var decrypted = "We hold these truths to be self-evident, that all men are created equal."; var encryptions = new HashSet <string>(); using (var cipher = new AesCipher()) { for (int i = 0; i < iterations; i++) { var encrypted = cipher.EncryptToBase64(decrypted); Assert.DoesNotContain(encrypted, encryptions); Assert.Equal(decrypted, cipher.DecryptStringFrom(encrypted)); encryptions.Add(encrypted); } } }
/// <summary> /// <para> /// Transforms responses before sending them back to the client. In this case it intercepts the initial Bla /// </para> /// </summary> /// <param name="httpContext">The HTTP Context.</param> /// <param name="proxyResponse">The Proxied Response.</param> /// <returns></returns> public override async ValueTask <bool> TransformResponseAsync(HttpContext httpContext, HttpResponseMessage proxyResponse) { await SyncContext.Clear; await base.TransformResponseAsync(httpContext, proxyResponse); var session = new Session() { Id = NeonHelper.CreateBase36Uuid(), UpstreamHost = proxyResponse.RequestMessage.RequestUri.Authority }; var headers = proxyResponse.Content.Headers; var mediaType = headers.ContentType?.MediaType ?? ""; if (!httpContext.Request.Cookies.ContainsKey(Service.SessionCookieName) || (mediaType == "text/html" && httpContext.Response.StatusCode == 200)) { httpContext.Response.Cookies.Append(Service.SessionCookieName, cipher.EncryptToBase64($"{session.Id}")); } await cache.SetAsync(session.Id, NeonHelper.JsonSerializeToBytes(session)); return(true); }
public void Encrypt_ToBase64() { // Encrypt a byte array: using (var cipher = new AesCipher()) { var decrypted = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; var encrypted = cipher.EncryptToBase64(decrypted); Assert.Equal(decrypted, cipher.DecryptBytesFrom(encrypted)); } // Encrypt a string: using (var cipher = new AesCipher()) { var decrypted = "1234567890"; var encrypted = cipher.EncryptToBase64(decrypted); Assert.Equal(decrypted, cipher.DecryptStringFrom(encrypted)); } }
/// <summary> /// Transforms the response before returning it to the client. /// /// <para> /// This method will add a <see cref="Cookie"/> to each response containing relevant information /// about the current authentication flow. It also intercepts redirects from Dex and saves any relevant /// tokens to a cache for reuse. /// </para> /// </summary> /// <param name="httpContext"></param> /// <param name="proxyResponse"></param> /// <returns></returns> public override async ValueTask <bool> TransformResponseAsync(HttpContext httpContext, HttpResponseMessage proxyResponse) { await base.TransformResponseAsync(httpContext, proxyResponse); Cookie cookie = null; if (httpContext.Request.Cookies.TryGetValue(Service.SessionCookieName, out var requestCookieBase64)) { try { logger.LogDebug($"Decrypting existing cookie."); cookie = NeonHelper.JsonDeserialize <Cookie>(cipher.DecryptBytesFrom(requestCookieBase64)); } catch (Exception e) { logger.LogError(e); cookie = new Cookie(); } } else { logger.LogDebug($"Cookie not present."); cookie = new Cookie(); } // If we're being redirected, intercept request and save token to cookie. if (httpContext.Response.Headers.Location.Count > 0 && Uri.IsWellFormedUriString(httpContext.Response.Headers.Location.Single(), UriKind.Absolute)) { var location = new Uri(httpContext.Response.Headers.Location.Single()); var code = HttpUtility.ParseQueryString(location.Query).Get("code"); if (!string.IsNullOrEmpty(code)) { if (cookie != null) { var redirect = cookie.RedirectUri; var token = await dexClient.GetTokenAsync(cookie.ClientId, code, redirect, "authorization_code"); await cache.SetAsync(code, cipher.EncryptToBytes(NeonHelper.JsonSerializeToBytes(token)), cacheOptions); logger.LogDebug(NeonHelper.JsonSerialize(token)); cookie.TokenResponse = token; httpContext.Response.Cookies.Append( Service.SessionCookieName, cipher.EncryptToBase64(NeonHelper.JsonSerialize(cookie)), new CookieOptions() { Path = "/", Expires = DateTime.UtcNow.AddSeconds(token.ExpiresIn.Value).AddMinutes(-60), Secure = true, SameSite = SameSiteMode.Strict }); return(true); } } } // Add query parameters to the cookie. if (httpContext.Request.Query.TryGetValue("client_id", out var clientId)) { logger.LogDebug($"Client ID: [{clientId}]"); cookie.ClientId = clientId; } if (httpContext.Request.Query.TryGetValue("state", out var state)) { logger.LogDebug($"State: [{state}]"); cookie.State = state; } if (httpContext.Request.Query.TryGetValue("redirect_uri", out var redirectUri)) { logger.LogDebug($"Redirect Uri: [{redirectUri}]"); cookie.RedirectUri = redirectUri; } if (httpContext.Request.Query.TryGetValue("scope", out var scope)) { logger.LogDebug($"Scope: [{scope}]"); cookie.Scope = scope; } if (httpContext.Request.Query.TryGetValue("response_type", out var responseType)) { logger.LogDebug($"Response Type: [{responseType}]"); cookie.ResponseType = responseType; } httpContext.Response.Cookies.Append( Service.SessionCookieName, cipher.EncryptToBase64(NeonHelper.JsonSerialize(cookie)), new CookieOptions() { Path = "/", Expires = DateTime.UtcNow.AddHours(24), Secure = true, SameSite = SameSiteMode.Strict }); return(true); }