@@ -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