From ca552f53c617f857a7e986b44ea22d7b32eb0aae Mon Sep 17 00:00:00 2001 From: Alex Vanin Date: Wed, 21 Oct 2020 20:30:54 +0300 Subject: [PATCH] [#115] Check session token validity Malicious user can stole public session key and use it by sending request from it's own scope. To prevent this each session token is signed and signature private key must be corresponded with owner id in token. Therefore malicious node cannot impersonate request without private key to sign token. Signed-off-by: Alex Vanin --- pkg/services/object/acl/classifier.go | 42 +++++++++++++++++++-------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/pkg/services/object/acl/classifier.go b/pkg/services/object/acl/classifier.go index b0dc590c..b440033a 100644 --- a/pkg/services/object/acl/classifier.go +++ b/pkg/services/object/acl/classifier.go @@ -9,7 +9,9 @@ import ( "github.com/nspcc-dev/neofs-api-go/pkg/container" "github.com/nspcc-dev/neofs-api-go/pkg/netmap" "github.com/nspcc-dev/neofs-api-go/pkg/owner" + "github.com/nspcc-dev/neofs-api-go/util/signature" "github.com/nspcc-dev/neofs-api-go/v2/session" + v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature" crypto "github.com/nspcc-dev/neofs-crypto" core "github.com/nspcc-dev/neofs-node/pkg/core/netmap" "github.com/pkg/errors" @@ -90,18 +92,9 @@ func requestOwner(req metaWithToken) (*owner.ID, *ecdsa.PublicKey, error) { } // if session token is presented, use it as truth source - if req.token != nil { - body := req.token.GetBody() - if body == nil { - return nil, nil, errors.Wrap(ErrMalformedRequest, "nil at session token body") - } - - signature := req.token.GetSignature() - if signature == nil { - return nil, nil, errors.Wrap(ErrMalformedRequest, "nil at signature") - } - - return owner.NewIDFromV2(body.GetOwnerID()), crypto.UnmarshalPublicKey(signature.GetKey()), nil + if req.token.GetBody() != nil { + // verify signature of session token + return ownerFromToken(req.token) } // otherwise get original body signature @@ -197,3 +190,28 @@ func lookupKeyInContainer( return false, nil } + +func ownerFromToken(token *session.SessionToken) (*owner.ID, *ecdsa.PublicKey, error) { + // 1. First check signature of session token. + signWrapper := v2signature.StableMarshalerWrapper{SM: token.GetBody()} + if err := signature.VerifyDataWithSource(signWrapper, func() (key, sig []byte) { + tokenSignature := token.GetSignature() + return tokenSignature.GetKey(), tokenSignature.GetSign() + }); err != nil { + return nil, nil, errors.Wrap(ErrMalformedRequest, "invalid session token signature") + } + + // 2. Then check if session token owner issued the session token + tokenIssuerKey := crypto.UnmarshalPublicKey(token.GetSignature().GetKey()) + tokenIssuerWallet, err := owner.NEO3WalletFromPublicKey(tokenIssuerKey) + if err != nil { + return nil, nil, errors.Wrap(ErrMalformedRequest, "invalid token issuer key") + } + + if !bytes.Equal(token.GetBody().GetOwnerID().GetValue(), tokenIssuerWallet.Bytes()) { + // todo: in this case we can issue all owner keys from neofs.id and check once again + return nil, nil, errors.Wrap(ErrMalformedRequest, "invalid session token owner") + } + + return owner.NewIDFromV2(token.GetBody().GetOwnerID()), tokenIssuerKey, nil +}