This is an automated email from the ASF dual-hosted git repository. jleroux pushed a commit to branch release22.01 in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git
The following commit(s) were added to refs/heads/release22.01 by this push: new 55b603e69d Fixed: JWT Authentication Error (OFBIZ-12724) 55b603e69d is described below commit 55b603e69d9fb88f319d879493a5eecdcd90c3c6 Author: Jacques Le Roux <jacques.le.r...@les7arts.com> AuthorDate: Thu Dec 15 09:28:16 2022 +0100 Fixed: JWT Authentication Error (OFBIZ-12724) Ensures the length of the secret is at least 512 bit long https://www.rfc-editor.org/rfc/rfc7518#page-7 https://javadoc.io/doc/com.auth0/java-jwt/latest/com/auth0/jwt/algorithms/Algorithm.html#HMAC512 We should follow the rule and give a 512 bit key by default and provide validation based on the same rule. jleroux: based on recommendation by Les Hazlewood (JJWT founder, Apache Shiro founder): https://github.com/jhipster/generator-jhipster/issues/8165#issuecomment-416246549 I used a 512 bits key I created using https://www.allkeysgenerator.com (Encryption key mode). But I got this error: EntitySaxReader |E| Fatal Error reading XML on line 23, column 155 org.xml.sax.SAXParseException: The reference to entity "F" must end with the ';' delimiter. It was due to SSOJWTDemoData content. So I removed security.token.key from this file and used only the property in security.properties. Thanks: Ayan Farooqui for report and suggestion --- framework/security/config/security.properties | 11 +++++++---- framework/security/data/SSOJWTDemoData.xml | 1 - .../src/docs/asciidoc/_include/sy-password-and-JWT.adoc | 7 +++---- .../main/java/org/apache/ofbiz/webapp/control/JWTManager.java | 8 ++++++-- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/framework/security/config/security.properties b/framework/security/config/security.properties index 1aa8c7ff9b..1e79caa265 100644 --- a/framework/security/config/security.properties +++ b/framework/security/config/security.properties @@ -141,17 +141,20 @@ security.login.externalLoginKey.enabled=true # -- Security key used to encrypt and decrypt the autogenerated password in forgot password functionality. # Read Passwords and JWT (JSON Web Tokens) usage documentation to choose the way you want to store this key -login.secret_key_string=login.secret_key_string +# The key must be 512 bits (ie 64 chars) as we use HMAC512 to create the token, cf. OFBIZ-12724 +login.secret_key_string=p2s5u8x/A?D(G+KbPeShVmYq3t6w9z$B&E)H@McQfTjWnZr4u7x!A%D*F-JaNdRg # -- Time To Live of the token send to the external server in seconds security.jwt.token.expireTime=1800 # -- Enables the internal Single Sign On feature which allows a token based login between OFBiz instances -# -- To make this work you also have to configure a secret key with security.token.key +# To make this work you also have to configure a secret key with security.token.key security.internal.sso.enabled=false -# -- The secret key for the JWT token signature. Read Passwords and JWT (JSON Web Tokens) usage documentation to choose the way you want to store this key -security.token.key=security.token.key +# -- The secret key for the JWT token signature. +# Read Passwords and JWT (JSON Web Tokens) usage documentation to choose the way you want to store this key +# The key must be 512 bits (ie 64 chars) as we use HMAC512 to create the token, cf. OFBIZ-12724 +security.token.key=%D*G-JaNdRgUkXp2s5v8y/B?E(H+MbPeShVmYq3t6w9z$C&F)J@NcRfTjWnZr4u7 # -- List of domains or IP addresses to be checked to prevent Host Header Injection, # -- no spaces after commas,no wildcard, can be extended of course... diff --git a/framework/security/data/SSOJWTDemoData.xml b/framework/security/data/SSOJWTDemoData.xml index 5e0e8823bb..27eb0f1eab 100644 --- a/framework/security/data/SSOJWTDemoData.xml +++ b/framework/security/data/SSOJWTDemoData.xml @@ -20,6 +20,5 @@ under the License. <entity-engine-xml> <SystemProperty systemResourceId="security" systemPropertyId="security.internal.sso.enabled" systemPropertyValue="false"/> - <SystemProperty systemResourceId="security" systemPropertyId="security.token.key" systemPropertyValue="security.token.key"/> <SystemProperty systemResourceId="security" systemPropertyId="SameSiteCookieAttribute" systemPropertyValue="strict"/> </entity-engine-xml> diff --git a/framework/security/src/docs/asciidoc/_include/sy-password-and-JWT.adoc b/framework/security/src/docs/asciidoc/_include/sy-password-and-JWT.adoc index 2bd20d774f..c63a708ecc 100644 --- a/framework/security/src/docs/asciidoc/_include/sy-password-and-JWT.adoc +++ b/framework/security/src/docs/asciidoc/_include/sy-password-and-JWT.adoc @@ -64,7 +64,7 @@ You might prefer to use pair of public/private keys, for now by default OFBiz us . We recommend to not use an environment variable as those can be considered weak: * http://movingfast.io/articles/environment-variables-considered-harmful * https://security.stackexchange.com/questions/49725/is-it-really-secure-to-store-api-keys-in-environment-variables - + . You may want to tie the encryption key to the logged in user. This is used by the password recreation feature. The JWT secret key is salted with a combination of the current logged in user and her/his password. This is a simple and effective safe way. . Use a https://tools.ietf.org/html/rfc7519#section-4.1.7[JTI] (JWT ID). A JTI prevents a JWT from being replayed. This https://auth0.com/blog/blacklist-json-web-token-api-keys/[auth0 blog article get deeper in that]. The same is kinda achieved with the password recreation feature. When the user log in after the new password creation, the password has already been changed. So the link (in the sent email) containing the JWT for the creation of the new password can't be reused. . Tie the encryption key to the hardware. You can refer to this https://en.wikipedia.org/wiki/Hardware_security_module[Wikipedia page] for more information. @@ -86,7 +86,7 @@ The _security.properties_ file contains five related properties: # -- Security key used to encrypt and decrypt the autogenerated password in forgot password functionality. # Read Passwords and JWT (JSON Web Tokens) usage documentation to choose the way you want to store this key - login.secret_key_string=login.secret_key_string + login.secret_key_string=p2s5u8x/A?D(G+KbPeShVmYq3t6w9z$B&E)H@McQfTjWnZr4u7x!A%D*F-JaNdRg # -- Time To Live of the token send to the external server in seconds security.jwt.token.expireTime=1800 @@ -96,14 +96,13 @@ The _security.properties_ file contains five related properties: security.internal.sso.enabled=false # -- The secret key for the JWT token signature. Read Passwords and JWT (JSON Web Tokens) usage documentation to choose the way you want to store this key - security.token.key=security.token.key + security.token.key=D*G-JaNdRgUkXp2s5v8y/B?E(H+MbPeShVmYq3t6w9z$C&F)J@NcRfTjWnZr4u7 There are also SSO related SystemProperties in __SSOJWTDemoData.xml__: [source,xml] ---- <SystemProperty systemResourceId="security" systemPropertyId="security.internal.sso.enabled" systemPropertyValue="false"/> - <SystemProperty systemResourceId="security" systemPropertyId="security.token.key" systemPropertyValue="security.token.key"/> <SystemProperty systemResourceId="security" systemPropertyId="SameSiteCookieAttribute" systemPropertyValue="strict"/> ---- diff --git a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/JWTManager.java b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/JWTManager.java index 6b9f17e2ab..758ec1e571 100644 --- a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/JWTManager.java +++ b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/JWTManager.java @@ -33,6 +33,7 @@ import org.apache.ofbiz.base.util.StringUtil; import org.apache.ofbiz.base.util.UtilDateTime; import org.apache.ofbiz.base.util.UtilHttp; import org.apache.ofbiz.base.util.UtilMisc; +import org.apache.ofbiz.base.util.UtilProperties; import org.apache.ofbiz.base.util.UtilValidate; import org.apache.ofbiz.entity.Delegator; import org.apache.ofbiz.entity.DelegatorFactory; @@ -140,7 +141,10 @@ public class JWTManager { */ public static String getJWTKey(Delegator delegator, String salt) { - String key = EntityUtilProperties.getPropertyValue("security", "security.token.key", delegator); + String key = UtilProperties.getPropertyValue("security", "security.token.key"); + if (key.length() < 64) { // The key must be 512 bits (ie 64 chars) as we use HMAC512 to create the token, cf. OFBIZ-12724 + throw new SecurityException("The JWT secret key is too short. It must be at least 512 bites."); + } if (salt != null) { return StringUtil.toHexString(salt.getBytes()) + key; } @@ -257,7 +261,7 @@ public class JWTManager { /** * Validates the provided token using a salt to recreate the key from the secret - * If the token is valid it will get the conteined claims and return them. + * If the token is valid it will get the contained claims and return them. * If token validation failed it will return an error. * @param delegator * @param jwtToken