From fe3de73e1f6af28ee0b218c879430cf45477b035 Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Thu, 17 Jan 2019 20:47:19 +0000 Subject: [PATCH] --- devdocs/specs/core-installation.txt | 6 +++ devdocs/todo.txt | 27 ++++++++++-- .../docs/ops-config-environment-variables.md | 1 + .../8.0/ayanova/docs/ops-config-jwt-secret.md | 44 +++++++++++++++++++ server/AyaNova/Startup.cs | 24 +++++----- 5 files changed, 87 insertions(+), 15 deletions(-) create mode 100644 devdocs/specs/core-installation.txt create mode 100644 docs/8.0/ayanova/docs/ops-config-jwt-secret.md diff --git a/devdocs/specs/core-installation.txt b/devdocs/specs/core-installation.txt new file mode 100644 index 00000000..0a6628b6 --- /dev/null +++ b/devdocs/specs/core-installation.txt @@ -0,0 +1,6 @@ +# Installer / installation specifications + + +Several environment variables need to be set by the installer. + +- The AYANOVA_JWT_SECRET *MUST* be set to a random value generated by the installer and kep for all time as the key to use unless the user overrides it diff --git a/devdocs/todo.txt b/devdocs/todo.txt index d6d190a5..00672110 100644 --- a/devdocs/todo.txt +++ b/devdocs/todo.txt @@ -17,7 +17,7 @@ SERVER - DO ALL THE THINGS!!!! - all the way down to DOCS MANUAL below which isn't urgent and go back to client stuff - - JWT issues?? + - LOOK INTO JWT issues?? - potentially lots of issues, look into it as using them kind of mindlessly right now. It could be simply that people are attempting to do other things I am not but to be safe read the criticism and see if any of it applies: http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/ @@ -25,8 +25,29 @@ SERVER https://news.ycombinator.com/item?id=14292223 https://news.ycombinator.com/item?id=18804875 - - PASETO instead of JWT?? - - https://paseto.io/ + - JWT Secret key issue: + - Secret key based on license to but is read in before license is read so it's always actually "UNLICENSED" as the regto making the jwt secret the same for all installations + - Unless overridden which no one will do + - Need to randomly generate a secret key on installation and add it as an environment variable + - Don't allow boot without pre-set secret key + - Un-tie secret key from license, they are two different things entirely and shouldn't be confounded + + + + - Add tests to ensure security of JWT + - https://assets.pentesterlab.com/jwt_security_cheatsheet/jwt_security_cheatsheet.pdf + - https://gist.github.com/ejcx/cbf2e1bb75b02c7d77bc1cfcf84a167e + - Test for expired token + - Wrong key / credentials rejected (ISS?) + - Test truncated signature portion (3rd part) + - Test signature transpose bytes + - Test with no or wrong algorithm ensure won't accept + - Test inactive user can't login + + + + + - UPDATE: Update all 3rd party libs in use with server and re-test - It's been a while, some of the modules date to last fall diff --git a/docs/8.0/ayanova/docs/ops-config-environment-variables.md b/docs/8.0/ayanova/docs/ops-config-environment-variables.md index f56a50ca..4e38d148 100644 --- a/docs/8.0/ayanova/docs/ops-config-environment-variables.md +++ b/docs/8.0/ayanova/docs/ops-config-environment-variables.md @@ -25,6 +25,7 @@ These values can all be specified as an environment variable or as a command lin ## API +- [AYANOVA_JWT_SECRET](ops-config-jwt-secret.md) - [AYANOVA_USE_URLS](ops-config-use-urls.md) - [AYANOVA_FOLDER_USER_FILES](ops-config-folder-user-files.md) - [AYANOVA_FOLDER_BACKUP_FILES](ops-config-folder-backup-files.md) diff --git a/docs/8.0/ayanova/docs/ops-config-jwt-secret.md b/docs/8.0/ayanova/docs/ops-config-jwt-secret.md new file mode 100644 index 00000000..a0ca8c9a --- /dev/null +++ b/docs/8.0/ayanova/docs/ops-config-jwt-secret.md @@ -0,0 +1,44 @@ +# JWT secret setting + +AyaNova uses JSON Web Tokens (JWT) for authentication. + +These time limited tokens are signed by the server using a secret key and issued to users when they log in to the AyaNova server. +Every time the user makes a request to the server the JWT is sent along as well and verified to be valid. + +Tokens have a built in expiry mechanism to force users to re-login at periodic intervals in the range of days to weeks. + +Users can be prevented from logging in even if they have a valid token by setting them to inactive. + +## Default + +If no secret key is specified the server will generate a new, random one each time it starts and this means that remote users who previously authenticated will need to login freshly if the server is restarted. + +If you would like to ensure that a server reboot does not affect remote users credentials then you can specify a value for the secret key so that the same key will always be used by the server even if it reboots. + +## Overriding + +AyaNova expects the JWT secret to be provided by an environment variable or command line parameter named + +`AYANOVA_JWT_SECRET` + +The value specified should be a string of up to 32 characters, for example: +`02847This_is_my_secret_key456576` +If fewer than 32 characters are provided they secret will be padded out to 32 characters. If more than 32 characters are specified it will only use the first 32. + +You should use the same precautions as for choosing any other password and ensure the secret is not well known or easily looked up in a dictionary. 32 random characters would be sufficient. + +Example command line parameter + +`dotnet run --AYANOVA_JWT_SECRET="02847This_is_my_secret_key456576"` + +Example environment variable + +Windows + +`set "AYANOVA_JWT_SECRET=02847This_is_my_secret_key456576"` + +Linux / MAC + +`export AYANOVA_JWT_SECRET="02847This_is_my_secret_key456576"` + +If both a command line parameter and an environment variable are set the command line parameter takes precedence. diff --git a/server/AyaNova/Startup.cs b/server/AyaNova/Startup.cs index 02ac09c9..e6987e22 100644 --- a/server/AyaNova/Startup.cs +++ b/server/AyaNova/Startup.cs @@ -98,7 +98,7 @@ namespace AyaNova bool LOG_SENSITIVE_DATA = false; #if (DEBUG) - //LOG_SENSITIVE_DATA = true; + //LOG_SENSITIVE_DATA = true; #endif @@ -211,15 +211,15 @@ namespace AyaNova //get the key if specified var secretKey = ServerBootConfig.AYANOVA_JWT_SECRET; - //If no key specified make a unique one based on license ID which is unique to each customer or trialler + //If no key specified make a unique one + //This means the jwt creds won't survive a server reboot + //so in that case users need to specify an AyaNova_JWT_SECRET environment variable if (string.IsNullOrWhiteSpace(secretKey)) { - // This ensures a key can't work on another AyaNova installation - if (AyaNova.Core.License.ActiveKey.TrialLicense) - secretKey = AyaNova.Core.License.ActiveKey.Id + "5G*QQJ8#bQ7$Xr_@sXfHq4"; - else - secretKey = AyaNova.Core.License.ActiveKey.RegisteredTo + "5G*QQJ8#bQ7$Xr_@sXfHq4"; + secretKey = Util.Hasher.GenerateSalt(); } + //WAS "UNLICENSED5G*QQJ8#bQ7$Xr_@sXfHq4" + //If secretKey is less than 32 characters, pad it if (secretKey.Length < 32) @@ -380,7 +380,7 @@ namespace AyaNova // ******************** TESTING WIPE DB ***************************** // //Set this to true to wipe the db and reinstall a trial license and re-seed the data - var TESTING_REFRESH_DB = true;//####################################################################################### + var TESTING_REFRESH_DB = false;//####################################################################################### #if (DEBUG) //TESTING @@ -438,17 +438,17 @@ namespace AyaNova { context.Request.Path = "/index.html"; context.Response.StatusCode = 200; - context.Response.ContentType = "text/html"; + context.Response.ContentType = "text/html"; await context.Response.SendFileAsync(Path.Combine(env.WebRootPath, "index.html")); } }); - + //Log the active user count so it's in the log record - _log.LogInformation($"BOOT: Active techs - {UserBiz.ActiveCount}" ); + _log.LogInformation($"BOOT: Active techs - {UserBiz.ActiveCount}"); //Log the license info so it's on the record - _log.LogInformation($"BOOT: License -\r\n=-=-=-=-=-=-=-=-=-=-\r\n{AyaNova.Core.License.LicenseInfo}=-=-=-=-=-=-=-=-=-=-" ); + _log.LogInformation($"BOOT: License -\r\n=-=-=-=-=-=-=-=-=-=-\r\n{AyaNova.Core.License.LicenseInfo}=-=-=-=-=-=-=-=-=-=-");