Security Requirements for Hyperlight developers
This document discusses the security requirements and best practices for services building on Hyperlight. These requirements are designed to uphold the security promise around the guest to host boundary:
Terminology
- MUST, MUST NOT -- A security requirement that is mandatory to perform, or not to perform, respectively.
- SHOULD, SHOULD NOT -- A security recommendation is encouraged to perform, or not perform, respectively.
Brief Checklist
- All host functions that receive parameters from a guest, or operate indirectly on guest data MUST be continuously fuzzed
- Host functions MUST NOT call APIs or be used to expose functionality deemed risky in a multi-tenant context
- Guests and host processes MUST use the same version of a FlatBuffer definition
More detailed guidance on the requirements and best practices is detailed below.
All host functions exposed to the guest MUST be continuously fuzzed
In the case of the host function calling guest functions, there will be the need to mock callees. Thus, be aware it may not be an identical state machine, thus have different bugs. A host exposed function should be able to execute at least 500 million of fuzz test cases iterations without any crash.
For rust code, Cargo-fuzz is the recommended way to harness and satisfy the fuzzing requirements. An example with a complete example implementation can be found Fuzzing with cargo-fuzz - Rust Fuzz Book (rust-fuzz.github.io) .
#![no_main]
#[macro_use] extern crate libfuzzer_sys;
extern crate url;
fuzz_target!(|data: &[u8]| {
if let Ok(s) = std::str::from_utf8(data) {
let _ = url::Url::parse(s);
}
});
Host functions MUST NOT call APIs or expose functionality deemed risky in a multi-tenant context
In a multi-tenant context, the following operations are considered security sensitive:
- File creation and manipulation
- Shared Data store access
- Accessing network resources
- Resource allocation and usage: if not designed properly, one guest may exhaust the resources for other tenants
- Managing encryption keys
If any of these operations is performed in a host process, a security audit MUST occur.
Flatbuffers - Guests and host processes MUST use compatible versions of a FlatBuffer definitions
The guests and host processes MUST use the exact same versions of a FlatBuffer definition. I.e., the same .fbs file MUST be used for generating the encoders and decoders.
Flatbuffers - If using the same language for development, the guests and host processes SHOULD use the same version of flatc compilers.
This can be seen in the header files containing FLATBUFFERS_COMMON_READER_H. For instance: /* Generated by flatcc 0.6.2 FlatBuffers schema compiler
.
We emit this recommendation because there is a history of compiler bugs, which may affect certain behaviors (encoding, decoding). We emit this recommendation because there is a history of compiler bugs, which may adversely affect certain behaviors (encoding, decoding).
Flatbuffers – a verifier should always be called before any decoder. In the case of failed verification, the input MUST NOT be processed.
For Rust code, if the return code is InvalidFlatBuffer, the input MUST be rejected.
Flatbuffers – the host process MUST NOT operate on Flatbuffers from several threads.
Because of the zero-copy approach that FlatBuffers is using, there is a risk of memory safety issues. Flatbuffers are unsafe to be used in a multithreaded environment. This is explicitly indicated in several parts of the Flatbuffer documentation.
Additionally, because Flatbuffers tainted data coming from the guests, this is even more critical in a multi-tenant scenario.
A rust host process SHOULD handle panics or the service SHOULD restart automatically
If the error is recoverable, the service SHOULD process the next input. Otherwise, the service SHOULD gracefully restart.