securecookie.go (19867B)
1 // Copyright 2012 The Gorilla Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package securecookie 6 7 import ( 8 "bytes" 9 "crypto/aes" 10 "crypto/cipher" 11 "crypto/hmac" 12 "crypto/rand" 13 "crypto/sha256" 14 "crypto/subtle" 15 "encoding/base64" 16 "encoding/gob" 17 "encoding/json" 18 "fmt" 19 "hash" 20 "io" 21 "strconv" 22 "strings" 23 "time" 24 ) 25 26 // Error is the interface of all errors returned by functions in this library. 27 type Error interface { 28 error 29 30 // IsUsage returns true for errors indicating the client code probably 31 // uses this library incorrectly. For example, the client may have 32 // failed to provide a valid hash key, or may have failed to configure 33 // the Serializer adequately for encoding value. 34 IsUsage() bool 35 36 // IsDecode returns true for errors indicating that a cookie could not 37 // be decoded and validated. Since cookies are usually untrusted 38 // user-provided input, errors of this type should be expected. 39 // Usually, the proper action is simply to reject the request. 40 IsDecode() bool 41 42 // IsInternal returns true for unexpected errors occurring in the 43 // securecookie implementation. 44 IsInternal() bool 45 46 // Cause, if it returns a non-nil value, indicates that this error was 47 // propagated from some underlying library. If this method returns nil, 48 // this error was raised directly by this library. 49 // 50 // Cause is provided principally for debugging/logging purposes; it is 51 // rare that application logic should perform meaningfully different 52 // logic based on Cause. See, for example, the caveats described on 53 // (MultiError).Cause(). 54 Cause() error 55 } 56 57 // errorType is a bitmask giving the error type(s) of an cookieError value. 58 type errorType int 59 60 const ( 61 usageError = errorType(1 << iota) 62 decodeError 63 internalError 64 ) 65 66 type cookieError struct { 67 typ errorType 68 msg string 69 cause error 70 } 71 72 func (e cookieError) IsUsage() bool { return (e.typ & usageError) != 0 } 73 func (e cookieError) IsDecode() bool { return (e.typ & decodeError) != 0 } 74 func (e cookieError) IsInternal() bool { return (e.typ & internalError) != 0 } 75 76 func (e cookieError) Cause() error { return e.cause } 77 78 func (e cookieError) Error() string { 79 parts := []string{"securecookie: "} 80 if e.msg == "" { 81 parts = append(parts, "error") 82 } else { 83 parts = append(parts, e.msg) 84 } 85 if c := e.Cause(); c != nil { 86 parts = append(parts, " - caused by: ", c.Error()) 87 } 88 return strings.Join(parts, "") 89 } 90 91 var ( 92 errGeneratingIV = cookieError{typ: internalError, msg: "failed to generate random iv"} 93 94 errNoCodecs = cookieError{typ: usageError, msg: "no codecs provided"} 95 errHashKeyNotSet = cookieError{typ: usageError, msg: "hash key is not set"} 96 errBlockKeyNotSet = cookieError{typ: usageError, msg: "block key is not set"} 97 errEncodedValueTooLong = cookieError{typ: usageError, msg: "the value is too long"} 98 99 errValueToDecodeTooLong = cookieError{typ: decodeError, msg: "the value is too long"} 100 errTimestampInvalid = cookieError{typ: decodeError, msg: "invalid timestamp"} 101 errTimestampTooNew = cookieError{typ: decodeError, msg: "timestamp is too new"} 102 errTimestampExpired = cookieError{typ: decodeError, msg: "expired timestamp"} 103 errDecryptionFailed = cookieError{typ: decodeError, msg: "the value could not be decrypted"} 104 errValueNotByte = cookieError{typ: decodeError, msg: "value not a []byte."} 105 errValueNotBytePtr = cookieError{typ: decodeError, msg: "value not a pointer to []byte."} 106 107 // ErrMacInvalid indicates that cookie decoding failed because the HMAC 108 // could not be extracted and verified. Direct use of this error 109 // variable is deprecated; it is public only for legacy compatibility, 110 // and may be privatized in the future, as it is rarely useful to 111 // distinguish between this error and other Error implementations. 112 ErrMacInvalid = cookieError{typ: decodeError, msg: "the value is not valid"} 113 ) 114 115 // Codec defines an interface to encode and decode cookie values. 116 type Codec interface { 117 Encode(name string, value interface{}) (string, error) 118 Decode(name, value string, dst interface{}) error 119 } 120 121 // New returns a new SecureCookie. 122 // 123 // hashKey is required, used to authenticate values using HMAC. Create it using 124 // GenerateRandomKey(). It is recommended to use a key with 32 or 64 bytes. 125 // 126 // blockKey is optional, used to encrypt values. Create it using 127 // GenerateRandomKey(). The key length must correspond to the block size 128 // of the encryption algorithm. For AES, used by default, valid lengths are 129 // 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256. 130 // The default encoder used for cookie serialization is encoding/gob. 131 // 132 // Note that keys created using GenerateRandomKey() are not automatically 133 // persisted. New keys will be created when the application is restarted, and 134 // previously issued cookies will not be able to be decoded. 135 func New(hashKey, blockKey []byte) *SecureCookie { 136 s := &SecureCookie{ 137 hashKey: hashKey, 138 blockKey: blockKey, 139 hashFunc: sha256.New, 140 maxAge: 86400 * 30, 141 maxLength: 4096, 142 sz: GobEncoder{}, 143 } 144 if hashKey == nil { 145 s.err = errHashKeyNotSet 146 } 147 if blockKey != nil { 148 s.BlockFunc(aes.NewCipher) 149 } 150 return s 151 } 152 153 // SecureCookie encodes and decodes authenticated and optionally encrypted 154 // cookie values. 155 type SecureCookie struct { 156 hashKey []byte 157 hashFunc func() hash.Hash 158 blockKey []byte 159 block cipher.Block 160 maxLength int 161 maxAge int64 162 minAge int64 163 err error 164 sz Serializer 165 // For testing purposes, the function that returns the current timestamp. 166 // If not set, it will use time.Now().UTC().Unix(). 167 timeFunc func() int64 168 } 169 170 // Serializer provides an interface for providing custom serializers for cookie 171 // values. 172 type Serializer interface { 173 Serialize(src interface{}) ([]byte, error) 174 Deserialize(src []byte, dst interface{}) error 175 } 176 177 // GobEncoder encodes cookie values using encoding/gob. This is the simplest 178 // encoder and can handle complex types via gob.Register. 179 type GobEncoder struct{} 180 181 // JSONEncoder encodes cookie values using encoding/json. Users who wish to 182 // encode complex types need to satisfy the json.Marshaller and 183 // json.Unmarshaller interfaces. 184 type JSONEncoder struct{} 185 186 // NopEncoder does not encode cookie values, and instead simply accepts a []byte 187 // (as an interface{}) and returns a []byte. This is particularly useful when 188 // you encoding an object upstream and do not wish to re-encode it. 189 type NopEncoder struct{} 190 191 // MaxLength restricts the maximum length, in bytes, for the cookie value. 192 // 193 // Default is 4096, which is the maximum value accepted by Internet Explorer. 194 func (s *SecureCookie) MaxLength(value int) *SecureCookie { 195 s.maxLength = value 196 return s 197 } 198 199 // MaxAge restricts the maximum age, in seconds, for the cookie value. 200 // 201 // Default is 86400 * 30. Set it to 0 for no restriction. 202 func (s *SecureCookie) MaxAge(value int) *SecureCookie { 203 s.maxAge = int64(value) 204 return s 205 } 206 207 // MinAge restricts the minimum age, in seconds, for the cookie value. 208 // 209 // Default is 0 (no restriction). 210 func (s *SecureCookie) MinAge(value int) *SecureCookie { 211 s.minAge = int64(value) 212 return s 213 } 214 215 // HashFunc sets the hash function used to create HMAC. 216 // 217 // Default is crypto/sha256.New. 218 func (s *SecureCookie) HashFunc(f func() hash.Hash) *SecureCookie { 219 s.hashFunc = f 220 return s 221 } 222 223 // BlockFunc sets the encryption function used to create a cipher.Block. 224 // 225 // Default is crypto/aes.New. 226 func (s *SecureCookie) BlockFunc(f func([]byte) (cipher.Block, error)) *SecureCookie { 227 if s.blockKey == nil { 228 s.err = errBlockKeyNotSet 229 } else if block, err := f(s.blockKey); err == nil { 230 s.block = block 231 } else { 232 s.err = cookieError{cause: err, typ: usageError} 233 } 234 return s 235 } 236 237 // Encoding sets the encoding/serialization method for cookies. 238 // 239 // Default is encoding/gob. To encode special structures using encoding/gob, 240 // they must be registered first using gob.Register(). 241 func (s *SecureCookie) SetSerializer(sz Serializer) *SecureCookie { 242 s.sz = sz 243 244 return s 245 } 246 247 // Encode encodes a cookie value. 248 // 249 // It serializes, optionally encrypts, signs with a message authentication code, 250 // and finally encodes the value. 251 // 252 // The name argument is the cookie name. It is stored with the encoded value. 253 // The value argument is the value to be encoded. It can be any value that can 254 // be encoded using the currently selected serializer; see SetSerializer(). 255 // 256 // It is the client's responsibility to ensure that value, when encoded using 257 // the current serialization/encryption settings on s and then base64-encoded, 258 // is shorter than the maximum permissible length. 259 func (s *SecureCookie) Encode(name string, value interface{}) (string, error) { 260 if s.err != nil { 261 return "", s.err 262 } 263 if s.hashKey == nil { 264 s.err = errHashKeyNotSet 265 return "", s.err 266 } 267 var err error 268 var b []byte 269 // 1. Serialize. 270 if b, err = s.sz.Serialize(value); err != nil { 271 return "", cookieError{cause: err, typ: usageError} 272 } 273 // 2. Encrypt (optional). 274 if s.block != nil { 275 if b, err = encrypt(s.block, b); err != nil { 276 return "", cookieError{cause: err, typ: usageError} 277 } 278 } 279 b = encode(b) 280 // 3. Create MAC for "name|date|value". Extra pipe to be used later. 281 b = []byte(fmt.Sprintf("%s|%d|%s|", name, s.timestamp(), b)) 282 mac := createMac(hmac.New(s.hashFunc, s.hashKey), b[:len(b)-1]) 283 // Append mac, remove name. 284 b = append(b, mac...)[len(name)+1:] 285 // 4. Encode to base64. 286 b = encode(b) 287 // 5. Check length. 288 if s.maxLength != 0 && len(b) > s.maxLength { 289 return "", errEncodedValueTooLong 290 } 291 // Done. 292 return string(b), nil 293 } 294 295 // Decode decodes a cookie value. 296 // 297 // It decodes, verifies a message authentication code, optionally decrypts and 298 // finally deserializes the value. 299 // 300 // The name argument is the cookie name. It must be the same name used when 301 // it was stored. The value argument is the encoded cookie value. The dst 302 // argument is where the cookie will be decoded. It must be a pointer. 303 func (s *SecureCookie) Decode(name, value string, dst interface{}) error { 304 if s.err != nil { 305 return s.err 306 } 307 if s.hashKey == nil { 308 s.err = errHashKeyNotSet 309 return s.err 310 } 311 // 1. Check length. 312 if s.maxLength != 0 && len(value) > s.maxLength { 313 return errValueToDecodeTooLong 314 } 315 // 2. Decode from base64. 316 b, err := decode([]byte(value)) 317 if err != nil { 318 return err 319 } 320 // 3. Verify MAC. Value is "date|value|mac". 321 parts := bytes.SplitN(b, []byte("|"), 3) 322 if len(parts) != 3 { 323 return ErrMacInvalid 324 } 325 h := hmac.New(s.hashFunc, s.hashKey) 326 b = append([]byte(name+"|"), b[:len(b)-len(parts[2])-1]...) 327 if err = verifyMac(h, b, parts[2]); err != nil { 328 return err 329 } 330 // 4. Verify date ranges. 331 var t1 int64 332 if t1, err = strconv.ParseInt(string(parts[0]), 10, 64); err != nil { 333 return errTimestampInvalid 334 } 335 t2 := s.timestamp() 336 if s.minAge != 0 && t1 > t2-s.minAge { 337 return errTimestampTooNew 338 } 339 if s.maxAge != 0 && t1 < t2-s.maxAge { 340 return errTimestampExpired 341 } 342 // 5. Decrypt (optional). 343 b, err = decode(parts[1]) 344 if err != nil { 345 return err 346 } 347 if s.block != nil { 348 if b, err = decrypt(s.block, b); err != nil { 349 return err 350 } 351 } 352 // 6. Deserialize. 353 if err = s.sz.Deserialize(b, dst); err != nil { 354 return cookieError{cause: err, typ: decodeError} 355 } 356 // Done. 357 return nil 358 } 359 360 // timestamp returns the current timestamp, in seconds. 361 // 362 // For testing purposes, the function that generates the timestamp can be 363 // overridden. If not set, it will return time.Now().UTC().Unix(). 364 func (s *SecureCookie) timestamp() int64 { 365 if s.timeFunc == nil { 366 return time.Now().UTC().Unix() 367 } 368 return s.timeFunc() 369 } 370 371 // Authentication ------------------------------------------------------------- 372 373 // createMac creates a message authentication code (MAC). 374 func createMac(h hash.Hash, value []byte) []byte { 375 h.Write(value) 376 return h.Sum(nil) 377 } 378 379 // verifyMac verifies that a message authentication code (MAC) is valid. 380 func verifyMac(h hash.Hash, value []byte, mac []byte) error { 381 mac2 := createMac(h, value) 382 // Check that both MACs are of equal length, as subtle.ConstantTimeCompare 383 // does not do this prior to Go 1.4. 384 if len(mac) == len(mac2) && subtle.ConstantTimeCompare(mac, mac2) == 1 { 385 return nil 386 } 387 return ErrMacInvalid 388 } 389 390 // Encryption ----------------------------------------------------------------- 391 392 // encrypt encrypts a value using the given block in counter mode. 393 // 394 // A random initialization vector (http://goo.gl/zF67k) with the length of the 395 // block size is prepended to the resulting ciphertext. 396 func encrypt(block cipher.Block, value []byte) ([]byte, error) { 397 iv := GenerateRandomKey(block.BlockSize()) 398 if iv == nil { 399 return nil, errGeneratingIV 400 } 401 // Encrypt it. 402 stream := cipher.NewCTR(block, iv) 403 stream.XORKeyStream(value, value) 404 // Return iv + ciphertext. 405 return append(iv, value...), nil 406 } 407 408 // decrypt decrypts a value using the given block in counter mode. 409 // 410 // The value to be decrypted must be prepended by a initialization vector 411 // (http://goo.gl/zF67k) with the length of the block size. 412 func decrypt(block cipher.Block, value []byte) ([]byte, error) { 413 size := block.BlockSize() 414 if len(value) > size { 415 // Extract iv. 416 iv := value[:size] 417 // Extract ciphertext. 418 value = value[size:] 419 // Decrypt it. 420 stream := cipher.NewCTR(block, iv) 421 stream.XORKeyStream(value, value) 422 return value, nil 423 } 424 return nil, errDecryptionFailed 425 } 426 427 // Serialization -------------------------------------------------------------- 428 429 // Serialize encodes a value using gob. 430 func (e GobEncoder) Serialize(src interface{}) ([]byte, error) { 431 buf := new(bytes.Buffer) 432 enc := gob.NewEncoder(buf) 433 if err := enc.Encode(src); err != nil { 434 return nil, cookieError{cause: err, typ: usageError} 435 } 436 return buf.Bytes(), nil 437 } 438 439 // Deserialize decodes a value using gob. 440 func (e GobEncoder) Deserialize(src []byte, dst interface{}) error { 441 dec := gob.NewDecoder(bytes.NewBuffer(src)) 442 if err := dec.Decode(dst); err != nil { 443 return cookieError{cause: err, typ: decodeError} 444 } 445 return nil 446 } 447 448 // Serialize encodes a value using encoding/json. 449 func (e JSONEncoder) Serialize(src interface{}) ([]byte, error) { 450 buf := new(bytes.Buffer) 451 enc := json.NewEncoder(buf) 452 if err := enc.Encode(src); err != nil { 453 return nil, cookieError{cause: err, typ: usageError} 454 } 455 return buf.Bytes(), nil 456 } 457 458 // Deserialize decodes a value using encoding/json. 459 func (e JSONEncoder) Deserialize(src []byte, dst interface{}) error { 460 dec := json.NewDecoder(bytes.NewReader(src)) 461 if err := dec.Decode(dst); err != nil { 462 return cookieError{cause: err, typ: decodeError} 463 } 464 return nil 465 } 466 467 // Serialize passes a []byte through as-is. 468 func (e NopEncoder) Serialize(src interface{}) ([]byte, error) { 469 if b, ok := src.([]byte); ok { 470 return b, nil 471 } 472 473 return nil, errValueNotByte 474 } 475 476 // Deserialize passes a []byte through as-is. 477 func (e NopEncoder) Deserialize(src []byte, dst interface{}) error { 478 if dat, ok := dst.(*[]byte); ok { 479 *dat = src 480 return nil 481 } 482 return errValueNotBytePtr 483 } 484 485 // Encoding ------------------------------------------------------------------- 486 487 // encode encodes a value using base64. 488 func encode(value []byte) []byte { 489 encoded := make([]byte, base64.URLEncoding.EncodedLen(len(value))) 490 base64.URLEncoding.Encode(encoded, value) 491 return encoded 492 } 493 494 // decode decodes a cookie using base64. 495 func decode(value []byte) ([]byte, error) { 496 decoded := make([]byte, base64.URLEncoding.DecodedLen(len(value))) 497 b, err := base64.URLEncoding.Decode(decoded, value) 498 if err != nil { 499 return nil, cookieError{cause: err, typ: decodeError, msg: "base64 decode failed"} 500 } 501 return decoded[:b], nil 502 } 503 504 // Helpers -------------------------------------------------------------------- 505 506 // GenerateRandomKey creates a random key with the given length in bytes. 507 // On failure, returns nil. 508 // 509 // Callers should explicitly check for the possibility of a nil return, treat 510 // it as a failure of the system random number generator, and not continue. 511 func GenerateRandomKey(length int) []byte { 512 k := make([]byte, length) 513 if _, err := io.ReadFull(rand.Reader, k); err != nil { 514 return nil 515 } 516 return k 517 } 518 519 // CodecsFromPairs returns a slice of SecureCookie instances. 520 // 521 // It is a convenience function to create a list of codecs for key rotation. Note 522 // that the generated Codecs will have the default options applied: callers 523 // should iterate over each Codec and type-assert the underlying *SecureCookie to 524 // change these. 525 // 526 // Example: 527 // 528 // codecs := securecookie.CodecsFromPairs( 529 // []byte("new-hash-key"), 530 // []byte("new-block-key"), 531 // []byte("old-hash-key"), 532 // []byte("old-block-key"), 533 // ) 534 // 535 // // Modify each instance. 536 // for _, s := range codecs { 537 // if cookie, ok := s.(*securecookie.SecureCookie); ok { 538 // cookie.MaxAge(86400 * 7) 539 // cookie.SetSerializer(securecookie.JSONEncoder{}) 540 // cookie.HashFunc(sha512.New512_256) 541 // } 542 // } 543 // 544 func CodecsFromPairs(keyPairs ...[]byte) []Codec { 545 codecs := make([]Codec, len(keyPairs)/2+len(keyPairs)%2) 546 for i := 0; i < len(keyPairs); i += 2 { 547 var blockKey []byte 548 if i+1 < len(keyPairs) { 549 blockKey = keyPairs[i+1] 550 } 551 codecs[i/2] = New(keyPairs[i], blockKey) 552 } 553 return codecs 554 } 555 556 // EncodeMulti encodes a cookie value using a group of codecs. 557 // 558 // The codecs are tried in order. Multiple codecs are accepted to allow 559 // key rotation. 560 // 561 // On error, may return a MultiError. 562 func EncodeMulti(name string, value interface{}, codecs ...Codec) (string, error) { 563 if len(codecs) == 0 { 564 return "", errNoCodecs 565 } 566 567 var errors MultiError 568 for _, codec := range codecs { 569 encoded, err := codec.Encode(name, value) 570 if err == nil { 571 return encoded, nil 572 } 573 errors = append(errors, err) 574 } 575 return "", errors 576 } 577 578 // DecodeMulti decodes a cookie value using a group of codecs. 579 // 580 // The codecs are tried in order. Multiple codecs are accepted to allow 581 // key rotation. 582 // 583 // On error, may return a MultiError. 584 func DecodeMulti(name string, value string, dst interface{}, codecs ...Codec) error { 585 if len(codecs) == 0 { 586 return errNoCodecs 587 } 588 589 var errors MultiError 590 for _, codec := range codecs { 591 err := codec.Decode(name, value, dst) 592 if err == nil { 593 return nil 594 } 595 errors = append(errors, err) 596 } 597 return errors 598 } 599 600 // MultiError groups multiple errors. 601 type MultiError []error 602 603 func (m MultiError) IsUsage() bool { return m.any(func(e Error) bool { return e.IsUsage() }) } 604 func (m MultiError) IsDecode() bool { return m.any(func(e Error) bool { return e.IsDecode() }) } 605 func (m MultiError) IsInternal() bool { return m.any(func(e Error) bool { return e.IsInternal() }) } 606 607 // Cause returns nil for MultiError; there is no unique underlying cause in the 608 // general case. 609 // 610 // Note: we could conceivably return a non-nil Cause only when there is exactly 611 // one child error with a Cause. However, it would be brittle for client code 612 // to rely on the arity of causes inside a MultiError, so we have opted not to 613 // provide this functionality. Clients which really wish to access the Causes 614 // of the underlying errors are free to iterate through the errors themselves. 615 func (m MultiError) Cause() error { return nil } 616 617 func (m MultiError) Error() string { 618 s, n := "", 0 619 for _, e := range m { 620 if e != nil { 621 if n == 0 { 622 s = e.Error() 623 } 624 n++ 625 } 626 } 627 switch n { 628 case 0: 629 return "(0 errors)" 630 case 1: 631 return s 632 case 2: 633 return s + " (and 1 other error)" 634 } 635 return fmt.Sprintf("%s (and %d other errors)", s, n-1) 636 } 637 638 // any returns true if any element of m is an Error for which pred returns true. 639 func (m MultiError) any(pred func(Error) bool) bool { 640 for _, e := range m { 641 if ourErr, ok := e.(Error); ok && pred(ourErr) { 642 return true 643 } 644 } 645 return false 646 }