惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Apple Machine Learning Research
Apple Machine Learning Research
AWS News Blog
AWS News Blog
Google DeepMind News
Google DeepMind News
U
Unit 42
博客园 - 叶小钗
博客园 - 聂微东
GbyAI
GbyAI
Stack Overflow Blog
Stack Overflow Blog
有赞技术团队
有赞技术团队
aimingoo的专栏
aimingoo的专栏
D
DataBreaches.Net
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Jina AI
Jina AI
美团技术团队
The Cloudflare Blog
M
MIT News - Artificial intelligence
Microsoft Azure Blog
Microsoft Azure Blog
I
InfoQ
S
Schneier on Security
C
Check Point Blog
Project Zero
Project Zero
The Hacker News
The Hacker News
Scott Helme
Scott Helme
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Cisco Talos Blog
Cisco Talos Blog
P
Privacy International News Feed
SecWiki News
SecWiki News
Latest news
Latest news
MongoDB | Blog
MongoDB | Blog
S
Secure Thoughts
Google Online Security Blog
Google Online Security Blog
F
Fortinet All Blogs
博客园 - 三生石上(FineUI控件)
H
Help Net Security
TaoSecurity Blog
TaoSecurity Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Last Week in AI
Last Week in AI
P
Privacy & Cybersecurity Law Blog
Forbes - Security
Forbes - Security
G
GRAHAM CLULEY
N
Netflix TechBlog - Medium
L
Lohrmann on Cybersecurity
A
About on SuperTechFans
T
The Exploit Database - CXSecurity.com
C
Cisco Blogs
PCI Perspectives
PCI Perspectives
大猫的无限游戏
大猫的无限游戏
T
Troy Hunt's Blog
H
Hacker News: Front Page
Vercel News
Vercel News

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Secrets Management Best Practices with HashiCorp Vault
DevOps Daily · 2026-06-22 · via DEV Community

A database password leaks. Maybe it was committed to a private repo three years ago, maybe it sat in a CI log, maybe a contractor copied it into a Slack DM. You do not know, because that password has been valid the entire time and nobody rotated it. Now you are in an incident channel at 2am trying to figure out the blast radius of a credential that every service, every old laptop, and every backup job has used since 2023.

This is the problem HashiCorp Vault solves, and it is not the problem most teams use it for. Most teams install Vault, run it in dev mode, dump a pile of static key-value secrets into it, and call it done. That gives you an encrypted password store with a nicer API. Useful, but it leaves the worst part untouched: secrets that live forever and that no human can fully account for.

The real win with Vault is making secrets short-lived and generated on demand, so a leak has an expiry date measured in hours instead of years. This post shows how to run Vault for that: a production server that survives reboots, machine authentication that does not depend on root tokens, dynamic database credentials, and encryption as a service. Every command here is one you can run.

TLDR

  • Never run vault server -dev for anything real. It is in-memory and unsealed, so a restart wipes every secret.
  • Use auto-unseal (AWS KMS, GCP KMS, or another Vault) so a reboot does not need five humans with key shares.
  • Authenticate machines with AppRole, not long-lived root or service tokens.
  • Use dynamic secrets for databases. Vault creates a unique DB user per request with a short TTL and deletes it when the lease ends.
  • Use the transit engine for encryption as a service so your apps never touch the encryption keys.
  • Write least-privilege policies, turn on the audit log, and revoke leases when something goes wrong.

Prerequisites

  • A Linux host (or VM) where you can install the Vault binary
  • Vault 1.15 or newer (vault version to check)
  • A PostgreSQL database you can point Vault at for the dynamic secrets section
  • An AWS account with a KMS key if you want auto-unseal (optional but recommended)
  • Basic comfort with the command line and HCL config files

Stop running Vault in dev mode

Dev mode is the trap. You run one command and get a working Vault:

vault server -dev

==> Vault server configuration:
             Api Address: http://127.0.0.1:8200
                     Cgo: disabled
         Cluster Address: https://127.0.0.1:8201
              Listener 1: tcp (addr: "127.0.0.1:8200", tls: "disabled")
               Log Level: info
                   Mlock: supported: true, enabled: false
           Recovery Mode: false
                 Storage: inmem

WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory
and starts unsealed with a single unseal key.

Read that warning. Storage: inmem means every secret lives in RAM and disappears on restart. tls: disabled means traffic is plaintext. It starts unsealed, so anyone who reaches port 8200 owns it. Dev mode is for trying commands on your laptop, nothing else.

A production server needs three things dev mode skips: persistent storage, TLS, and a seal. Here is a real config.hcl using integrated Raft storage and AWS KMS auto-unseal:

# /etc/vault.d/vault.hcl
storage "raft" {
  path    = "/opt/vault/data"
  node_id = "vault-1"
}

listener "tcp" {
  address       = "0.0.0.0:8200"
  tls_cert_file = "/opt/vault/tls/vault.crt"
  tls_key_file  = "/opt/vault/tls/vault.key"
}

# Auto-unseal: Vault asks KMS to decrypt its root key on boot.
# No more gathering humans with key shares after every restart.
seal "awskms" {
  region     = "us-east-1"
  kms_key_id = "arn:aws:kms:us-east-1:111122223333:key/abc-12345"
}

api_addr     = "https://vault-1.internal:8200"
cluster_addr = "https://vault-1.internal:8201"
ui           = true

Start it and initialize once:

vault server -config=/etc/vault.d/vault.hcl &

export VAULT_ADDR="https://vault-1.internal:8200"
vault operator init -recovery-shares=5 -recovery-threshold=3

Recovery Key 1: vR2k9... (give to a different person than key 2)
Recovery Key 2: 8Lp4m...
Recovery Key 3: qW7nZ...
Recovery Key 4: 3xF8t...
Recovery Key 5: hT1bY...

Initial Root Token: hvs.CAESIJ...

Success! Vault is initialized

Recovery key initialized with 5 key shares and a key threshold of 3.

Because of auto-unseal you get recovery keys instead of unseal keys. Vault unseals itself on boot using KMS, and the recovery keys are only for emergencies like regenerating the root token. Split them across different people and store them offline. Never keep all of them in one place.

Now use that root token once to set up authentication and policies, then throw it away. Root tokens are for break-glass moments, not daily use.

vault login hvs.CAESIJ...

If you ever see this, your Vault restarted and could not reach its seal:

$ vault kv get secret/payments/stripe
Error making API request.
URL: GET https://vault-1.internal:8200/v1/secret/data/payments/stripe
Code: 503. Errors:
* Vault is sealed

A sealed Vault answers nothing. That is the whole point. Auto-unseal exists so this state heals itself instead of paging you.

Authenticate machines with AppRole, not tokens

A common mistake: generate a long-lived token, paste it into an app's environment, and forget it exists. Now you have the same forever-credential problem one layer up. If that token leaks, it works until someone notices.

For machines, use AppRole. The app proves its identity with a role_id (think username, not very secret) and a secret_id (think password, short-lived and delivered separately), and gets back a token scoped to exactly what it needs.

vault auth enable approle

# Create a role for the payments service.
vault write auth/approle/role/payments-api \
    token_policies="payments-api" \
    token_ttl=1h \
    token_max_ttl=4h \
    secret_id_ttl=24h \
    secret_id_num_uses=1

# role_id is stable and tied to the role.
vault read auth/approle/role/payments-api/role-id

Key        Value
---        -----
role_id    7b1c4e2a-9f3d-4a8e-b6c1-2d5f8e0a1b3c

The secret_id is the part that needs care. Generate it just before the app starts and hand it over once. With secret_id_num_uses=1 it works exactly one time, so a leaked secret_id in a log is already useless.

vault write -f auth/approle/role/payments-api/secret-id

Key                   Value
---                   -----
secret_id             d8a3...e91f
secret_id_accessor    4c2b...77a0
secret_id_ttl         24h

The app logs in with both and gets a short-lived token:

vault write auth/approle/login \
    role_id="7b1c4e2a-9f3d-4a8e-b6c1-2d5f8e0a1b3c" \
    secret_id="d8a3...e91f"

Key                  Value
---                  -----
token                hvs.CAESI...
token_duration       1h
token_renewable      true
token_policies       ["default" "payments-api"]

That token dies in an hour unless the app renews it. The pattern that delivers the secret_id securely (a sidecar, a cloud instance identity, or Vault Agent) is its own topic, but the rule is simple: the role_id can live in config, the secret_id should be freshly minted and single-use.

Dynamic database credentials

This is the feature that changes how you think about secrets. Instead of one shared database password that every service knows, Vault creates a brand new database user for each request, with a short TTL, and deletes it when the lease expires.

Enable the database engine and point it at PostgreSQL:

vault secrets enable database

vault write database/config/orders-db \
    plugin_name="postgresql-database-plugin" \
    allowed_roles="orders-readonly" \
    connection_url="postgresql://{{username}}:{{password}}@db.internal:5432/orders?sslmode=require" \
    username="vault-admin" \
    password="$ROOT_DB_PASSWORD"

The vault-admin account is the only static credential, and it is a privileged account Vault uses to create and drop other users. Now define a role that says what a generated user is allowed to do:

vault write database/roles/orders-readonly \
    db_name="orders-db" \
    creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
      GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
    default_ttl="1h" \
    max_ttl="24h"

Ask for credentials:

vault read database/creds/orders-readonly

Key                Value
---                -----
lease_id           database/creds/orders-readonly/Qm9iY...
lease_duration     1h
lease_renewable    true
password           A1a-9Zx2Kp4Lq7Rt0Vn3
username           v-approle-orders-rea-x7Qd2bN9

That username did not exist a second ago. Run the command again and you get a different user with a different password. Each service instance, each request if you want, gets its own credentials. When the lease ends, Vault runs the revocation statement and the user is gone from PostgreSQL.

Here is why this matters in numbers. A static password sits valid until a human rotates it, which in practice means months or years. A dynamic credential with a one-hour TTL is useless to an attacker an hour after it leaks.

How long a leaked credential stays valid

The shrink in exposure window is the entire reason to run Vault. If you take one thing from this post, make it this section.

Encryption as a service with the transit engine

Sometimes you do not want to store a secret, you want to encrypt application data: a customer's tax ID, a token, a column in your database. The wrong move is to ship an AES key to every app and hope nobody loses it. The transit engine keeps the key inside Vault and exposes encrypt and decrypt operations. Your app sends plaintext and gets ciphertext back. It never sees the key.

vault secrets enable transit
vault write -f transit/keys/orders-pii

Encrypt some data (transit takes base64 input):

vault write transit/encrypt/orders-pii \
    plaintext=$(echo -n "4111-1111-1111-1111" | base64)

Key            Value
---            -----
ciphertext     vault:v1:8SDd4HCQ9p7Hf2bxN0kZ...
key_version    1

Store vault:v1:8SDd... in your database. To read it back:

vault write transit/decrypt/orders-pii \
    ciphertext="vault:v1:8SDd4HCQ9p7Hf2bxN0kZ..."

Key          Value
---          -----
plaintext    NDExMS0xMTExLTExMTEtMTExMQ==

Base64-decode that and you are back to the card number. The v1 prefix is the key version, which means you can rotate the key with vault write -f transit/keys/orders-pii/rotate and old ciphertext still decrypts while new writes use the fresh key. No key ever leaves Vault, so an app compromise leaks data the app could already see, not the key that protects all of it.

Least-privilege policies and the audit log

Tokens are only as safe as the policy attached to them. The payments-api policy referenced earlier should grant exactly what the service needs and nothing more:

# payments-api.hcl
# Read dynamic DB creds for the orders database.
path "database/creds/orders-readonly" {
  capabilities = ["read"]
}

# Encrypt and decrypt PII, but not manage or export the key.
path "transit/encrypt/orders-pii" {
  capabilities = ["update"]
}
path "transit/decrypt/orders-pii" {
  capabilities = ["update"]
}

vault policy write payments-api payments-api.hcl

Notice what is missing. No database/creds/orders-admin, no transit/keys/* management, no wildcard paths. If the payments token leaks, the attacker can read orders and decrypt PII for an hour, and that is the ceiling. When a request asks for something outside the policy, Vault refuses:

$ vault read database/creds/orders-admin
Error reading database/creds/orders-admin: Error making API request.
URL: GET https://vault-1.internal:8200/v1/database/creds/orders-admin
Code: 403. Errors:
* 1 error occurred:
    * permission denied

Turn on the audit log before you put anything real in Vault. It records every request and response (secrets are HMAC'd, not stored in clear) so you can answer "who read this secret and when" during an incident:

vault audit enable file file_path=/var/log/vault/audit.log

And when you do have an incident, dynamic secrets give you a clean kill switch. Revoke every credential a database role ever issued in one command:

vault lease revoke -prefix database/creds/orders-readonly

All revocation operations queued successfully!

Every dynamic user that role created gets dropped from the database. Try doing that with a shared password that lives in forty places.

Where to go next

You now have the shape of a real Vault setup: a sealed, persistent server; AppRole for machines; dynamic database credentials; transit for encryption; tight policies; and an audit trail. The static KV store is still there when you need it, but it should be the exception, not the default.

Concrete next steps:

  1. Replace one static database password with a dynamic role this week. Pick a low-risk read-only service and cut over. Seeing credentials expire on their own is what makes the model click.
  2. Stand up a 3-node Raft cluster, not a single server. One Vault node is a single point of failure for every secret you own. Run vault operator raft list-peers to confirm the cluster.
  3. Deploy Vault Agent to handle AppRole login and token renewal so your apps read a rendered file or env var instead of calling the Vault API directly.
  4. Set short TTLs and test revocation. Run vault lease revoke -prefix against a staging role and confirm the users vanish from your database. Know the command works before you need it at 2am.
  5. Ship the audit log to your SIEM so secret access shows up next to the rest of your security telemetry.

Start with step one. Turning a single forever-password into a one-hour credential is the smallest change that removes the largest class of secret leaks you have.