This guide covers security patterns used in osquery with OpenFrame, including authentication, encryption, secrets management, input validation, and common vulnerability mitigations.
The OpenFrame layer uses a provider-controlled singleton pattern to manage JWT authentication tokens:
graph LR
Provider["AuthorizationManagerProvider"] --> Manager["AuthorizationManager"]
Extractor["TokenExtractor"] --> Manager
Refresher["TokenRefresher"] --> Extractor
Manager --> Token["Bearer JWT Token"]
Token --> HTTP["Outbound HTTPS Requests"]
Key principles:
OpenframeAuthorizationManageris non-copyable (boost::noncopyable) — tokens cannot be accidentally duplicated- Only
OpenframeAuthorizationManagerProvidercan construct or destroy the manager (private constructor/destructor) - The
OpenframeTokenRefresherruns in a dedicated background thread usingstd::atomic<bool>for lock-free lifecycle control - Token updates and reads are centralized through
updateToken()andgetToken()
Never:
- Store JWT tokens in plain text files or environment variables without appropriate OS-level access controls
- Log token values, even at debug level
- Pass tokens as command-line arguments
The OpenframeEncryptionService provides symmetric encryption using AES-256-GCM — an authenticated encryption scheme:
// Initialize with a 256-bit secret key
OpenframeEncryptionService enc("my-32-byte-secret-key-goes-here!");
// Decrypt a Base64-encoded AES-256-GCM ciphertext
std::string plaintext = enc.decrypt(ciphertext_base64);Encryption properties:
| Property | Value |
|---|---|
| Algorithm | AES-256-GCM |
| Key size | 32 bytes (256-bit) |
| IV (nonce) size | 12 bytes (96-bit) |
| Authentication tag | 16 bytes (128-bit) |
| Encoding | Base64 |
Security rules:
- The symmetric key must be exactly 32 bytes
- GCM authentication tag is verified on every decryption — tampered ciphertext throws
std::runtime_error - Never reuse nonces for the same key
- Store keys using OS keystore mechanisms or secret management systems, not in source code
osquery's embedded SQLite engine includes a strict authorizer (sqliteAuthorizer) that allowlists only safe SQL operations:
graph TD
Prepare["sqlite3_prepare_v2"] --> Authorizer["sqliteAuthorizer"]
Authorizer -->|"Allowed"| Continue["Execute Statement"]
Authorizer -->|"Denied"| Reject["SQLITE_DENY"]
Explicitly allowlisted:
SELECT,READoperations- Controlled
INSERT,UPDATE,DELETE(virtual tables only) - Virtual table creation and drop
- Limited
PRAGMAcommands
Explicitly denied:
SQLITE_ATTACH— cannot attach external database files- Any non-allowlisted opcode
This prevents osquery from being abused to read arbitrary files via SQLite's ATTACH mechanism or execute unsafe operations.
All SQL submitted through the distributed querying or config channels is:
- Parsed by the SQLite authorizer before execution
- Validated against the virtual table schema
- Size-limited by configuration constraints
The Config singleton enforces:
- JSON max depth limits
- JSON max document size limits
- Comment stripping (non-standard JSON is rejected)
- Hash-based change detection to prevent replay attacks
Extension processes are validated during registration:
- SDK version compatibility is enforced
- Duplicate extension names are rejected
- Route UUIDs are generated server-side (not client-controlled)
Never embed secrets in:
- Source code
- CMake configuration
- Build scripts
- Log output
Use OS-level secrets management:
| Platform | Recommended Tool |
|---|---|
| Linux | systemd credentials, HashiCorp Vault, AWS Secrets Manager |
| macOS | Keychain, AWS Secrets Manager |
| Windows | Windows Credential Manager, Azure Key Vault |
Sensitive flags (TLS certificates, enrollment secrets) should be stored in a flagfile with restricted permissions:
# Create flagfile with restricted permissions
sudo touch /etc/osquery/osquery.secret.flags
sudo chmod 600 /etc/osquery/osquery.secret.flags
sudo chown root:root /etc/osquery/osquery.secret.flags
# Write sensitive flags
echo "--tls_server_certs=/etc/osquery/server.pem" | sudo tee -a /etc/osquery/osquery.secret.flags
echo "--enroll_secret_path=/etc/osquery/enroll.secret" | sudo tee -a /etc/osquery/osquery.secret.flagsReference the flagfile:
sudo ./osqueryd --flagfile=/etc/osquery/osquery.secret.flagsThe Remote HTTP Client module (osquery/remote/) enforces:
- TLS by default for all remote communication
- Peer certificate verification
- Configurable cipher suites
- Custom CA certificate paths
- Timeout enforcement (no hanging connections)
Client::Options options;
options.ssl_connection(true)
.verify_peer(true)
.ca_verify_path("/etc/ssl/certs/ca-certificates.crt")
.timeout(30);
Never disable peer verification in production. The verify_peer flag should only be false in isolated development environments.
| Vulnerability | Mitigation in osquery |
|---|---|
| SQL Injection | Authorizer blocks unsafe opcodes; only allowlisted operations execute |
| Credential Theft | JWT tokens non-copyable, stored in singleton with restricted access |
| Ciphertext Tampering | AES-256-GCM authentication tag verified on every decrypt |
| Privilege Escalation | Watcher/Worker model isolates query execution from the supervisor |
| External DB Injection | SQLITE_ATTACH explicitly denied by authorizer |
| Token Replay | OpenframeTokenRefresher rotates tokens on a schedule |
| Extension Impersonation | SDK version check and UUID server-assignment prevent spoofing |
| Resource Exhaustion | Watchdog enforces CPU/memory limits and respawns workers |
| Config Tampering | Hash-based change detection and JSON size/depth limits |
# Build with tests enabled
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DOSQUERY_BUILD_TESTS=ON
cmake --build build --parallel $(nproc)
# Run all tests
cd build && ctest --output-on-failure
# Run specific security-related tests
cd build && ctest -R "tls" --output-on-failure
cd build && ctest -R "config" --output-on-failure
cd build && ctest -R "sql" --output-on-failureBefore submitting any PR that touches security-sensitive code:
- No secrets or credentials hardcoded
- All SQL inputs pass through the authorizer
- AES-GCM nonces are generated freshly (not reused)
- TLS peer verification is not disabled
- Error paths do not leak sensitive information in logs
- New extension APIs validate input size and type
- Thread-shared state uses proper synchronization primitives
- New config keys have size/depth validation
Security vulnerabilities should be reported directly to the Flamingo team through the OpenMSP Slack community:
Do not open public GitHub Issues for security vulnerabilities.