gRPC security: what to check before you expose a service
gRPC security starts from one fact: gRPC is not a REST API with a different wire format. It sends Protocol Buffers over HTTP/2, services call each other directly, and a single call can be a stream of many messages. The OWASP gRPC Security Cheat Sheet says these differences bring security challenges that traditional REST checks do not cover, so the checklist below follows its sections.
Building or testing something that has to be safe? Book a meeting with whitehatstoic: it builds web and mobile apps, tests web apps and AI systems for weak spots, and gives independent advice.
gRPC security: what the framework leaves to you
The OWASP gRPC Security Cheat Sheet is written for services that talk to each other in a distributed system. Its main controls are encrypted transport, authentication and authorization on each method, validation of every message, limits on size, rate and time, careful errors, and no service discovery for strangers. Each one is a setting or a piece of code you add. None of them is on by default just because you chose gRPC.
The sheet's examples are in Go, but the ideas carry over to Java, Python and C#. Its short language notes at the end add points for each, such as setting up TLS by hand in Go.
Transport: TLS everywhere, mutual TLS between services
The sheet says production deployments need TLS, to protect against eavesdropping and man-in-the-middle attacks. It asks for TLS 1.2 or higher, strong cipher suites, and weak protocols and ciphers disabled. The sheet notes that Go's gRPC requires you to set TLS up by hand. Our TLS settings checklist has the general version.
For calls between your own services, the sheet recommends mutual TLS (mTLS). Both sides check each other's certificates, so a service that cannot present one cannot call you. It adds one habit: use certificates that live 90 days or less and rotate them automatically, which limits the damage from a stolen key. This is the same idea as the zero trust architecture post: being inside the network proves nothing.
Authentication and authorization on every method
The sheet asks for an authentication check on each protected service method, usually in an interceptor, which is gRPC's hook that runs before your handler. It shows token checks and API key checks that read the credential from metadata headers. Two rules stand out:
- Keep credentials out of method parameters. Put them in metadata headers, so they do not end up in request messages that get logged.
- Use short-lived tokens. The sheet suggests 15 to 60 minutes, with expiry and a refresh path. The JWT security post lists what to check on the token itself.
The sheet then makes a point that is easy to miss. Its example interceptor handles unary calls only, and it says outright that registering a unary interceptor does not protect streaming methods. A streaming method needs the same check in a stream server interceptor, run before the stream handler.
Authorization is a separate step. The sheet asks for a permission check per method, based on least privilege, and for every authorization failure to be logged so attacks show up. Its example denies by default when a method has no entry in the permission table. The general rules are in the authorization checks post.
Input, size limits and message counts
Protocol Buffers give type safety, not business rules. A field typed as a string still accepts any string. The sheet says to validate on the server, with allowlists for strings, and it points to a validation plugin that attaches rules such as length and range to the message definition. When input reaches a database, use prepared statements, as in the SQL injection testing post.
For size, set a limit on each message in both directions. In grpc-go the server's receive limit already defaults to 4 MiB, and the sheet shows it set explicitly. Then comes the streaming catch: a stream can carry many individually valid messages, so a per-message limit does not bound the total data or the work for the stream. The sheet asks you to limit messages per stream and the maximum session duration.
Rate limits and timeouts
The sheet's rate limiter is a good example of reading code critically. It keeps one limiter per client in a map and says plainly what is missing in a quick version:
- It covers unary calls only. Streams need admission limits in a stream interceptor, plus limits on messages inside the stream, because limiting how often streams open does not limit what arrives on one that is already open.
- The map needs a cap on entries and a cleanup job that actually runs, or the limiter itself becomes a memory problem.
- Client keys must come from the connection or from an authenticated identity, never from forwarded headers a caller can set.
- The store is local to one process, so limits across several replicas need a shared rate-limiting service.
Timeouts protect the server from calls that never finish. The sheet says to set them on both the client and the server, and its example gives each call 5 seconds on the server unless the client asked for less. See also the denial of service post for the wider idea of capping what one client can use.
Errors, logs and reflection
Return a generic message to the caller and log the detail on the server. The sheet asks for fitting status codes: UNAUTHENTICATED for authentication failures, PERMISSION_DENIED for authorization failures and INVALID_ARGUMENT for validation errors. Log authentication attempts, authorization failures and odd behavior, with a correlation ID to follow a request across services, and keep passwords and tokens out of the logs.
Then reflection. It lets a client ask a running server for its methods and message definitions, which is handy in development. The sheet says the same ability gives an attacker a map of your API, so turn it off in production. Its example registers reflection only when the environment is not production.
For service discovery, the sheet says to protect it so nobody can inject a fake endpoint or read your service list, for example by using mTLS to reach the discovery service and tight read-only roles for it. It also suggests a service mesh when you want mTLS and central policy without writing it in every service.
A pre-launch pass on one service
Take a small service with a unary GetUser method and a streaming WatchOrders method. Run these steps in order.

A check written for unary calls does not cover streams.
- Transport. Confirm the server starts with TLS credentials and that calls from other services need a client certificate.
- Authentication. Call
GetUserandWatchOrderswith no token and with an invalid one. Both must fail with UNAUTHENTICATED. The stream is the one that gets forgotten. - Authorization. Call each method as a user without the needed role and expect PERMISSION_DENIED, then check the failure was logged.
- Input. Send an empty value, a very long string and a value in the wrong format to each field.
- Limits. Send a message over the size limit, then open a stream and keep sending small valid messages. Something should stop it by message count or session length.
- Information. Trigger an error and read the reply. Ask the server to list its services with reflection; in production it should refuse.
The sheet suggests exactly these categories for your pipeline: authentication bypass, authorization boundaries, input validation and injection, rate limiting and message size enforcement. It names a command-line client called grpcurl and custom test clients as ways to run them. A run like this finds the gaps in your own checks; it does not tell you the service is safe.
Frequently asked questions
Is gRPC secure by default?
No. The sheet treats TLS, authentication, authorization, validation and limits as controls you add. Go's gRPC needs TLS set up by hand, for instance.
Does an authentication interceptor protect streaming methods?
Not if it is a unary interceptor. The sheet says a unary interceptor does not protect streaming methods, so add a stream server interceptor that checks before the stream handler runs.
Should I leave gRPC reflection on in production?
No. Reflection lets anyone who can reach the server discover its methods and message definitions. The sheet says to disable it in production and keep it for development.
What is mTLS and when do I need it?
Mutual TLS means client and server both present certificates. The sheet recommends it for service-to-service calls, with certificates that last 90 days or less and rotate automatically.
Get started
Run the pre-launch pass on one service this week and write down what each step shows, streams first. whitehatstoic tests web apps and APIs for weak spots, with a written report and a retest after fixes. Book a meeting with whitehatstoic and tell us about your service, your deadline and your biggest worry; work is scoped after a short call.
Build it right. Test it thoroughly. Keep it safe.
Visit whitehatstoic.com↗


Comments
No comments yet.
Sign in or make an account to comment.