-
Notifications
You must be signed in to change notification settings - Fork 18k
proposal: Go 2: capability based security via stateless packages #23267
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I don’t think this proposal will be successful for two reasons
I think this proposal should be rejected and efforts placed instead into integrating one form of const or immutability into Go 2. |
@davecheney I like to understand your comment better. Can you explain how "the usage of the unsafe package" allows mutating anything, like https://golang.org/src/unsafe/unsafe.go? I was involved with the Java unsafe package many years ago. I am quite confident on designing an unsafe package that is secure and usable by stateless packages. Whether to mark Functional languages, such as Haskell, offer no real security in practice. Rust supports const, but the core libraries end up having massive use of One way to solve this type of design decision is to choose several key use cases, and we can compare the proposed developer experience and runtime architecture side by side. Let's not assume that immutability is the real and better solution than stateless package without sufficient data and comparison. One reason that functional language is not widely adopted is the practicality. @rsc probably still remembers my original proposal from years ago. I am happy to hear his thoughts. |
@wora I don't understand, the purpose of Often, a module is stateless, but it has I could also use stenography to communicate to "pure" modules. Something like this: type MyStruct struct {
// all fields are private
}
func PureFunc(s *MyStruct, output io.Writer) {
if fmt.Sprintf("%p", s) % 10000 == 0 {
output.Write([]byte("<script .../>"))
}
} I could do the same for your func Transcode(r Reader, w Writer, c Config) err {
b := make([]byte, 2048)
for {
n, _ := r.Read(b)
if n == 1337 {
w.Write([]byte("<script .../>"))
}
...
}
} So, it's still possible to have lurking exploits with stateless functions. It appears to me that this "stateless" module feature doesn't really add any security, but instead it misleads the programmer to think that it is more secure/deterministic than it actually is. With the alternative const-mutable type proposal, we can remove module-level vars entirely, but modules could still have mutable state. Disallowing module-level vars which is currently being abused seems more pressing. |
I don't understand why this is a "security" proposal. |
Analyzing a proposal for enhancing security requires a threat model. We have to understand what attacks we are protecting against. I think that needs to be clarified here. For example, if the threat model is "an untrusted third party library might extract security keys and send them to an attacker," then clearly untrusted libraries may not be permitted to import "unsafe". A package that imports "unsafe" can examine anything in memory. It's not clear to me why preventing a third party library from having mutable global variables provides any additional security. For example, even without mutable global variables, a package could open a network connection to an external database and use it as a mutable store. |
@ianlancetaylor The threat model is global access, such as reading a file in user's home directory. For a stateless package, the worst thing it can do is damaging the inputs, but it cannot read or write anything else. I must misunderstood the design of current Go The io and network packages are stateful, therefore a stateless package cannot possibly import them. I will update my proposal to mention it explicitly. A network connection has a file descriptor, which is a global variable by nature. |
I have removed reference to |
Thanks. The threat model still seems a bit underspecified to me. You say that the os and net packages have state, but they don't have any global variables (at least, not any important ones) so I still don't clearly understand the relationship between mutable global variables and the property you are after. If you don't want to permit a library to do something external to the program, you can ban importing the unsafe and syscall packages, and any packages that import those packages. That will leave you with only pure computation. In that scenario, what is the threat of mutable global variables? And what in particular is the threat of importing other, trusted, packages that themselves use mutable global variables? It's not clear to me that permitting only pure computation packages is very useful. How many third party libraries that do only pure computation are actually usable? I understand your example of transcoding libraries, but I have to imagine that that is a pretty small subset of the libraries out there. Are there are other examples? |
The concept here is **global state**, not global variable. Even they don't
have any global variable, os and net and unsafe are not stateless packages,
so they can not be used by stateless package directly.
We don't prevent a library from doing something external to the program. We
just require the library to get the capability from its caller. For
example, the following code would be fine:
```
Transcode(nio.Reader) []byte
```
A stateless package can perform io read as long as the reader is passed to
the library (the capability). The stateless package itself doesn't have the
capability to open a file. This is one of the reasons that this proposal
does not depend on immutable inputs.
Only pure computation package is essentially functional programming. It
won't be useful for Go and I am not proposing it. Once unsafe (Rust) or
Monad (Haskell) is used to workaround the limitations, there is no security
anymore. This proposal allows the stateless package to do anything as long
as the caller gives the capability. I believe the proposal offers good
balance between security needs and Go language design.
NOTE: if the proposal was adopted at the very beginning of Go language
design, the entire Go language and all libraries would be stateless except
the main function, e.g. Go language itself can be a capability-based
system. The only capability is given to the main function like this:
```
func main(sys System)
```
Where the `sys` represents unsafe and syscall packages with the current
process capability. While it was possible in theory, I am not sure how
practical to design the libraries around it. Anyway, I am not proposing to
make entire Go language as stateless or capability based here.
|
Read-only types proposal: #22876 Ian’s suggestion of inspecting the third-party libraries for improper imports (like a transcoding library importing net/http) as part of the build process seems like the right solution for this problem to me. Dave shows how to do this with go list: https://dave.cheney.net/2014/09/14/go-list-your-swiss-army-knife |
@wora Thanks for the explanation. I now think that I don't understand what you are proposing. What is the exact definition of a "stateless package," other than "it may only import stateless packages?" You mention the unicode package as though it is a problem; in what way is the current unicode package not already a "stateless package?" |
Another question: what do global variables have to do with this? If we permit a "stateless package" to have global variables, what threat are we permitting that is not otherwise available? |
Auditing third-party libraries is a manual work. It cannot be done at
scale, and it is limited by human errors. This proposal is implemented by
the linker (e.g. build time), but requires little auditing.
The read-only types is a performance improvement to this proposal, but it
is not required. I will comment on the other issue if I have anything to
add.
The definition of stateless package is:
* It can not have any global variable.
* It can not access any global state outside the process, such as
current time, file descriptor, syscalls.
* It can only important stateless packages.
The reason to prevent global variable is to prevent one function invocation
stores some value into a global variable, and another function invocation
read the value back. If a global variable exists, it can lead to data
leakage in hidden way. If we whitelist a stateless package to have global
variable, e.g. manual trust, we are permitting information leakage across
different calls.
For comparison, Java uses class loader as security boundary. If the same
class file is loaded by 2 class loaders, there are two copies of the class
with two sets of global variables (aka class variables). Essentially, we
move the problem from language to runtime.
Go `unicode` package is well written by the Go team. We can trust it
doesn't have mutable global state after init. If we make unicode package
stateless, we don't need to worry about the trust. The linker would force
it by running the init() at link time and build static data segment.
Go linker can whitelist unicode package as stateless if we are confident
with the implementation. But we cannot mark syscall as stateless even it
doesn't have any global variable.
…On Thu, Dec 28, 2017 at 11:07 AM, Ian Lance Taylor ***@***.*** > wrote:
@wora <https://github.com/wora> Thanks for the explanation. I now think
that I don't understand what you are proposing. What is the exact
definition of a "stateless package," other than "it may only import
stateless packages?"
You mention the unicode package as though it is a problem; in what way is
the current unicode package not already a "stateless package?"
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#23267 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AGq7pTcv3WqknxNZoyhpO_aRJgUYN63zks5tE-bfgaJpZM4ROCsM>
.
|
Since a goal of this proposal is automatic determination that a package is safe, can you make that statement precise? Or, it's possible that that statement is unnecessary, and the constraints are satisfied by
It would be possible to audit a set of existing Go packages to see how many could exist within those constraints. If the percentage is small, it seems unlikely that this proposal would be useful in practice. |
I updated the original proposal to make the spec more precise, but it likely needs more revision to become a real spec. My intention is to force library developers to declare whether a package is stateless and Go linker can easily validate the declaration. In theory, we can automatically mark a package is safe or stateless. The problem is a package can become unsafe suddenly by introducing a global variable or adding a log statement. That would trigger cascaded build failures all over the places. I don't expect many existing Go packages are stateless, but new packages can be easily developed as stateless packages, which can be used in secure environments. @ianlancetaylor Your question does bring up a critical point. The existing Go library design mixed stateful and stateless elements together. For example, io.Reader is a harmless interface, since it is defined inside We can change the spec to allow using type definitions in unsafe packages, but not allowing any calls to unsafe packages. This essentially move the Overall, I think my original proposal would offer an easy-to-use security model for Go, especially for new libraries. However, refactoring existing libraries may still be costly. PS: we could rename the concept |
I hardly heard of a video transcoding library written in Go. Besides, video transcoding often needs something like DirectX, CUDA and OpenCL. Maybe |
Why do you think math is not unsafe? All codes run as machine instructions, either produced by compilers or by humans. Compilers are also written by humans. By the end of the day, you have to trust some humans. This proposal implicitly trusts Go compiler, linker and core libraries. If we don't trust Go compiler, linker, and core libraries, the standard solution is sandbox or VM, which are widely adopted. It is a fine choice that Go leaves the security problem to sandbox and VM like other languages. This proposal is a usable solution, but I am not arguing that Go must provide in-process security. Conceptually, the security boundary can be drawn at function level (Rust), package level (this proposal), process level (sandbox), OS level (VM), machine level (dedicated instance), data center level (on-premise and Gov Cloud), regions, or even country level (Great Firewall). I am pretty open all these choices. |
I don't think My point is: Video transcoding and |
Java security model does not cover native methods. My proposal does not
make assembly code safe. Such problem can be solved by **code auditing** on
assembly code and manual whitelist on the assembly code. To be clear, if
you include assembly code in a stateless package, the linker can not verify
it, it is up to the application developer to manually review the assembly
code and whitelist it, e.g. tell linker to accept it.
You can expect Google and other companies do run production applications
with third party libraries today, like libc. The safety is achieved via a
combination of trust and audit.
|
An automated auditing tool is possible for these security considerations. The Go standard library packages do mix a lot of behavior so locking out entire packages because of something like an unsafe or syscall package import may not be reasonable for your goals. But we are working with versioned standard library APIs, so an option is to audit the standard library and require third party libraries to follow a list of allowed standard types, functions, and global vars. Without the standard library only safe computation can be done.
Auditing the standard library for usage that matches your security needs is not a huge task. Perhaps this library auditing tool could be part of the standard language distribution. |
I agree with everyone that suggested auditing because this seems like a compile time problem that wants to be solved at runtime. Has it been demonstrated that it is impossible to derive state in a function call by observing the runtime indirectly?
There are close-source codec implementations written in pure Go. They don't need any of these things.
Video transcoding is not stateless. Even a decoder without package state sounds like a logistical nightmare. That call to Transcode needs to initialize 2 decoders and 1 encoder every time it's called and would need to process a complete elementary stream. They are also incompatible with this proposal because you need to be able to measure time to encode a conforming stream. |
Whether transcoding is stateless depends on the implementation. I don't think a decoder without state would be a nightmare. Such problem can be solved by minor API change.
To measure the time, the caller can pass a timer object to the transcoding function, see above. If we decide to use capability-based security, the library needs to be written in a way that is compatible with the model. It would be a straightforward experience in a language that is based on capability-based security from day one. Fitting any security model to an existing language would be extremely hard, since all existing libraries don't conform to the model and developers would not be happy to think things in different ways within the same language. FWIW, I don't think a real transcoder would use package state. That would prevent transcoding two streams at the same time. Most libraries should support parallel data processing. Comparing to browser sandbox and VMs, it is much simpler and more efficient to use capability-based security model within a process. However, it would still be significant amount of work for library authors to adopt the model. |
It appears we are not on the same page. Maybe the misunderstanding here comes from a lack of formalization of what it means for a package to have state. Is it:
The discussions seems to imply that
It makes very little sense to disallow package scoped variables but allow objects to have state when these objects are part of the untrusted package. If the function can tell that its been executed before, that function can derive state from within its object (in this case the function is a method).
A video encoder generates output by compressing spacial and temporal information in the signal. They don't just need a timer, they need to know what was encoded before. To do this without propagating error, modern encoders contain most of the decoder as well, and store a partially-decoded state of what the encoder encoded. The elementary stream itself is stored in a container format, which provides metadata for the elementary stream so that a decoder can decode that stream. At the lower levels of the stream are many similar state machines. To make matters worse, the decision of which machine to use depends on not only the profile/level of the encoder (which I assume you could put in the config), but is a characteristic of the stream itself. This crosses off the possibility of removing the object state. The possibility of exporting all of these state carriers is as function parameters is slim, unless you want a package function with hundreds of parameters (many of these states exist in warm or hot loops, but speed/simplicity/security, pick two or pick security). At the most abstract level, we can explore the security guarantees of a video encoder by simply creating a wrapper around ffmpeg, which provides all of these complex pieces in a command line executable. If we call But what if we implement all of the pieces of ffmpeg in Go? Ok, assume we did that. What security guarantee do we have now? No memory corruption, for one, but Go already provides this for free. Even then this hypothetical ffmpeg still has its own state, whether it stores it internally in a process or an object or a package level variable doesn't matter. My original point is that the video transcoder example is impossible by design if you remove object state. One benefit of this proposal is that the hypothetical ffmpeg can't import The proposal seems to target these cases where the developer crosses an imaginary trust boundary based on package scope, and interacts with some data outside the package. It presents no formal definition of security and relies on general assumptions of what defines security, and this makes the current proposal very difficult to reason about. |
The original issue description has the precise definition of what stateless
means in this proposal. Please use that definition to discuss the rest of
the proposal. Repeat the original definition here:
* No global variable.
* No direct access to state outside the current process, such as system
timer, system calls, io devices.
The rest of the proposal is just to implement capability based security
model in Go. The proposal assumes one knows the capability based security
and agrees with it. I don't intend to invent a fundamental security model
here.
Regarding the concern about object state, you can compare it to a file
descriptor. If caller gives you a file descriptor, you can read it or write
it. If not, you can't read or write any file. Open file is a global state,
you cannot do within a stateless package. Object state and file descriptor
are capabilities managed by the callers. It is up to developers to use the
capability based security to achieve their goals.
In short, this proposal suggests we can have a very simple way to implement
capability based security for Go language. Whether Go language needs a
security model or which security model should be addressed separately.
Latter is a philosophical question. We can always push the security problem
to sandbox layer or VM layer, which is what people do today.
|
The example by @crvv doesn’t look like global state to me. I interpreted the idea as meaning global vars that can be edited by external packages. Keeping state in a caller-owned var is stateless for this context. For example, package os has Stdin, Stdout, Stderr, and the shell args all as editable global vars which may be a security problem in some applications, such as a malicious library redirecting sensitive Stdout information to a network connection. Many packages have global error vars that may be changed to introduce another error handling behavior that uncovers a security hole found only by an attacker.
These packages have effects outside of the program’s address space, which can only be done with operating system calls (syscall).
|
The environment variables, file descriptors, GDI handle buffers, current working directory handle, thread environment block, process name, loaded module list, etc all live or are referenced by the process environment block, which is in the "current user address space". I don't know what a user address space is, but I do know that executing:
In any Windows amd64 process gives you a pointer to the PEB in rax, and the ability to modify this large data structure and its associated bits. |
Whatever a stateless package is, I think it's clear that it must not contain assembler code, and must not import the unsafe package. |
Let me clarify the porposal a bit. The proposal has at least 4 required conditions, more if we found bugs.
We should not debate on individual condition. Obviously, individual condition is not sufficient to provide any security. I believe these 4 conditions together are sufficient to offer capability-based security for Go. However, security researchers may find flaws with the proposal and I am happy to learn and improve on it. Whether Go needs a security model or whether capability-based security model is preferred choice is a separate topic. I don't plan to address them in this issue. |
Necessary conditions for security are clear definitions of security and an outline of assumptions those definitions rely on. What do you mean by user space? Are you talking about user vs kernel mode or something completely different? |
I mean the user mode part of the current process address space. Since Go
language itself can only touch the user mode part of the address space, we
should be fine even without this necessary condition.
|
As I believe @randall77 pointed out elsewhere, if you're excluding assembly because the author can use it to do nefarious things, you probably also need to exclude the |
I agree. |
Because writing to the same memory location repeatedly may affect the value of adjacent memory, |
The row hammering should be hardware bug. To avoid such problem, you can't even run two VMs on the same machine. You can only run one trusted application per machine, which is not a viable business. With go routine, you can return an array from a function, and use a go routine to keep changing the array data. That would be a huge security risk. |
Finally figured out why not having module-level state is good... I was working on github.com/tendermint/go-wire (sdk2 branch), when I realized that any call to wire.RegisterConcrete(...) could have devastating effects in unrelated decoding logic. In short, callers who rely on the global wire.(Un)Marshal*() are at the mercy of anyone calling the global wire.Register*() functions. (which changes the way registered types are marshaled and unmarshaled, which can break binary compatibility). One way to solve this problem is to get rid of the global implicit &wire.Codec{} instance altogether, so that all users of the go-wire module are forced to create their own (hopefully unexposed) codec instances. |
No, you can run an unbounded number of trusted applications per machine. They're trusted for a reason, aren't they? The question is whether this proposal (which frames security into the narrow tube of information hiding) a good substitute for traditional code review. Row hammering and friends are practical issues for modern day processors, ignoring them is academic ignorance: making it work in theory is useless if it can't work on real systems with real security requirements.
The proposal still makes an assumption that executable code can protect itself in the same process space. What modern system supports this? I feel like this proposal is an attempt to make it easier for people to incorporate random third party code in their go projects rather than achieving security. |
I get the data-race problem, but the solution is to fit more into 64 bits, and/or, to use 128 bit processors.
Seems like a problem to be solved at the RAM level. Maybe replication can help as well. |
OTOH, on some modules, you'll want to use a |
@as, I think we are using the same term to mean different thing. Running
multiple trusted applications on the same machine is equivalent to run one
trusted application with many components. If one is compromised, all are
compromised.
Even for a large company like Google, it is impossible to review the code
written by the rest of the world. Human errors happen all the time.
Protecting code inside the same address space is how browser works until
Chrome introduced multi process architecture. I think it does have its
value. Security protection always needs multiple layers. I fully expect
every layer has its own bugs.
If you think adding the layer proposed here will make it easier to use
random third party libraries, that becomes a meta debate. Every technology
can be abused, and it had been true through the human history, like car
accidents.
This proposal would provide capability based security for Go. That is all.
It won't pretend to solve all problems.
|
I don't understand how "fitting more into 64 bits" fixes the problem of untrusted code using data races to achieve pernicious goals. Or, for that matter, how it fixes any data race problems at all. |
As we discussed previously, stateless package cannot use go keyword, so
data race is not an issue for this specific proposal.
|
There is a particular data-race problem where three go-routines can coordinate to create an interface var value that would otherwise be impossible without the usage of the "unsafe" module. Since our CPUs are 64-bit, but Golang interface var's are 128-bit on a 64-bit machine, 2 goroutines with a data-race can cause an unsafe value to be picked up by the 3rd goroutine. But wait, I think we can just run the program with the race-detector... it'll be slower, but it'll be correct, as in, this data-race issue would be detected at run-time. The other solution is to pack more into 64-bits, as in, be able to represent interface var's as 64-bits, which would require (say) allocating some number of bits for the itable, and some 50+ bits to point to the heap... This would resolve the data-race issue on 64-bit machines. |
In any case, avoiding exposed global state seems like it's still generally good practice, so maybe that's all we need... to make illegal the exposing of var's at the module level. And through blogs and so on communicate why global state is bad in a certain perspective of security analysis, where the threat model is a malicious dependency. By locking the dependency versions w/ glide (say) and scanning the dependency list for usage of "unsafe", and running w/ the race-detector (say), and avoiding the usage of module level vars (e.g. the "crypto/random.Rand" var), and only buying RAM that isn't vulnerable to loopholes, or sufficiently duplicating the logic if the loophole is sufficiently unpredictable, we reduce the surface-area for the malicious dependency to operate in, minimizing and possibly even neutralizing any damage that may result. I think this is what @wora means by "capabilities based security". That's what I mean by it. It can be summarized with maybe a single mantra... "If a function has access to a capability as provided by the runtime or programming language, then it's because the function is implicitly allowed permission to execute those capabilities, such as reading or writing to a slice, or calling a function with side-effects". Using this as the basis for secure programming design, we can grow a programming framework that reduces points of vulnerability. Your dependencies won't be able to muck with things that they can't even access! |
Stateless packages, no assembly, and import constraints are too big of a concession for competing public libraries and also leave the memory accessible through other means we haven’t thought about. That data race is one of likely many possible hacks when working within the process memory space.
@jaekwon, @wora, what is your source for this? Specifically process capability security, not OS or hardware capability security as described by the Wikipedia article. |
Java and C# already solved the same problem with a more complicated and less usable solution. Browser also solved it in a different way. Obviously there is a cost for it, but I don't think it is prohibitive. I haven't use any global variable in my code for many years, and it works just fine. I worked on JVM before. I am confident to say capability-based model would be significantly simpler and more usable than the Java security model. It also encourages developers to have better design for their software. @erights is an expert in this area. The E programming language uses capability-based security, see https://en.wikipedia.org/wiki/E_(programming_language). |
@jaekwon Your assumption is wrong for multiple reasons. There is no guarantee types of a certain size are inherently safe to update concurrently. Even if they were, interface types are not the only types, and not every such type would be aligned on a memory location that would guarantee atomic updates. |
You're referring to how it's implemented now. I'm referring to how it could be implemented, since I'm more interested in the long-term prospects of the language and ecosystem. Go2 (say) could be implemented such that it supports atomic updates for all types. It could do this by packing more into 64 bits, and it would work on commodity machines. It would probably take a performance hit. |
I believe this proposal can be implemented entirely using a static analysis tool. That tool can look through the whole program and verify that a specific set of packages are safe. There is no need to change the language for this. As we gain experience with that tool, if it turns out to be very useful, then we can consider bringing it into the language proper. |
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:
The stateless package can be expressed with the following language syntax by reusing
const
keyword: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:
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 no 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++ styleconst
does not offer much security, and forces workarounds usingmutable
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.
The text was updated successfully, but these errors were encountered: