Skip to content

Commit 354e39a

Browse files
Cherry pick branch 'genexuslabs:fix/sdt-jwt-token-size' into beta
(cherry picked from commit 27c268b) # Conflicts: # dotnet/src/dotnetframework/GxClasses/Security/WebSecurity.cs
1 parent 38a472e commit 354e39a

File tree

2 files changed

+106
-22
lines changed

2 files changed

+106
-22
lines changed

dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1661,21 +1661,29 @@ protected string GetObjectAccessWebToken(String cmpCtx)
16611661
return GetSecureSignedToken(cmpCtx, string.Empty, this.context);
16621662
}
16631663

1664-
protected string GetSecureSignedToken(String cmpCtx, object Value, IGxContext context)
1664+
protected string GetSecureSignedToken(String cmpCtx, object value, IGxContext context)
16651665
{
1666-
return GetSecureSignedToken(cmpCtx, Serialize(Value), context);
1666+
if (value is IGxJSONSerializable)
1667+
return GetSecureSignedHashedToken(cmpCtx, SecureTokenHelper.GetTokenValue(value as IGxJSONSerializable), context);
1668+
else
1669+
return GetSecureSignedToken(cmpCtx, Serialize(value), context);
16671670
}
16681671

16691672
protected string GetSecureSignedToken(String cmpCtx, GxUserType Value, IGxContext context)
16701673
{
1671-
return GetSecureSignedToken(cmpCtx, Serialize(Value), context);
1674+
return GetSecureSignedHashedToken(cmpCtx, SecureTokenHelper.GetTokenValue(Value), context);
16721675
}
16731676

16741677

16751678
protected string GetSecureSignedToken(string cmpCtx, string value, IGxContext context)
16761679
{
16771680
return WebSecurityHelper.Sign(PgmInstanceId(cmpCtx), string.Empty, value, SecureTokenHelper.SecurityMode.Sign, context);
16781681
}
1682+
private string GetSecureSignedHashedToken(string cmpCtx, TokenValue tokenValue, IGxContext context)
1683+
{
1684+
return WebSecurityHelper.Sign(PgmInstanceId(cmpCtx), string.Empty, tokenValue, SecureTokenHelper.SecurityMode.Sign, context);
1685+
}
1686+
16791687
protected bool VerifySecureSignedToken(string cmpCtx, Object value, string pic, string signedToken, IGxContext context)
16801688
{
16811689
GxUserType SDT = value as GxUserType;

dotnet/src/dotnetframework/GxClasses/Security/WebSecurity.cs

Lines changed: 95 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,23 @@ public static string StripInvalidChars(string input)
2424
{
2525
if (string.IsNullOrEmpty(input))
2626
return input;
27-
var output = new string(input.Where(c => !char.IsControl(c)).ToArray());
27+
string output = new string(input.Where(c => !char.IsControl(c)).ToArray());
2828
return output.Trim();
2929
}
3030

3131
public static string Sign(string pgmName, string issuer, string value, SecurityMode mode, IGxContext context)
3232
{
3333
return SecureTokenHelper.Sign(new WebSecureToken { ProgramName = pgmName, Issuer = issuer, Value = string.IsNullOrEmpty(value) ? string.Empty: StripInvalidChars(value) }, mode, GetSecretKey(context));
3434
}
35+
internal static string Sign(string pgmName, string issuer, TokenValue tokenValue, SecurityMode mode, IGxContext context)
36+
{
37+
return SecureTokenHelper.Sign(new WebSecureToken {
38+
ProgramName = pgmName,
39+
Issuer = issuer,
40+
ValueType = tokenValue.ValueType,
41+
Value = string.IsNullOrEmpty(tokenValue.Value) ? string.Empty : StripInvalidChars(tokenValue.Value) },
42+
mode, GetSecretKey(context));
43+
}
3544

3645
private static string GetSecretKey(IGxContext context)
3746
{
@@ -88,30 +97,63 @@ public static bool Verify(string pgmName, string issuer, string value, string jw
8897

8998
internal static bool VerifySecureSignedSDTToken(string cmpCtx, IGxCollection value, string signedToken, IGxContext context)
9099
{
91-
WebSecureToken Token = SecureTokenHelper.getWebSecureToken(signedToken, GetSecretKey(context));
92-
if (Token == null)
100+
WebSecureToken token = SecureTokenHelper.getWebSecureToken(signedToken, GetSecretKey(context));
101+
if (token == null)
93102
return false;
94-
IGxCollection PayloadObject = (IGxCollection)value.Clone();
95-
PayloadObject.FromJSonString(Token.Value);
96-
return GxUserType.IsEqual(value, PayloadObject);
103+
if (token.ValueType == SecureTokenHelper.ValueTypeHash)
104+
{
105+
return VerifyTokenHash(value.ToJSonString(), token);
106+
}
107+
else
108+
{
109+
IGxCollection PayloadObject = (IGxCollection)value.Clone();
110+
PayloadObject.FromJSonString(token.Value);
111+
return GxUserType.IsEqual(value, PayloadObject);
112+
}
97113
}
98114

99115
internal static bool VerifySecureSignedSDTToken(string cmpCtx, GxUserType value, string signedToken, IGxContext context)
100116
{
101-
WebSecureToken Token = SecureTokenHelper.getWebSecureToken(signedToken, GetSecretKey(context));
102-
if (Token == null)
117+
WebSecureToken token = SecureTokenHelper.getWebSecureToken(signedToken, GetSecretKey(context));
118+
if (token == null)
103119
return false;
104-
GxUserType PayloadObject = (GxUserType)value.Clone();
105-
PayloadObject.FromJSonString(Token.Value);
106-
return GxUserType.IsEqual(value, PayloadObject);
107-
}
120+
if (token.ValueType == ValueTypeHash)
121+
{
122+
return VerifyTokenHash(value.ToJSonString(), token);
123+
}
124+
else
125+
{
126+
GxUserType PayloadObject = (GxUserType)value.Clone();
127+
PayloadObject.FromJSonString(token.Value);
128+
return GxUserType.IsEqual(value, PayloadObject);
129+
}
108130

131+
}
109132

133+
private static bool VerifyTokenHash(string payloadJsonString, WebSecureToken token)
134+
{
135+
string hash = GetHash(payloadJsonString);
136+
if (hash != token.Value)
137+
{
138+
GXLogging.Error(_log, $"WebSecurity Token Verification error - Hash mismatch '{hash}' <> '{token.Value}'");
139+
GXLogging.Debug(_log, "Payload TokenOriginalValue: " + payloadJsonString);
140+
return false;
141+
}
142+
return true;
143+
}
110144
}
145+
internal class TokenValue
146+
{
147+
internal string Value { get; set; }
148+
internal string ValueType { get; set; }
149+
}
150+
111151
[SecuritySafeCritical]
112152
public static class SecureTokenHelper
113153
{
114-
private static readonly ILog _log = LogManager.GetLogger(typeof(GeneXus.Web.Security.SecureTokenHelper));
154+
static readonly IGXLogger _log = GXLoggerFactory.GetLogger(typeof(SecureTokenHelper).FullName);
155+
internal const string ValueTypeHash = "hash";
156+
const int MaxTokenValueLength = 1024;
115157

116158
public enum SecurityMode
117159
{
@@ -143,6 +185,7 @@ internal static WebSecureToken getWebSecureToken(string signedToken, string secr
143185
WebSecureToken outToken = new WebSecureToken();
144186
var claims = handler.ValidateToken(signedToken, validationParameters, out securityToken);
145187
outToken.Value = claims.Identities.First().Claims.First(c => c.Type == WebSecureToken.GXVALUE).Value;
188+
outToken.ValueType = claims.Identities.First().Claims.First(c => c.Type == WebSecureToken.GXVALUE_TYPE)?.Value ?? string.Empty;
146189
return outToken;
147190
}
148191
}
@@ -160,7 +203,8 @@ public static string Sign(WebSecureToken token, SecurityMode mode, string secret
160203
new Claim(WebSecureToken.GXISSUER, token.Issuer),
161204
new Claim(WebSecureToken.GXPROGRAM, token.ProgramName),
162205
new Claim(WebSecureToken.GXVALUE, token.Value),
163-
new Claim(WebSecureToken.GXEXPIRATION, token.Expiration.Subtract(new DateTime(1970, 1, 1)).TotalSeconds.ToString())
206+
new Claim(WebSecureToken.GXEXPIRATION, token.Expiration.Subtract(new DateTime(1970, 1, 1)).TotalSeconds.ToString()),
207+
new Claim(WebSecureToken.GXVALUE_TYPE, token.ValueType ?? string.Empty)
164208
}),
165209
notBefore: DateTime.UtcNow,
166210
expires: token.Expiration,
@@ -208,10 +252,38 @@ internal static bool Verify(string jwtToken, WebSecureToken outToken, string sec
208252
}
209253
}
210254
return ok;
211-
}
212-
}
255+
}
256+
internal static TokenValue GetTokenValue(IGxJSONSerializable obj)
257+
{
258+
259+
string jsonString = obj.ToJSonString();
213260

214-
[DataContract]
261+
if (jsonString.Length > MaxTokenValueLength)
262+
{
263+
string hash = GetHash(jsonString);
264+
GXLogging.Debug(_log, $"GetTokenValue: TokenValue is too long, using hash: {hash} instead of original value.");
265+
GXLogging.Debug(_log, $"Server TokenOriginalValue:" + jsonString);
266+
return new TokenValue() { Value = hash, ValueType = ValueTypeHash };
267+
}
268+
else
269+
{
270+
GXLogging.Debug(_log, $"GetTokenValue:" + jsonString);
271+
return new TokenValue() { Value = jsonString };
272+
}
273+
}
274+
internal static string GetHash(string jsonString)
275+
{
276+
using (var sha256 = System.Security.Cryptography.SHA256.Create())
277+
{
278+
byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(jsonString));
279+
jsonString = Convert.ToBase64String(hashBytes);
280+
return jsonString;
281+
}
282+
}
283+
284+
}
285+
286+
[DataContract]
215287
public abstract class SecureToken : IGxJSONSerializable
216288
{
217289
public abstract string ToJSonString();
@@ -241,8 +313,9 @@ public class WebSecureToken: SecureToken
241313
internal const string GXPROGRAM = "gx-pgm";
242314
internal const string GXVALUE = "gx-val";
243315
internal const string GXEXPIRATION = "gx-exp";
316+
internal const string GXVALUE_TYPE = "gx-val-type";
244317

245-
[DataMember(Name = GXISSUER, IsRequired = true, EmitDefaultValue = false)]
318+
[DataMember(Name = GXISSUER, IsRequired = true, EmitDefaultValue = false)]
246319
public string Issuer { get; set; }
247320

248321
[DataMember(Name = GXPROGRAM, IsRequired = true, EmitDefaultValue = false)]
@@ -254,7 +327,9 @@ public class WebSecureToken: SecureToken
254327
[DataMember(Name = GXEXPIRATION, EmitDefaultValue = false)]
255328
public DateTime Expiration { get; set; }
256329

257-
public WebSecureToken()
330+
[DataMember(Name = GXVALUE_TYPE, EmitDefaultValue = false)]
331+
public string ValueType { get; set; }
332+
public WebSecureToken()
258333
{
259334
Expiration = DateTime.Now.AddDays(15);
260335
}
@@ -267,6 +342,7 @@ public override bool FromJSonString(string s)
267342
this.Value = wt.Value;
268343
this.ProgramName = wt.ProgramName;
269344
this.Issuer = wt.Issuer;
345+
this.ValueType = wt.ValueType;
270346
return true;
271347
}
272348
catch (Exception)

0 commit comments

Comments
 (0)