Adds more unit testing
Some basic security check for tempering of JWS.
This commit is contained in:
@@ -80,9 +80,9 @@ func isBase64URLEncoding(b byte) bool {
|
||||
}
|
||||
|
||||
// DecodeJWS decode an object encoded with the JWS serialized fromat
|
||||
// as specified by RFX 7515. to avoid an attack where the unprotected
|
||||
// JOSE header would contain a tempered signing algorithm, the Signer
|
||||
// should also be specified.
|
||||
// as specified by RFC 7515. to avoid an attack where the unprotected
|
||||
// JOSE header would contain a modified alg field, the Signer should
|
||||
// also be specified.
|
||||
func DecodeJWS(data []byte, v interface{}, s Signer) error {
|
||||
var headerLength, payloadLength int
|
||||
for i, c := range data {
|
||||
|
||||
134
jwt/jws_test.go
134
jwt/jws_test.go
@@ -2,6 +2,11 @@ package jwt
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
@@ -75,3 +80,132 @@ func (s *JWSSuite) TestCanEncodeUnprotected(c *C) {
|
||||
c.Check(redecoded, DeepEquals, decoded)
|
||||
|
||||
}
|
||||
|
||||
func (s *JWSSuite) TestAttackerTriesToForgeAToken(c *C) {
|
||||
type ident struct {
|
||||
Iat int64
|
||||
Adm bool
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Server side
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
//we create a token and sign it
|
||||
|
||||
validToken, err := EncodeJWS(&JOSE{}, ident{Iat: time.Now().Unix(), Adm: false}, s.signers[0])
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Untrusted side
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// now an attacker takes our valid token, unmarshall it, and create a new one with no signature
|
||||
t := ident{}
|
||||
|
||||
tokenRx := regexp.MustCompile(`\A([a-zA-Z0-9_\-]+)\.([a-zA-Z0-9_\-]+).([a-zA-Z0-9_\-]+)\z`)
|
||||
|
||||
matches := tokenRx.FindSubmatch(validToken)
|
||||
c.Assert(matches, NotNil)
|
||||
|
||||
// extract JOSE
|
||||
j, err := DecodeJOSE(matches[1])
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// extract payload
|
||||
payload := make([]byte, Base64DecodedLenFromStripped(len(matches[2])))
|
||||
Base64Decode(payload, matches[2])
|
||||
err = json.Unmarshal(payload, &t)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
//we forge an admin token
|
||||
t.Adm = true
|
||||
|
||||
//we un-sign the token, by using the none algorithm
|
||||
forgedToken, err := EncodeJWS(j, t, nil)
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Server side
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
//we verify the forgedToken, it should fail
|
||||
received := ident{Adm: false}
|
||||
// please note that we speficy ecplitely the signer, and therefore
|
||||
// the algorithm to use.
|
||||
err = DecodeJWS(forgedToken, &received, s.signers[0])
|
||||
|
||||
//we got a wrong signature for our chosen algorithm
|
||||
c.Assert(err, ErrorMatches, fmt.Sprintf(`jws: .* %s .*`, s.signers[0].Algorithm()))
|
||||
// of course, our token remain Adm:false
|
||||
c.Assert(received.Adm, Equals, false)
|
||||
|
||||
}
|
||||
|
||||
func (s *JWSSuite) TestAttackerTriesToForgeAHMACToken(c *C) {
|
||||
type ident struct {
|
||||
Iat int64
|
||||
Adm bool
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Server side
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
//we create a token and sign it using RSA256
|
||||
serverSigner := NewRSA256Signer(s.rsaKey)
|
||||
validToken, err := EncodeJWS(&JOSE{}, ident{Iat: time.Now().Unix(), Adm: false}, serverSigner)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Untrusted side
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// now an attacker takes our valid token, unmarshall it, and create a new one with no signature
|
||||
t := ident{}
|
||||
|
||||
tokenRx := regexp.MustCompile(`\A([a-zA-Z0-9_\-]+)\.([a-zA-Z0-9_\-]+).([a-zA-Z0-9_\-]+)\z`)
|
||||
|
||||
matches := tokenRx.FindSubmatch(validToken)
|
||||
c.Assert(matches, NotNil)
|
||||
|
||||
// extract JOSE
|
||||
j, err := DecodeJOSE(matches[1])
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// extract payload
|
||||
payload := make([]byte, Base64DecodedLenFromStripped(len(matches[2])))
|
||||
Base64Decode(payload, matches[2])
|
||||
err = json.Unmarshal(payload, &t)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
//we forge an admin token
|
||||
t.Adm = true
|
||||
|
||||
//now here is the trick, we sign an HMAC token using the serverPublicKey
|
||||
|
||||
publicKeyAsByte, err := x509.MarshalPKIXPublicKey(s.rsaKey.Public())
|
||||
c.Assert(err, IsNil, Commentf("While marshalling key"))
|
||||
//we create a new signing algorithm expecting the server will
|
||||
//still use the assymetric key on the HMAC.
|
||||
attackerSigner := NewHMAC256Signer(publicKeyAsByte)
|
||||
//we un-sign the token, by using another symmetric algorithm, and
|
||||
//the publickKey
|
||||
forgedToken, err := EncodeJWS(j, t, attackerSigner)
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Server side
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
//we verify the forgedToken, it should fail
|
||||
received := ident{Adm: false}
|
||||
// please note that we speficy ecplitely the signer, and therefore
|
||||
// the algorithm to use.
|
||||
err = DecodeJWS(forgedToken, &received, serverSigner)
|
||||
|
||||
//we got a wrong signature for our chosen algorithm, we still are
|
||||
//using the rsa algorithm
|
||||
c.Assert(err, ErrorMatches, `crypto/rsa: verification error`)
|
||||
// of course, our token remain Adm:false
|
||||
c.Assert(received.Adm, Equals, false)
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user