jwt settings workout on review feedback

This commit is contained in:
Sergey Matvienko 2022-11-10 18:00:54 +01:00
parent 1f1c8fb013
commit b95f1c95e0
3 changed files with 20 additions and 6 deletions

View File

@ -164,7 +164,11 @@ public class JwtSettingsServiceDefault implements JwtSettingsService {
if (isAllowedDefaultJwtSigningKey()) { if (isAllowedDefaultJwtSigningKey()) {
log.warn("Default JWT signing key is allowed. This is a security issue. Please, consider to set a strong key in admin settings"); log.warn("Default JWT signing key is allowed. This is a security issue. Please, consider to set a strong key in admin settings");
} else { } else {
String message = "Please, set a unique signing key with env variable JWT_TOKEN_SIGNING_KEY. Key is a Base64 encoded phrase. This will require to generate new tokens for all users and API that uses JWT tokens. To allow insecure JWS use TB_ALLOW_DEFAULT_JWT_SIGNING_KEY=true"; String message = "UPGRADE ERROR. YOUR ACTION REQUIRED. Please, set a unique signing key with env variable JWT_TOKEN_SIGNING_KEY. " +
"The key should be a Base64 encoded string representing at least 256 bits of data. " +
"This will require to generate new tokens for all UI users and scripts that use JWT. " +
"To keep the default non-secure JWT signing key set TB_ALLOW_DEFAULT_JWT_SIGNING_KEY=true and restart the upgrade. " +
"You may change the JWT signing key later in the Admin Settings UI.";
log.error(message); log.error(message);
throw new ValidationException(message); throw new ValidationException(message);
} }

View File

@ -16,6 +16,7 @@
package org.thingsboard.server.config.jwt; package org.thingsboard.server.config.jwt;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Arrays;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -23,6 +24,7 @@ import org.thingsboard.server.dao.exception.DataValidationException;
import java.util.Base64; import java.util.Base64;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.TimeUnit;
@Component @Component
@AllArgsConstructor @AllArgsConstructor
@ -32,11 +34,14 @@ public class JwtSettingsValidator {
if (StringUtils.isEmpty(jwtSettings.getTokenIssuer())) { if (StringUtils.isEmpty(jwtSettings.getTokenIssuer())) {
throw new DataValidationException("JWT token issuer should be specified!"); throw new DataValidationException("JWT token issuer should be specified!");
} }
if (Optional.ofNullable(jwtSettings.getRefreshTokenExpTime()).orElse(0) <= 0) { if (Optional.ofNullable(jwtSettings.getRefreshTokenExpTime()).orElse(0) <= TimeUnit.MINUTES.toSeconds(15)) {
throw new DataValidationException("JWT refresh token expiration time should be specified!"); throw new DataValidationException("JWT refresh token expiration time should be at least 15 minutes!");
} }
if (Optional.ofNullable(jwtSettings.getTokenExpirationTime()).orElse(0) <= 0) { if (Optional.ofNullable(jwtSettings.getTokenExpirationTime()).orElse(0) <= TimeUnit.MINUTES.toSeconds(1)) {
throw new DataValidationException("JWT token expiration time should be specified!"); throw new DataValidationException("JWT token expiration time should be at least 1 minute!");
}
if (jwtSettings.getTokenExpirationTime() >= jwtSettings.getRefreshTokenExpTime()) {
throw new DataValidationException("JWT token expiration time should greater than JWT refresh token expiration time!");
} }
if (StringUtils.isEmpty(jwtSettings.getTokenSigningKey())) { if (StringUtils.isEmpty(jwtSettings.getTokenSigningKey())) {
throw new DataValidationException("JWT token signing key should be specified!"); throw new DataValidationException("JWT token signing key should be specified!");
@ -52,7 +57,11 @@ public class JwtSettingsValidator {
if (Arrays.isNullOrEmpty(decodedKey)) { if (Arrays.isNullOrEmpty(decodedKey)) {
throw new DataValidationException("JWT token signing key should be non-empty after Base64 decoding!"); throw new DataValidationException("JWT token signing key should be non-empty after Base64 decoding!");
} }
Arrays.fill(decodedKey, (byte) 0); if (decodedKey.length * Byte.SIZE < 256) {
throw new DataValidationException("JWT token signing key should be a Base64 encoded string representing at least 256 bits of data!");
}
System.arraycopy(decodedKey, 0, RandomUtils.nextBytes(decodedKey.length), 0, decodedKey.length); //secure memory
} }
} }

View File

@ -178,6 +178,7 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
} else if (EntityType.TENANT.equals(componentLifecycleMsg.getEntityId().getEntityType())) { } else if (EntityType.TENANT.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
if (TenantId.SYS_TENANT_ID.equals(componentLifecycleMsg.getTenantId())) { if (TenantId.SYS_TENANT_ID.equals(componentLifecycleMsg.getTenantId())) {
jwtSettingsService.ifPresent(JwtSettingsService::reloadJwtSettings); jwtSettingsService.ifPresent(JwtSettingsService::reloadJwtSettings);
return;
} else { } else {
tenantProfileCache.evict(componentLifecycleMsg.getTenantId()); tenantProfileCache.evict(componentLifecycleMsg.getTenantId());
partitionService.removeTenant(componentLifecycleMsg.getTenantId()); partitionService.removeTenant(componentLifecycleMsg.getTenantId());