the Chromium logo

The Chromium Projects

Security Tips for IPC

Note: This document is for legacy IPC. For Mojo IPC, please refer to https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/mojo.md

The Integer Semantics section has moved to Markdown too.

Chrome'sinter-process communication (IPC) layer is the communication channel supporting our multi-process architecture. Security bugs in IPC can have nasty consequences, but sticking to these tips should help you avoid most pitfalls. Questions, feedback, or suggestions to security@chromium.org.

Trust only the browser process.

Generally, privileged processes must set all policy. In Chromium, this means the browser process. "Policy" means: sizes, addresses, object names, filesystem pathnames, permissions/ACLs, specific implementations of interfaces, etc. In practice, this is how it should work:

******Unprivileged process asks for capability.******

******Privileged process applies policy to find an implementation for the
capability.******

******Unprivileged process receives it and performs constrained operations
on it.******

******Privileged process owns the capability lifecycle.******

Do not trust renderer, PPAPI, or GPU processes.

IPC messages from the renderers must be viewed with the same skepticism as one would apply to user input. These messages are untrustworthy input.

Sanitize and validate untrustworthy input.

If you're handling filenames or paths derived from untrustworthy input, make sure to avoid directory traversal attacks by sanitizing (e.g. by using a FilePath rather than a string, because FilePath implicitly checks for ".." traversal sequences and other unanticipated platform behavior) and ensuring the resulting path is within your base directory.

To construct a valid pathname, apply a function like FilePath::BaseName() to the untrustworthy pathnames; now it's a basename for sure, and not a full pathname or a sneaky trick. Then prefix the basename with a static directory name. Also apply simple, "obviously-correct" lexical checks such as an RE match for /^\w{8,16}$/.

Allowing is better than blocking.

If you know the full set of valid data, then compare against that rather than checking for occurrences of known-bad data.

Safely handle known-bad input.

When validating untrustworthy input, don't simply use CHECK. We do not want the input validation mechanism to become an easy way for a malicious renderer to kill the browser process. It's usually better to ignore the bad input, or for the privileged process to immediately kill the sender of invalid inputs.

To terminate a malfunctioning IPC sender, see BrowserChildProcessHostImpl::TerminateOnBadMessageReceived.

Use and validate specific, constrained types; let the compiler work for you.

Use the most specific data type you can to enable the type system to do part of your data validation. In other words, do you really need to use a string? Using more constrained types (e.g. primitives, enum, GURL instead of std::string that is "supposed to be a URL", etc.) lets the compiler do the type checking for you and leads to faster, often smaller, and safer code that is easier for maintainers and reviewers to understand. The most common pattern we see violating this is unnecessary use of strings, which are essentially "blob types" that are often slow (copying, lexing/parsing, used as keys in maps), and "vague" in API contracts. The caller has to know how the callee is going to parse the string, and the callee has to parse and validate it correctly (see the next section).

Some other specific tips:

More generally, don't implement your own serialization mechanism (std::vector<char>, protobufs) on top of the Chrome IPC system. Break up your structs and use the primitives provided by Chrome IPC.

Keep it simple.

Send limited capabilities (e.g. file descriptors -but not directory descriptors-), not open-ended, complex objects (e.g. pathnames). For example, to write a temporary file, the renderer should ask the browser for a file descriptor/HANDLE; the browser should create one entirely according to its own policy; and then the browser should pass the descriptor to the renderer.

Be aware of the subtleties of integer types.

First read about the scary security implications of integer arithmetic. Adhere to these best practices:

Be aware of the subtleties of integer types across C++ and Java, too.

When writing code for Chromium on Android, you will often need to marshall arrays, and their sizes and indices, across the language barrier (and possibly also across the IPC barrier). The trouble here is that the Java integer types are well-defined, but the C++ integer types are whimsical. A Java int is a signed 32-bit integer with well-defined overflow semantics, and a Java long is a signed 64-bit integer with well-defined overflow semantics. in C++, only the explicitly-sized types (e.g. int32_t) have guaranteed exact sizes, and only unsigned integers (of any size) have defined overflow semantics.

Essentially, Java integers actually are what people often (incorrectly) assume C++ integers are. Furthermore, Java Arrays are indexed with Java ints, whereas C++ arrays are indexed with size_t (often implicitly cast, of course). Note that this also implies a 2 Gig limit on the number of elements in an array that is coming from or going to Java. That Should Be Enough For Anybody, but it's good to keep in mind.

You need to make sure that every integer value survives its journey across languages intact. That generally means explicit casts with range checks; the easiest way to do this is with the base::checked_cast cast or base::saturated_cast templates in safe_conversions.h. Depending on how the integer object is going to be used, and in which direction the value is flowing, it may make sense to cast the value to jint (an ID or regular integer), jlong (a regular long integer), size_t (a size or index), or one of the other more exotic C++ integer types like off_t.

Don't leak information, don't pass information that would be risky to use.

In particular, don't leak addresses/pointers over the IPC channel, either explicitly or accidentally. (Don't defeat our ASLR!) Worse: sending pointers over the IPC is almost certainly a sign of something very wrong and could easily lead to memory corruption.

Do not pass child_id, that is the ID of child processes as viewed by the browser (which are not the same as the OS PIDs), from the browser via IPC. This construct is risky because it would be tempting to send back this ID to the browser and mistakenly use it as an authentication token, which it is not.

Avoid Unsafe (Common) Coding Patterns

What About Mojo?

The underlying principles are exactly the same whether reviewing a Mojo-based CL vs. a Chromium IPC CL. A short presentation can be found at https://docs.google.com/a/google.com/presentation/d/1uo8WAD6Hgq_gjlODb2ioUNGQs9RGlUYSQywxZOwCzcE/edit?usp=sharing