Configuration Security — Complete Implementation Guide
In this tutorial, you will learn about Configuration Security. We cover key concepts, practical examples, and best practices to help you master this topic.
Configuration security encompasses the practices of protecting sensitive configuration data including secrets, API keys, and certificates from unauthorized access, leakage, and tampering throughout the application lifecycle.
What You'll Learn
By the end of this tutorial, you will know how to secure configuration data, detect secrets in code, implement encryption, manage access control, audit config changes, and meet compliance requirements for configuration management.
Why It Matters
Configuration breaches are one of the most common security incidents. Hardcoded secrets in code, misconfigured storage, and overly permissive access have led to some of the largest data breaches in history.
Real-World Use
DodaTech scans all repositories with git-secrets and truffleHog before every commit. Secrets in production are stored in Vault with automatic rotation, and all config access is logged for SOC 2 compliance.
Config Security Learning Path
flowchart LR
A[Config Pipeline] --> B[Config Security]
B --> C[Secret Detection]
B --> D[Encryption]
B --> E[Access Control]
B --> F[Audit]
B --> G{You Are Here}
style G fill:#f90,color:#fff
Secret Detection in Code
Automated tools scan code for hardcoded secrets before they reach version control.
class SecretDetector {
constructor() {
this.patterns = [
{ name: "AWS Access Key", regex: /AKIA[0-9A-Z]{16}/, severity: "critical" },
{ name: "Private Key", regex: /-----BEGIN\s+(RSA|EC|DSA|OPENSSH)\s+PRIVATE\s+KEY-----/, severity: "critical" },
{ name: "Generic API Key", regex: /(?:api[_-]?key|apikey|secret)[\s:=]+['"]?[0-9A-Za-z]{20,}['"]?/i, severity: "high" },
{ name: "Password in Code", regex: /password[\s:=]+['"]?[^'"\s]{6,}['"]?/i, severity: "high" },
{ name: "JWT Token", regex: /eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/, severity: "high" },
{ name: "Connection String", regex: /(?:postgres|mysql|mongodb|redis):\/\/[^@\s]+:[^@\s]+@/, severity: "critical" }
];
}
scan(content, filename) {
const findings = [];
this.patterns.forEach(pattern => {
const matches = content.matchAll(new RegExp(pattern.regex, "g"));
for (const match of matches) {
findings.push({
pattern: pattern.name,
severity: pattern.severity,
line: this.getLineNumber(content, match.index),
match: this.maskSecret(match[0]),
filename
});
}
});
return findings;
}
getLineNumber(content, index) {
return content.slice(0, index).split("\n").length;
}
maskSecret(secret) {
if (secret.length <= 8) return "********";
return secret.slice(0, 4) + "****" + secret.slice(-4);
}
scanFile(filepath) {
const fs = require("fs");
const content = fs.readFileSync(filepath, "utf8");
return this.scan(content, filepath);
}
}
const detector = new SecretDetector();
const codeSample = `
const awsKey = "AKIAIOSFODNN7EXAMPLE";
const password = "supersecret123!";
const dbUrl = "postgresql://admin:pass123@localhost:5432/app";
`;
const findings = detector.scan(codeSample, "config.js");
console.log("Secrets found:", findings.length);
findings.forEach(f => console.log(` [${f.severity}] ${f.pattern}: ${f.match}`));
// Secrets found: 3
// [critical] AWS Access Key: AKIA****MPLE
// [high] Password in Code: supe****123!
// [critical] Connection String: post****@...
Encryption at Rest for Configuration
Configuration data should be encrypted when stored, whether in files, databases, or secrets managers.
class ConfigEncryption {
constructor() {
this.algorithms = {
aes256gcm: {
encrypt: (data, key) => {
const crypto = require("crypto");
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
let encrypted = cipher.update(data, "utf8", "hex");
encrypted += cipher.final("hex");
const tag = cipher.getAuthTag().toString("hex");
return { encrypted, iv: iv.toString("hex"), tag, algorithm: "aes-256-gcm" };
},
decrypt: (encrypted, key, iv, tag) => {
const crypto = require("crypto");
const decipher = crypto.createDecipheriv("aes-256-gcm", key, Buffer.from(iv, "hex"));
decipher.setAuthTag(Buffer.from(tag, "hex"));
let decrypted = decipher.update(encrypted, "hex", "utf8");
decrypted += decipher.final("utf8");
return decrypted;
}
}
};
}
encryptConfig(config, key) {
const serialized = JSON.stringify(config);
return this.algorithms.aes256gcm.encrypt(serialized, key);
}
decryptConfig(encrypted, key, iv, tag) {
const decrypted = this.algorithms.aes256gcm.decrypt(encrypted, key, iv, tag);
return JSON.parse(decrypted);
}
}
const crypto = require("crypto");
const enc = new ConfigEncryption();
const key = crypto.randomBytes(32);
const secureConfig = enc.encryptConfig({
databaseUrl: "postgresql://app:secret@db:5432/prod",
apiKey: "sk-prod-abc123xyz"
}, key);
console.log("Encrypted config:", secureConfig.encrypted.slice(0, 20) + "...");
console.log("Encryption algorithm:", secureConfig.algorithm);
const decrypted = enc.decryptConfig(secureConfig.encrypted, key, secureConfig.iv, secureConfig.tag);
console.log("Decrypted API key:", decrypted.apiKey);
// Encrypted config: a1b2c3d4e5f6...
// Encryption algorithm: aes-256-gcm
// Decrypted API key: sk-prod-abc123xyz
Access Control for Configuration
Control who and what can read or modify configuration data.
class ConfigAccessControl {
constructor() {
this.roles = new Map();
this.permissions = new Map();
this.auditLog = [];
}
defineRole(name, permissions) {
this.roles.set(name, permissions);
}
grantPermission(principal, configPath, actions) {
const key = `${principal}:${configPath}`;
this.permissions.set(key, actions);
this.auditLog.push({
action: "grant",
principal,
resource: configPath,
permissions: actions,
timestamp: new Date().toISOString()
});
console.log(`Granted ${actions.join(", ")} on ${configPath} to ${principal}`);
}
checkAccess(principal, configPath, action) {
// Check direct permissions
const directKey = `${principal}:${configPath}`;
const directPerms = this.permissions.get(directKey);
if (directPerms && directPerms.includes(action)) {
return true;
}
// Check role permissions
const rolePerms = this.roles.get(principal);
if (rolePerms && rolePerms.includes(`${configPath}:${action}`)) {
return true;
}
// Check wildcard permissions
for (const [key, perms] of this.permissions) {
const [p, resource] = key.split(":");
if (p === principal && resource.endsWith("*")) {
const prefix = resource.slice(0, -1);
if (configPath.startsWith(prefix) && perms.includes(action)) {
return true;
}
}
}
return false;
}
requireAccess(principal, configPath, action) {
const allowed = this.checkAccess(principal, configPath, action);
this.auditLog.push({
action: allowed ? "allowed" : "denied",
principal,
resource: configPath,
operation: action,
timestamp: new Date().toISOString()
});
if (!allowed) {
throw new Error(`Access denied: ${principal} cannot ${action} ${configPath}`);
}
return true;
}
getAuditLog() {
return this.auditLog;
}
}
const access = new ConfigAccessControl();
access.defineRole("readonly", ["config/*:read"]);
access.grantPermission("service-api", "config/database/url", ["read"]);
access.grantPermission("deploy-bot", "config/*", ["read", "write"]);
console.log("API service can read database URL:",
access.checkAccess("service-api", "config/database/url", "read"));
console.log("API service can write database URL:",
access.checkAccess("service-api", "config/database/url", "write"));
// Granted read on config/database/url to service-api
// Granted read, write on config/* to deploy-bot
// API service can read database URL: true
// API service can write database URL: false
Audit Logging for Configuration
Every configuration change should be logged for compliance and incident response.
class ConfigAuditor {
constructor() {
this.logs = [];
this.storage = [];
}
logChange(event) {
const entry = {
id: this.generateId(),
timestamp: new Date().toISOString(),
principal: event.principal,
action: event.action,
resource: event.resource,
oldValue: event.oldValue ? this.maskValue(event.resource, event.oldValue) : null,
newValue: event.newValue ? this.maskValue(event.resource, event.newValue) : null,
sourceIP: event.sourceIP,
userAgent: event.userAgent,
status: event.status || "success"
};
this.logs.push(entry);
this.storage.push(entry);
return entry;
}
generateId() {
return `cfg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
}
maskValue(resource, value) {
const sensitivePaths = ["password", "secret", "key", "token", "credential"];
const isSensitive = sensitivePaths.some(p => resource.toLowerCase().includes(p));
if (isSensitive && value && value.length > 4) {
return value.slice(0, 2) + "****" + value.slice(-2);
}
return value;
}
query(filters = {}) {
return this.logs.filter(entry => {
return Object.entries(filters).every(([key, value]) => {
return entry[key] === value;
});
});
}
generateReport(startDate, endDate) {
const filtered = this.logs.filter(entry => {
const date = new Date(entry.timestamp);
return date >= new Date(startDate) && date <= new Date(endDate);
});
const summary = {
totalChanges: filtered.length,
byAction: {},
byPrincipal: {},
byStatus: {}
};
filtered.forEach(entry => {
summary.byAction[entry.action] = (summary.byAction[entry.action] || 0) + 1;
summary.byPrincipal[entry.principal] = (summary.byPrincipal[entry.principal] || 0) + 1;
summary.byStatus[entry.status] = (summary.byStatus[entry.status] || 0) + 1;
});
return summary;
}
}
const auditor = new ConfigAuditor();
auditor.logChange({
principal: "admin@dodatech.com",
action: "update",
resource: "config/database/url",
oldValue: "postgresql://old:pass@localhost:5432/app",
newValue: "postgresql://new:pass@prod-db:5432/app",
sourceIP: "10.0.1.100",
userAgent: "ConfigDashboard/1.0"
});
auditor.logChange({
principal: "ci-bot",
action: "create",
resource: "config/feature-flags/new-checkout",
newValue: '{"enabled": false}',
sourceIP: "10.0.0.50",
userAgent: "GitOpsPipeline/2.0",
status: "success"
});
const report = auditor.generateReport("2026-06-01", "2026-06-30");
console.log("Config changes this month:", report.totalChanges);
console.log("By status:", report.byStatus);
// Config changes this month: 2
// By status: { success: 2 }
Git Hooks for Secret Prevention
Prevent secrets from being committed with pre-commit hooks.
class GitSecretHook {
constructor() {
this.detector = new SecretDetector();
this.exclusions = [".env.example", "test/fixtures/", "*.md"];
}
async runPreCommit() {
const { execSync } = require("child_process");
try {
const files = execSync("git diff --cached --name-only --diff-filter=ACM")
.toString()
.split("\n")
.filter(Boolean);
let hasSecrets = false;
for (const file of files) {
if (this.isExcluded(file)) continue;
try {
const content = execSync(`git show :${file}`).toString();
const findings = this.detector.scan(content, file);
if (findings.length > 0) {
console.error(`Secrets detected in ${file}:`);
findings.forEach(f => {
console.error(` [${f.severity.toUpperCase()}] Line ${f.line}: ${f.pattern}`);
});
hasSecrets = true;
}
} catch (err) {
// Binary file or deletion, skip
}
}
if (hasSecrets) {
console.error("\nCommit blocked: Remove secrets before committing.");
console.error("Use git-secrets or truffleHog for comprehensive scanning.");
process.exit(1);
}
console.log("No secrets detected in staged files");
} catch (err) {
console.error("Pre-commit hook error:", err.message);
}
}
isExcluded(file) {
return this.exclusions.some(pattern => {
if (pattern.endsWith("*")) {
const prefix = pattern.slice(0, -1);
return file.startsWith(prefix) || file.endsWith(prefix);
}
return file === pattern;
});
}
}
const hook = new GitSecretHook();
hook.runPreCommit();
// No secrets detected in staged files
Compliance Requirements
Configuration security often must meet compliance standards like SOC 2, PCI DSS, and HIPAA.
class ComplianceChecker {
constructor() {
this.standards = new Map();
}
addStandard(name, requirements) {
this.standards.set(name, requirements);
}
check(standardName, config) {
const standard = this.standards.get(standardName);
if (!standard) throw new Error(`Unknown standard: ${standardName}`);
const results = standard.map(req => {
const passed = req.check(config);
return {
requirement: req.name,
description: req.description,
passed,
details: passed ? "Compliant" : req.remediation || "Action required"
};
});
return {
standard: standardName,
compliant: results.every(r => r.passed),
results,
timestamp: new Date().toISOString()
};
}
}
const compliance = new ComplianceChecker();
compliance.addStandard("SOC2-Config", [
{
name: "CC6.1",
description: "Logical access to config data is restricted",
check: (config) => config.accessControl?.enabled === true,
remediation: "Enable access control on configuration storage"
},
{
name: "CC6.7",
description: "Config data is encrypted at rest",
check: (config) => config.encryptionAtRest?.enabled === true,
remediation: "Enable encryption at rest for configuration data"
},
{
name: "CC7.2",
description: "Config changes are monitored and logged",
check: (config) => config.auditLogging?.enabled === true,
remediation: "Enable audit logging for configuration changes"
},
{
name: "CC8.1",
description: "Secrets are not stored in code",
check: (config) => config.secretsInCode === false || config.secretsInCode === undefined,
remediation: "Remove hardcoded secrets and use a secrets manager"
}
]);
const configStatus = {
accessControl: { enabled: true },
encryptionAtRest: { enabled: false },
auditLogging: { enabled: true }
};
const result = compliance.check("SOC2-Config", configStatus);
console.log("SOC 2 compliant:", result.compliant);
console.log("Failed requirements:", result.results.filter(r => !r.passed).length);
// SOC 2 compliant: false
// Failed requirements: 1
Common Mistakes
Hardcoding secrets in source code -- Secrets in code end up in version control history forever. Use environment variables or a secrets manager for all sensitive values.
Overly permissive config access -- Giving all developers access to production config creates unnecessary risk. Implement least-privilege access with audit trails.
No encryption of config at rest -- Config files on disk are plaintext by default. Encrypt sensitive configuration using your operating system's encryption or a secrets manager.
Logging secrets in plain text -- Application logs often contain configuration values. Implement log scrubbing to redact secrets before they reach log storage.
Not rotating configuration secrets -- Static secrets that never change increase breach risk. Implement automatic rotation with short validity periods.
Storing config in environment variables with secrets -- Environment variables are visible in Process listings, /proc filesystem, and debugging tools. Use file-based secrets with restricted permissions.
Practice Questions
What is the most effective way to prevent secrets from being committed to Git? Use a combination of pre-commit hooks (git-secrets, truffleHog), CI/CD scanning, and developer education. Pre-commit hooks catch secrets before they reach the remote.
How should secrets be stored in a Kubernetes environment? Use Kubernetes Secrets with encryption at rest enabled. For GitOps, use Sealed Secrets or External Secrets Operator to synchronize secrets from Vault or AWS Secrets Manager.
What is the principle of Least Privilege for configuration? Each service or person should have access only to the configuration they need to perform their function. No one should have blanket access to all configuration.
Challenge: Implement a configuration security scanner that detects common vulnerabilities.
class ConfigSecurityScanner {
scan(config) {
const vulnerabilities = [];
if (config.password === "password" || config.password === "admin") {
vulnerabilities.push({
severity: "critical",
finding: "Default password in use",
remediation: "Change to a strong, unique password"
});
}
if (config.sslEnabled === false) {
vulnerabilities.push({
severity: "high",
finding: "SSL/TLS is disabled",
remediation: "Enable SSL/TLS for all connections"
});
}
if (config.corsOrigins?.includes("*")) {
vulnerabilities.push({
severity: "medium",
finding: "CORS allows all origins",
remediation: "Restrict CORS to specific trusted origins"
});
}
return { vulnerabilities, secure: vulnerabilities.length === 0 };
}
}
FAQ
Mini Project
Build a configuration security manager that encrypts config files, validates access control, audits all changes, scans for secrets, and generates a compliance report.
class ConfigSecurityManager {
constructor() {
this.encryption = new ConfigEncryption();
this.auditor = new ConfigAuditor();
this.access = new ConfigAccessControl();
this.detector = new SecretDetector();
this.key = crypto.randomBytes(32);
}
secureStore(config, path, principal) {
this.access.requireAccess(principal, path, "write");
const secretsFound = this.detector.scan(JSON.stringify(config), path);
if (secretsFound.length > 0) {
throw new Error(`Cannot store: secrets detected in config (${secretsFound.length} findings)`);
}
const encrypted = this.encryption.encryptConfig(config, this.key);
this.auditor.logChange({
principal,
action: "secure-store",
resource: path,
newValue: "encrypted",
status: "success"
});
return encrypted;
}
secureRetrieve(encrypted, path, principal) {
this.access.requireAccess(principal, path, "read");
const config = this.encryption.decryptConfig(encrypted.encrypted, this.key, encrypted.iv, encrypted.tag);
this.auditor.logChange({
principal,
action: "secure-retrieve",
resource: path,
status: "success"
});
return config;
}
}
const mgr = new ConfigSecurityManager();
const config = { databaseUrl: "postgresql://app:secret@db:5432/app", port: 8080 };
const secure = mgr.secureStore(config, "config/production/database", "deploy-bot");
console.log("Config secured and stored");
What's Next
Now that you understand configuration security, put everything together in the configuration project. Then explore feature flags for dynamic runtime configuration.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro