Problem of request validation by hash signature.

51 views
Skip to first unread message

high...@gmail.com

unread,
Feb 8, 2018, 4:27:04 AM2/8/18
to grpc.io
Hi,

  I try to use call credentials to verify the request sanity:

  In the client, 
         serialize the request to string,
         sign the string,
         and send the signature as metadata.
 
  In the server, 
        serialize the request to string,
        sign the string,
        sign the request,
        and compare the signature to the one in metadata.

  It works perfect until I met a message with a map. Protobuf  serialization doesn't guarantee the order of map items. 

  How could I get original serialization string of the request in the server side using python API?

Thanks,
Haiwei

Benjamin Krämer

unread,
Feb 8, 2018, 7:34:57 AM2/8/18
to grpc.io
I haven't used the python API and don't know if and at what level intercepting is implemented. But it sound's like a better plan to do this based on the transmitted data (client: after serialization, server: bevore deserialization) instead of manually serializing it.

Carl Mastrangelo

unread,
Feb 8, 2018, 10:34:12 PM2/8/18
to grpc.io
To do this, you'll need to wrap the serialized proto.   Actually, you don't even need to put the signature in the headers.   For example:


message Wrapper {
  bytes signature = 1;
  bytes message = 2;
}


From the client:

1.   Serialize your messsage
2.   Put this in field 2
3.   Sign the message and put this in field 1.

Send the wrapper as your message type.   

From the server:

1.  Receive the wrapper proto
2.   Verify the signature on the data
3.  Deserialize the data.


You can also do this from an interceptor, which would make the process transparent from the application point of view.

That said, you're probably better off just using TLS with a client side certificate, which the server can verify and then you can trust all the data that comes over the wire. 

Haiwei Zhou

unread,
Feb 9, 2018, 1:19:40 AM2/9/18
to Carl Mastrangelo, grpc.io
Thanks for replying.

TLS had been adopted, otherwise call credentials cannot be used. 

A RPC service is designed to handle core logic. A Web service provides UI to proxy user request to the RPC service. Then a request signature should be introduced to verify the real authorization.

Using customized interceptor means a lot of boilerplate code, which I try to avoid. The best way I guess is that call credentials API provides raw request buffer.


--
You received this message because you are subscribed to a topic in the Google Groups "grpc.io" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/grpc-io/SPAv92gUypA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to grpc-io+unsubscribe@googlegroups.com.
To post to this group, send email to grp...@googlegroups.com.
Visit this group at https://groups.google.com/group/grpc-io.
To view this discussion on the web visit https://groups.google.com/d/msgid/grpc-io/e1cfcfca-9da1-4439-b4e3-d9a37d533648%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Carl Mastrangelo

unread,
Feb 9, 2018, 1:34:37 AM2/9/18
to high...@gmail.com, grp...@googlegroups.com
One thing to realize is that Protobuf is not always going to serialize the message the same way, so you'll need to use raw bytes to wrap the message anyways.   

I'm surprised that the interceptor is a lot of boiler plate; what language are you using gRPC with?

Lastly: gRPC is protobuf agnostic.   You can use it without using proto at all, so you should always be able to get at the raw message bytes, and not just through the call credentials api.


To unsubscribe from this group and all its topics, send an email to grpc-io+u...@googlegroups.com.

To post to this group, send email to grp...@googlegroups.com.
Visit this group at https://groups.google.com/group/grpc-io.

Mehrdad Afshari

unread,
Feb 9, 2018, 6:39:53 PM2/9/18
to grpc.io
While it certainly seems like the TLS client certificate is a better way to identify the peer on the other end, if for any reason you need to authenticate the payload of the RPC, I recommend directly signing the payload and sending the signature alongside it (instead of metadata). You can do this by wrapping the request_serializer/response_deserializer functions on the client side and request_deserializer/response_serializer objects on the server side. Here's probably the easiest way to do it:

On the client side, create your own object implementing the grpc.Channel interface, wrapping the gRPC channel object, and wrapping the serializers in unary_unary, unary_stream, stream_unary, stream_stream:


def _signing_serializer(identity, serializer):
def sign_and_serialize(message):
binary_message = message if serializer is None else serializer(message)
# _sign implements your signing logic and returns a bytestring with message and signature
return _sign(identity, binary_message)
return sign_and_serialize

def _verifying_deserializer(identity, deserializer):
def verify_and_deserialize(message):
# _verify_and_unwrap implements your custom verification logic and returns the original binary message and a boolean to indicate validity
binary_message, valid = _verify_and_unwrap(message)
if not valid:
return None
return binary_message if deserializer is None else deserializer(message)
return verify_and_deserialize

class PayloadSigningChannel(grpc.Channel)

def __init__(channel, identity):
self._channel = channel
self._identity = identity

def unary_unary(self,
method,
request_serializer=None,
response_deserializer=None):
return self._wrapped_channel.unary_unary(method,
request_serializer=_signing_request_serializer(self._identity, request_serializer),
response_deserializer=_verifying_deserializer(self._identity, response_deserializer))


and on the server, you can register a simple interceptor to accomplish the same thing:

class PayloadSigningServerInterceptor(grpc.ServerInterceptor):
def __init__(identity):
self._identity = identity
def intercept_service(self, continuation, handler_call_details):
handler = continuation(handler_call_details)
if handler is None:
return None
if handler.request_streaming and handler.response_streaming:
return grpc.stream_stream_rpc_method_handler(handler.stream_stream,
request_deserializer=_verifying_deserializer(self._identity, handler.request_deserializer),
response_serializer=_signing_serializer(self._identity, handler._response_serializer))
elif handler.request_streaming and not handler.response_streaming:
return grpc.stream_unary_rpc_method_handler(handler.stream_unary,
request_deserializer=_verifying_deserializer(self._identity, handler.request_deserializer),
response_serializer=_signing_serializer(self._identity, handler._response_serializer))
elif not handler.request_streaming and handler.response_streaming:
return grpc.unary_stream_rpc_method_handler(handler.unary_stream,
request_deserializer=_verifying_deserializer(self._identity, handler.request_deserializer),
response_serializer=_signing_serializer(self._identity, handler._response_serializer))
else:
return grpc.unary_unary_rpc_method_handler(handler.unary_unary,
request_deserializer=_verifying_deserializer(self._identity, handler.request_deserializer),
response_serializer=_signing_serializer(self._identity, handler._response_serializer))
Reply all
Reply to author
Forward
0 new messages