Description
Gocap
Gocap proposes a simple way to introduce capability-based security to Go language. It allows a Go application to safely import or load untrusted third-party libraries with little security risk. For example, third-party data encoding or decoding libraries.
Problem
Most programming languages don't provide security protection within a single progress. In a word, every piece of code is running under the privilege of the current process. It means a third-party library can easily steal sensitive data and exfiltrate the data elsewhere. It also means a third-party library can issue direct attack against the surrounding environment, such as sending malicious requests to other processes or servers.
A few languages, such as Java and C#, do support in-process security protection, originally introduced to run Java applets. The basic model is to use a complicated ACL model based on the context, such as the call stack. However, the user experience is generally very poor and hardly anyone uses it in real production environment.
In the modern programming environments, using third-party libraries is an inevitable reality, especially with open source software and package management. Developers use convenient tools, such as maven install
, to download and use third-party libraries without much thinking.
Currently, the only safe way to run third-party libraries is to use sandbox, such as browsers, or use VMs. These approaches have very high runtime overhead, and it is typically impractical to break a single application into multiple sandboxes or multiple VMs.
If there is a simple way to provide security protection within a single process, it would make building applications with third-party libraries much safer. The capability-based security model is a well known technique that provides simple and resilient security protection in various contexts, see https://en.wikipedia.org/wiki/Capability-based_security. Many people think it is a reasonable choice for providing security at language level. This proposal (Gocap) suggests a simple way to implement capability-based security for Go language.
Proposal
This proposal introduces a new concept to Go language: stateless package, which has the following definition:
- A stateless package must not have any global variable.
- A stateless package must not access state outside the current program, such as reading arbitrary memory, read current time, issue system calls.
- A stateless package can only import other stateless packages.
The stateless package can be expressed with the following language syntax by reusing const
keyword:
package const math
Because a stateless package doesn't have any state, it can only operate on inputs and generate output, and the inputs become the security capability. Conceptually, a stateless package is equivalent to a complicated pure function. However, since Go is an imperative language, a stateless package can still destroy the inputs and cause serious damage. It requires the caller to protect the inputs using primitive values, data copies, or immutable interfaces.
The stateless package prevents access to stateful packages, such as io and network, therefore a stateless package can not read user's home directory or open a network connection. This reflects the fundamental design principle of capability-based security. Opening a file is a capability, and a stateless package should not have any capability besides function inputs.
Implementation
The implementation can be done with minimum change to Go linker. The linker needs to verify a stateless package doesn't have any state, e.g. global variable, and only depends on other stateless packages. The stateless
property can be a boolean flag on the package definition, so the linker validation can be done quickly.
Constant Support
One tricky problem with the proposal is constant support. In order to make core libraries usable by stateless packages, the core libraries themselves must be stateless, such as unicode
. This requires Go compiler and linker to support complicated constants, including structs, arrays, maps and more. This functionality is equivalent to C++ constexpr. These complicated constants will be put into read-only data segment at runtime, so they are not mutable by any runtime code after the package is loaded.
This part is by far the most complicated work for this proposal. Had Go supports complicated constants, one could implement Gocap using standard Go with very minor modification.
Benefits
If this proposal is implemented by Go, most third party libraries can be designed and implemented as stateless packages. They can be safely used by browsers and servers. One real world example is data transcoding library. Google runs many services that process user data in various formats, such as photos and videos. This requires use of many third party libraries. It is impossible to audit every such library. So the only way to run them is to use either sandbox or VM, plus infrastructure to manage them.
With stateless package, any data transcoding can be handled by a simple interface implemented by stateless packages, such as:
Transcode(Reader, Writer, Config) err
With proper implementation of Reader and Writer, such an interface has very little security risk regardless who implements the interface.
The same design principle allows safe loading of dynamic libraries, go get
third party libraries, runtime scripting, web apps, and other use cases.
Summay
Gocap proposes to use stateless package to implement capability-based security for Go language. It provides great security and usability for wide range of use cases. With proper constant support, this proposal requires very minimum change to the Go language and runtime.
Questions
Where this proposal came from originally?
This proposal was originally brought up internally at Google well before Go was released to public. It was intended to let Go run third-party code safely and offer significant usability advantage to Java. However, security was not a focus for Go language. The lack of constant support makes this proposal infeasible.
What is a concrete use case for this proposal?
YouTube uses many third party libraries for video transcoding. Because such libraries change often, it is impossible to audit the source code. If a third party library is stateless package, you can simply call it like Transcode(input []byte, settings map[string]string) []byte
, and there is n
732E
o security risk besides cpu and memory cost.
Does this proposal depend on immutable parameters?
No. This proposal does not depend on immutable parameters, but it would greatly benefit from immutable parameters.
Without immutable parameters, a stateless package may destroy input values, but it can be handled via better library design, similar to Java immutable collections. Supporting immutable parameters requires significant language and library changes. I don't think the benefit outweighs the cost.
In order to provide security, we need deep immutability, which requires every function to correct mark const
for each pointer parameter. The C++ style const
does not offer much security, and forces workarounds using mutable
keyword.
Why use untrusted code in a secure application?
We should not trust any code downloaded from internet, but we cannot live without them. Most third-party libraries can and should be stateless package, and we don't even need to worry about their security risk.
Why is security important for Go?
For large organizations, such as healthcare and military, they cannot trust any third party libraries they have to use. It is a huge burden to design a security solution to this problem. Most people simply live with the problem because there is no other choice.