Affected Versions
| Affected | not applicable (first-party code) |
| Fixed in | not applicable (first-party code) — see PR for commit |
| Ecosystem | Go |
| CVE / GHSA | not assigned |
| CWE | CWE-311: Missing Encryption of Sensitive Data |
The Vulnerability Explained
When InitAuthDB(filepath string) creates a new SQLite database for authentication data, it relies on SQLite's default file creation behavior. On most systems, this means the file inherits permissions from the process umask—typically 0644, readable by any user on the host.
The vulnerable code opened the database without subsequent permission hardening:
func InitAuthDB(filepath string) error {
var err error
authDB, err = sql.Open("sqlite3", filepath)
if err != nil {
return err
}
// Set busy timeout for better concurrent access
_, err = authDB.Exec("PRAGMA busy_timeout=5000;")
This left bcrypt password hashes, session tokens, user settings, and call metadata in a plaintext file accessible to any local account. An attacker with non-privileged filesystem access—through a compromised service account, container escape, or shared hosting environment—could simply cat the database file and extract credentials without ever interacting with the application API.
The attack is particularly severe because bcrypt hashes, while slow to crack, are still vulnerable to offline brute-force attacks. Session tokens grant immediate access without any cracking effort.
The Fix
The patch adds explicit permission enforcement immediately after database creation:
authDB, err = sql.Open("sqlite3", filepath)
if err != nil {
return err
}
// The auth database holds bcrypt password hashes, session tokens, and user
// settings. Restrict it to the owner so other local accounts can't read
// those secrets straight off disk; sql.Open may create the file with a
// looser mode depending on umask, so enforce it explicitly here.
if err = os.Chmod(filepath, 0600); err != nil {
return fmt.Errorf("failed to restrict auth database file permissions: %w", err)
}
The os.Chmod(filepath, 0600) call executes before any sensitive data is written, ensuring the file is owner-readable and owner-writable only. The error is wrapped with context to aid debugging if the permission change fails—critical for fail-secure behavior where a misconfigured filesystem should block database initialization rather than proceed with insecure permissions.
This change addresses the root cause: SQLite's sql.Open does not provide a cross-platform way to specify creation permissions, and Go's os.OpenFile flags cannot be passed through the database driver. Post-creation permission adjustment is the only reliable mechanism.
Key Takeaways
- Never trust default file permissions for security-critical data: Database drivers and filesystems vary in behavior; explicitly set permissions after creation.
- Bcrypt hashes require the same protection as plaintext passwords: Offline cracking makes hash exposure nearly as dangerous as credential theft.
- Session tokens stored on disk need immediate access controls: Unlike password hashes, tokens provide instant authentication without computation.
- Fail-secure on permission enforcement: Return errors and halt initialization if permissions cannot be restricted, rather than continuing with potentially insecure defaults.
- Document security intent in code comments: The inline comment explains why 0600 is necessary, preserving this knowledge for future maintainers.
How Orbis AppSec Detected This
Source: The filepath parameter to InitAuthDB(), accepting arbitrary database locations from calling code.
Sink: sql.Open("sqlite3", filepath) creating a database file with default umask-derived permissions.
Missing control: No os.Chmod or equivalent permission restriction between file creation and data storage.
CWE: CWE-311: Missing Encryption of Sensitive Data
Fix: Added os.Chmod(filepath, 0600) immediately after sql.Open with wrapped error handling to enforce owner-only access.
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
This vulnerability demonstrates that encryption at rest does not require complex cryptography—sometimes proper filesystem permissions are the critical missing control. The InitAuthDB() fix shows how a single os.Chmod call can eliminate a local privilege escalation vector. For Go developers using SQLite for sensitive data, always verify and enforce permissions after database creation rather than assuming secure defaults.