github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/mw_http_signature_validation.go (about) 1 package gateway 2 3 import ( 4 "crypto" 5 "crypto/hmac" 6 "crypto/rsa" 7 "crypto/sha1" 8 "crypto/sha256" 9 "crypto/sha512" 10 "encoding/base64" 11 "errors" 12 "hash" 13 "math" 14 "net/http" 15 "net/url" 16 "strconv" 17 "strings" 18 "sync" 19 "text/scanner" 20 "time" 21 22 "github.com/sirupsen/logrus" 23 24 "github.com/TykTechnologies/tyk/apidef" 25 "github.com/TykTechnologies/tyk/regexp" 26 "github.com/TykTechnologies/tyk/user" 27 ) 28 29 const dateHeaderSpec = "Date" 30 const altHeaderSpec = "x-aux-date" 31 32 // HTTPSignatureValidationMiddleware will check if the request has a signature, and if the request is allowed through 33 type HTTPSignatureValidationMiddleware struct { 34 BaseMiddleware 35 lowercasePattern *regexp.Regexp 36 } 37 38 func (hm *HTTPSignatureValidationMiddleware) Name() string { 39 return "HTTPSignatureValidationMiddleware" 40 } 41 42 func (k *HTTPSignatureValidationMiddleware) EnabledForSpec() bool { 43 return k.Spec.EnableSignatureChecking 44 } 45 46 func (hm *HTTPSignatureValidationMiddleware) Init() { 47 hm.lowercasePattern = regexp.MustCompile(`%[a-f0-9][a-f0-9]`) 48 } 49 50 // getAuthType overrides BaseMiddleware.getAuthType. 51 func (hm *HTTPSignatureValidationMiddleware) getAuthType() string { 52 return hmacType 53 } 54 55 func (hm *HTTPSignatureValidationMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) { 56 if ctxGetRequestStatus(r) == StatusOkAndIgnore { 57 return nil, http.StatusOK 58 } 59 60 token, _ := hm.getAuthToken(hm.getAuthType(), r) 61 if token == "" { 62 return hm.authorizationError(r) 63 } 64 logger := hm.Logger().WithField("key", obfuscateKey(token)) 65 66 // Clean it 67 token = stripSignature(token) 68 69 // Separate out the field values 70 fieldValues, err := getFieldValues(token) 71 if err != nil { 72 logger.WithError(err).Error("Field extraction failed") 73 return hm.authorizationError(r) 74 } 75 76 // Generate a signature string 77 signatureString, err := generateHMACSignatureStringFromRequest(r, fieldValues.Headers, r.URL.Path) 78 79 if err != nil { 80 logger.WithError(err).WithField("signature_string", signatureString).Error("Signature string generation failed") 81 return hm.authorizationError(r) 82 } 83 84 if len(hm.Spec.HmacAllowedAlgorithms) > 0 { 85 algorithmAllowed := false 86 for _, alg := range hm.Spec.HmacAllowedAlgorithms { 87 if alg == fieldValues.Algorthm { 88 algorithmAllowed = true 89 break 90 } 91 } 92 if !algorithmAllowed { 93 logger.WithError(err).WithField("algorithm", fieldValues.Algorthm).Error("Algorithm not supported") 94 return hm.authorizationError(r) 95 } 96 } 97 98 var secret string 99 var rsaKey *rsa.PublicKey 100 session := user.SessionState{Mutex: &sync.RWMutex{}} 101 102 if strings.HasPrefix(fieldValues.Algorthm, "rsa") { 103 var certificateId string 104 105 certificateId, session, err = hm.getRSACertificateIdAndSessionForKeyID(r, fieldValues.KeyID) 106 if err != nil { 107 logger.WithError(err).WithFields(logrus.Fields{ 108 "keyID": fieldValues.KeyID, 109 }).Error("Failed to fetch session/public key") 110 return hm.authorizationError(r) 111 } 112 113 publicKey := CertificateManager.ListRawPublicKey(certificateId) 114 if publicKey == nil { 115 log.Error("Certificate not found") 116 return errors.New("Certificate not found"), http.StatusInternalServerError 117 } 118 var ok bool 119 rsaKey, ok = publicKey.(*rsa.PublicKey) 120 if !ok { 121 log.Error("Certificate doesn't contain RSA Public key") 122 return errors.New("Certificate doesn't contain RSA Public key"), http.StatusInternalServerError 123 } 124 } else { 125 // Get a session for the Key ID 126 secret, session, err = hm.getSecretAndSessionForKeyID(r, fieldValues.KeyID) 127 if err != nil { 128 logger.WithError(err).WithFields(logrus.Fields{ 129 "keyID": fieldValues.KeyID, 130 }).Error("No HMAC secret for this key") 131 return hm.authorizationError(r) 132 } 133 } 134 var matchPass bool 135 136 if strings.HasPrefix(fieldValues.Algorthm, "rsa") { 137 matchPass, err = validateRSAEncodedSignature(signatureString, rsaKey, fieldValues.Algorthm, fieldValues.Signature) 138 if err != nil { 139 logger.WithError(err).Error("Signature validation failed.") 140 } 141 142 if !matchPass { 143 isLower, lowerList := hm.hasLowerCaseEscaped(fieldValues.Signature) 144 if isLower { 145 logger.Debug("--- Detected lower case encoding! ---") 146 upperedSignature := hm.replaceWithUpperCase(fieldValues.Signature, lowerList) 147 matchPass, err = validateRSAEncodedSignature(signatureString, rsaKey, fieldValues.Algorthm, upperedSignature) 148 if err != nil { 149 logger.WithError(err).Error("Signature validation failed.") 150 } 151 } 152 } 153 154 if !matchPass { 155 logger.WithFields(logrus.Fields{ 156 "got": fieldValues.Signature, 157 }).Error("Signature string does not match!") 158 return hm.authorizationError(r) 159 } 160 } else { 161 // Create a signed string with the secret 162 encodedSignature, err := generateHMACEncodedSignature(signatureString, secret, fieldValues.Algorthm) 163 if err != nil { 164 logger.WithFields(logrus.Fields{ 165 "error": err, 166 }).Error("Failed to validate signature") 167 return hm.authorizationError(r) 168 } 169 170 // Compare 171 matchPass = encodedSignature == fieldValues.Signature 172 173 // Check for lower case encoding (.Net issues, again) 174 if !matchPass { 175 isLower, lowerList := hm.hasLowerCaseEscaped(fieldValues.Signature) 176 if isLower { 177 logger.Debug("--- Detected lower case encoding! ---") 178 upperedSignature := hm.replaceWithUpperCase(fieldValues.Signature, lowerList) 179 if encodedSignature == upperedSignature { 180 matchPass = true 181 encodedSignature = upperedSignature 182 } 183 } 184 } 185 186 if !matchPass { 187 logger.WithFields(logrus.Fields{ 188 "expected": encodedSignature, 189 "got": fieldValues.Signature, 190 }).Error("Signature string does not match!") 191 return hm.authorizationError(r) 192 } 193 } 194 195 // Check clock skew 196 _, dateVal := getDateHeader(r) 197 if !hm.checkClockSkew(dateVal) { 198 logger.Error("Clock skew outside of acceptable bounds") 199 return hm.authorizationError(r) 200 } 201 202 // Set session state on context, we will need it later 203 switch hm.Spec.BaseIdentityProvidedBy { 204 case apidef.HMACKey, apidef.UnsetAuth: 205 ctxSetSession(r, &session, fieldValues.KeyID, false) 206 hm.setContextVars(r, fieldValues.KeyID) 207 } 208 209 // Everything seems in order let the request through 210 return nil, http.StatusOK 211 } 212 213 func stripSignature(token string) string { 214 token = strings.TrimPrefix(token, "Signature") 215 token = strings.TrimPrefix(token, "signature") 216 return strings.TrimSpace(token) 217 } 218 219 func (hm *HTTPSignatureValidationMiddleware) hasLowerCaseEscaped(signature string) (bool, []string) { 220 foundList := hm.lowercasePattern.FindAllString(signature, -1) 221 return len(foundList) > 0, foundList 222 } 223 224 func (hm *HTTPSignatureValidationMiddleware) replaceWithUpperCase(originalSignature string, lowercaseList []string) string { 225 newSignature := originalSignature 226 for _, lStr := range lowercaseList { 227 asUpper := strings.ToUpper(lStr) 228 newSignature = strings.Replace(newSignature, lStr, asUpper, -1) 229 } 230 231 return newSignature 232 } 233 234 func (hm *HTTPSignatureValidationMiddleware) setContextVars(r *http.Request, token string) { 235 if !hm.Spec.EnableContextVars { 236 return 237 } 238 // Flatten claims and add to context 239 if cnt := ctxGetData(r); cnt != nil { 240 // Key data 241 cnt["token"] = token 242 ctxSetData(r, cnt) 243 } 244 } 245 246 func (hm *HTTPSignatureValidationMiddleware) authorizationError(r *http.Request) (error, int) { 247 hm.Logger().Info("Authorization field missing or malformed") 248 token, _ := hm.getAuthToken(hm.getAuthType(), r) 249 AuthFailed(hm, r, token) 250 251 return errors.New("Authorization field missing, malformed or invalid"), http.StatusBadRequest 252 } 253 254 func (hm HTTPSignatureValidationMiddleware) checkClockSkew(dateHeaderValue string) bool { 255 // Reference layout for parsing time: "Mon Jan 2 15:04:05 MST 2006" 256 refDate := "Mon, 02 Jan 2006 15:04:05 MST" 257 // Fall back to a numeric timezone, since some environments don't provide a timezone name code 258 refDateNumeric := "Mon, 02 Jan 2006 15:04:05 -07" 259 260 tim, err := time.Parse(refDate, dateHeaderValue) 261 if err != nil { 262 tim, err = time.Parse(refDateNumeric, dateHeaderValue) 263 } 264 265 if err != nil { 266 hm.Logger().WithError(err).WithField("date_string", tim).Error("Date parsing failed") 267 return false 268 } 269 270 inSec := tim.UnixNano() 271 now := time.Now().UnixNano() 272 273 diff := now - inSec 274 275 in_ms := diff / 1000000 276 277 if hm.Spec.HmacAllowedClockSkew <= 0 { 278 return true 279 } 280 281 if math.Abs(float64(in_ms)) > hm.Spec.HmacAllowedClockSkew { 282 hm.Logger().Debug("Difference is: ", math.Abs(float64(in_ms))) 283 return false 284 } 285 286 return true 287 } 288 289 type HMACFieldValues struct { 290 KeyID string 291 Algorthm string 292 Headers []string 293 Signature string 294 } 295 296 func (hm *HTTPSignatureValidationMiddleware) getSecretAndSessionForKeyID(r *http.Request, keyId string) (string, user.SessionState, error) { 297 session, keyExists := hm.CheckSessionAndIdentityForValidKey(&keyId, r) 298 if !keyExists { 299 return "", session, errors.New("Key ID does not exist") 300 } 301 302 if session.HmacSecret == "" || !session.HMACEnabled && !session.EnableHTTPSignatureValidation { 303 hm.Logger().Info("API Requires HMAC signature, session missing HMACSecret or HMAC not enabled for key") 304 305 return "", session, errors.New("This key ID is invalid") 306 } 307 308 return session.HmacSecret, session, nil 309 } 310 311 func (hm *HTTPSignatureValidationMiddleware) getRSACertificateIdAndSessionForKeyID(r *http.Request, keyId string) (string, user.SessionState, error) { 312 session, keyExists := hm.CheckSessionAndIdentityForValidKey(&keyId, r) 313 if !keyExists { 314 return "", session, errors.New("Key ID does not exist") 315 } 316 317 if session.RSACertificateId == "" || !session.EnableHTTPSignatureValidation { 318 hm.Logger().Info("API Requires RSA signature, session missing RSA Certificate Id or RSA not enabled for key") 319 return "", session, errors.New("This key ID is invalid") 320 } 321 322 return session.RSACertificateId, session, nil 323 } 324 325 func getDateHeader(r *http.Request) (string, string) { 326 auxHeaderVal := r.Header.Get(altHeaderSpec) 327 // Prefer aux if present 328 if auxHeaderVal != "" { 329 log.WithFields(logrus.Fields{ 330 "prefix": "hmac", 331 }).Warning("Using auxiliary header for this request") 332 return strings.ToLower(altHeaderSpec), auxHeaderVal 333 } 334 335 dateHeaderVal := r.Header.Get(dateHeaderSpec) 336 if dateHeaderVal != "" { 337 log.WithFields(logrus.Fields{ 338 "prefix": "hmac", 339 }).Debug("Got date header") 340 return strings.ToLower(dateHeaderSpec), dateHeaderVal 341 } 342 343 return "", "" 344 } 345 346 // parses v which is a string of key1=value1,,key2=value2 ... format and returns 347 // a map of key:value pairs. 348 func loadKeyValues(v string) map[string]string { 349 s := &scanner.Scanner{} 350 s.Init(strings.NewReader(v)) 351 m := make(map[string]string) 352 // the state of the scanner. 353 // 0 - key 354 // 1 - value 355 var mode int 356 var key string 357 for { 358 tok := s.Scan() 359 if tok == scanner.EOF { 360 break 361 } 362 text := s.TokenText() 363 switch text { 364 case "=": 365 mode = 1 366 continue 367 case ",": 368 mode = 0 369 continue 370 default: 371 switch mode { 372 case 0: 373 key = text 374 mode = 1 375 case 1: 376 m[key] = text 377 mode = 0 378 } 379 } 380 } 381 return m 382 } 383 384 func getFieldValues(authHeader string) (*HMACFieldValues, error) { 385 set := HMACFieldValues{} 386 m := loadKeyValues(authHeader) 387 for key, value := range m { 388 if len(value) > 0 && value[0] == '"' { 389 v, err := strconv.Unquote(m[key]) 390 if err != nil { 391 return nil, err 392 } 393 value = v 394 } 395 switch strings.ToLower(key) { 396 case "keyid": 397 set.KeyID = value 398 case "algorithm": 399 set.Algorthm = value 400 case "headers": 401 set.Headers = strings.Split(value, " ") 402 case "signature": 403 set.Signature = value 404 default: 405 log.WithFields(logrus.Fields{ 406 "prefix": "hmac", 407 "field": key, 408 }).Warning("Invalid header field found") 409 return nil, errors.New("Header key is not valid, not in allowed parameter list") 410 } 411 } 412 413 // Date is the absolute minimum header set 414 if len(set.Headers) == 0 { 415 set.Headers = append(set.Headers, "date") 416 } 417 418 return &set, nil 419 } 420 421 // "Signature keyId="9876",algorithm="hmac-sha1",headers="x-test x-test-2",signature="queryEscape(base64(sig))"") 422 func generateHMACSignatureStringFromRequest(r *http.Request, headers []string, path string) (string, error) { 423 signatureString := "" 424 for i, header := range headers { 425 loweredHeader := strings.TrimSpace(strings.ToLower(header)) 426 if loweredHeader == "(request-target)" { 427 requestHeaderField := "(request-target): " + strings.ToLower(r.Method) + " " + path 428 signatureString += requestHeaderField 429 } else { 430 // exception for dates and .Net oddness 431 headerVal := r.Header.Get(loweredHeader) 432 if loweredHeader == "date" { 433 loweredHeader, headerVal = getDateHeader(r) 434 } 435 headerField := strings.TrimSpace(loweredHeader) + ": " + strings.TrimSpace(headerVal) 436 signatureString += headerField 437 } 438 439 if i != len(headers)-1 { 440 signatureString += "\n" 441 } 442 } 443 log.Debug("Generated sig string: ", signatureString) 444 return signatureString, nil 445 } 446 447 func generateHMACEncodedSignature(signatureString, secret string, algorithm string) (string, error) { 448 if secret == "" { 449 return "", errors.New("Hmac secret is empty") 450 } 451 452 key := []byte(secret) 453 454 var hashFunction func() hash.Hash 455 456 switch algorithm { 457 case "hmac-sha256": 458 hashFunction = sha256.New 459 case "hmac-sha384": 460 hashFunction = sha512.New384 461 case "hmac-sha512": 462 hashFunction = sha512.New 463 default: 464 hashFunction = sha1.New 465 } 466 467 h := hmac.New(hashFunction, key) 468 h.Write([]byte(signatureString)) 469 encodedString := base64.StdEncoding.EncodeToString(h.Sum(nil)) 470 return url.QueryEscape(encodedString), nil 471 } 472 473 func validateRSAEncodedSignature(signatureString string, publicKey *rsa.PublicKey, algorithm string, signature string) (bool, error) { 474 var hashFunction hash.Hash 475 var hashType crypto.Hash 476 477 switch algorithm { 478 case "rsa-sha256": 479 hashFunction = sha256.New() 480 hashType = crypto.SHA256 481 default: 482 hashFunction = sha256.New() 483 hashType = crypto.SHA256 484 } 485 hashFunction.Write([]byte(signatureString)) 486 hashed := hashFunction.Sum(nil) 487 488 decodedSignature, err := base64.StdEncoding.DecodeString(signature) 489 if err != nil { 490 log.Error("Error while base64 decoding signature:", err) 491 return false, err 492 } 493 err = rsa.VerifyPKCS1v15(publicKey, hashType, hashed, decodedSignature) 494 if err != nil { 495 log.Error("Signature match failed:", err) 496 return false, err 497 } 498 499 return true, nil 500 }