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

推荐订阅源

N
News and Events Feed by Topic
T
The Exploit Database - CXSecurity.com
P
Palo Alto Networks Blog
T
Threat Research - Cisco Blogs
Cloudbric
Cloudbric
Recent Commits to openclaw:main
Recent Commits to openclaw:main
I
Intezer
Attack and Defense Labs
Attack and Defense Labs
P
Privacy International News Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
L
Lohrmann on Cybersecurity
C
Cybersecurity and Infrastructure Security Agency CISA
V2EX - 技术
V2EX - 技术
AWS News Blog
AWS News Blog
O
OpenAI News
L
LINUX DO - 最新话题
N
News | PayPal Newsroom
PCI Perspectives
PCI Perspectives
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Troy Hunt's Blog
Latest news
Latest news
D
Darknet – Hacking Tools, Hacker News & Cyber Security
A
Arctic Wolf
Spread Privacy
Spread Privacy
G
GRAHAM CLULEY
T
Tor Project blog
博客园_首页
Know Your Adversary
Know Your Adversary
有赞技术团队
有赞技术团队
S
Secure Thoughts
美团技术团队
Apple Machine Learning Research
Apple Machine Learning Research
爱范儿
爱范儿
T
Tailwind CSS Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
V
Visual Studio Blog
J
Java Code Geeks
Cisco Talos Blog
Cisco Talos Blog
Schneier on Security
Schneier on Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Security Affairs
Jina AI
Jina AI
人人都是产品经理
人人都是产品经理
雷峰网
雷峰网
宝玉的分享
宝玉的分享
量子位
Last Week in AI
Last Week in AI
月光博客
月光博客
罗磊的独立博客
S
SegmentFault 最新的问题

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
SSH Agent Forwarding vs ProxyJump: Why Agent Forwarding Is Dangerous and What to Use Instead
Mahafuzur Rahaman · 2026-05-31 · via DEV Community

Thousands of tutorials recommend ForwardAgent yes. Most of them don't tell you what it actually does to your security posture. Here's the full picture.


You need to SSH from your laptop to a bastion, then from the bastion to an internal server. You've seen the solution in a dozen tutorials:

Host bastion
    ForwardAgent yes

Enter fullscreen mode Exit fullscreen mode

It works. It's convenient. And it creates a security hole that could let anyone with root on the bastion impersonate you to every server your key unlocks — for as long as your session is open.

This isn't a theoretical risk. It's a well-documented attack vector with a name: SSH agent hijacking. And the fix — ProxyJump — has been available since 2017 and solves the same problem without the exposure.

This article explains exactly what agent forwarding does under the hood, why it's dangerous, when (if ever) it's acceptable, and how ProxyJump eliminates the need for it in the most common use case.


What SSH Agent Forwarding Actually Does

To understand the risk, you need to understand the mechanism.

The SSH Agent

ssh-agent is a background process that holds your decrypted private keys in memory. When you run ssh host, the SSH client asks the agent to perform cryptographic operations (signing challenges) on its behalf. Your private key never leaves the agent — the client just asks the agent to sign things.

Your SSH client  ──── "sign this challenge" ────►  ssh-agent
                 ◄─── "here's the signature" ─────  ssh-agent

Enter fullscreen mode Exit fullscreen mode

The agent communicates through a Unix socket, stored at a path like /tmp/ssh-agent.XXXXX/agent.12345. The environment variable SSH_AUTH_SOCK points to this socket.

What Forwarding Does

When you SSH to a remote server with ForwardAgent yes, SSH does something specific: it creates a new socket on the remote server that tunnels back to your local agent.

Remote server /tmp/ssh-XXXXX/agent.5678  ──► tunnel ──►  Your local ssh-agent

Enter fullscreen mode Exit fullscreen mode

On the remote server, SSH_AUTH_SOCK points to this forwarded socket. Any process on the remote server that connects to this socket gets routed back to your local agent.

This means: from the remote server's perspective, your local agent is present and available. When you run ssh db.internal from the bastion, the bastion's SSH client uses the forwarded socket, which calls back to your local agent, which signs the challenge with your private key.

It feels like your key is on the bastion. It effectively is — for the duration of your session.

The Attack

Here's the problem. Anyone with root access on the remote server can:

  1. Find all active forwarded agent sockets: ls /tmp/ssh-*/
  2. Impersonate any user with a forwarded socket by setting SSH_AUTH_SOCK to their socket path
  3. Use that socket to authenticate as that user to any server their key unlocks
# An attacker with root on your bastion runs:
ls /tmp/ssh-*/
# Finds: /tmp/ssh-AbCdEf/agent.1234

SSH_AUTH_SOCK=/tmp/ssh-AbCdEf/agent.1234 ssh ubuntu@db.internal
# Connected. As you. Using your key.

Enter fullscreen mode Exit fullscreen mode

The attacker never sees your private key. They don't need to. They use your agent — which does the signing for them.

This works for every server your key can reach, for as long as your SSH session to the bastion remains open. If you leave a session running overnight, that window stays open overnight.

What Makes This Worse

  • It requires root on the compromised server. Root access is more common than people expect: a container escape, a sudo misconfiguration, an unpatched privilege escalation vulnerability.
  • It's invisible. Your agent doesn't log that it was used. You get no notification.
  • It's transitive. If you forward your agent to server A, and server A forwards it to server B, anyone with root on either server can use your agent.
  • It affects all keys in your agent. Not just the key you used to connect — every key loaded in ssh-agent.

ProxyJump: The Right Solution

ProxyJump was designed specifically for multi-hop SSH. It solves the "connect through a bastion" problem without forwarding your agent.

How ProxyJump Works

Instead of giving the bastion access to your agent, ProxyJump tells your local SSH client to make two connections — both originating from your machine:

Your machine  ──TCP──►  Bastion:22  ──TCP tunnel──►  db.internal:22
              (full SSH)             (proxied through bastion TCP)

Enter fullscreen mode Exit fullscreen mode

Your SSH client connects to the bastion and immediately asks the bastion to open a TCP connection to the target server. The bastion acts as a dumb TCP proxy — it passes bytes back and forth but has no involvement in the authentication. Your local SSH client handles authentication to both the bastion and the target.

The critical difference: your agent never touches the bastion. The bastion's SSH server forwards TCP traffic; it never performs any authentication on your behalf.

Implementation

Host bastion
    HostName bastion.example.com
    User ubuntu
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes
    # Note: No ForwardAgent here

Host db.internal
    User ubuntu
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes
    ProxyJump bastion

Enter fullscreen mode Exit fullscreen mode

That's it. ssh db.internal connects through the bastion, authenticates to both with your local agent, and never forwards the agent to either server.

Multi-Hop

Host deep.internal
    ProxyJump bastion,internal-gateway.example.com

Enter fullscreen mode Exit fullscreen mode

Each hop is a TCP proxy. Your local SSH client authenticates to each server in sequence. No agent is forwarded anywhere in the chain.

Files and Commands

ProxyJump works seamlessly with all SSH-based tools:

scp -J bastion file.txt ubuntu@db.internal:/tmp/
rsync -avz -e "ssh -J bastion" local/ ubuntu@db.internal:remote/
sftp -J bastion ubuntu@db.internal

Enter fullscreen mode Exit fullscreen mode


Side-by-Side Comparison

Agent Forwarding ProxyJump
Mechanism Forwards agent socket to remote TCP proxy through intermediate host
Authentication location Remote server calls back to local agent Local client authenticates everywhere
Agent exposure Agent available on remote server Agent never leaves local machine
Root attack surface Root on bastion can use your agent Root on bastion sees TCP bytes only
Session window Risk exists for entire session duration No persistent risk
Multi-hop Requires forwarding through each hop Handled natively, comma-separated
SCP/rsync support Works (but risk applies) Works, no risk
Available since OpenSSH 1.x OpenSSH 7.3 (2016)

When Agent Forwarding Might Be Acceptable

Agent forwarding isn't always wrong. There are narrow cases where it's appropriate:

Interactive development servers you fully control
If you're the only user on a server, you own root, and it exists solely as your dev environment — forwarding your agent there is low risk. The threat model requires an attacker to have root on that server; if you are root and the server isn't shared, that threat is remote.

Legacy systems that don't support ProxyJump
OpenSSH 7.3+ supports ProxyJump. Systems running older versions may need ProxyCommand or agent forwarding as a workaround. This should be a migration trigger, not a permanent state.

When you explicitly need the agent on the remote machine
Legitimate use cases: cloning private Git repos on a remote server without copying keys, or running SSH commands from a remote machine that genuinely need to reach a third server you can't reach directly. Even here, minimize the exposure window (connect, do the operation, disconnect).

Mitigations If You Must Use Agent Forwarding

If you genuinely need it, minimize the risk:

# Load only the specific key needed, with a time limit
ssh-add -t 3600 ~/.ssh/id_ed25519_specific   # Expires in 1 hour

# Confirm what's in your agent before forwarding
ssh-add -l

# Remove all keys after you're done
ssh-add -D

Enter fullscreen mode Exit fullscreen mode

Use ssh-add -c to require confirmation for every agent operation:

ssh-add -c ~/.ssh/id_ed25519

Enter fullscreen mode Exit fullscreen mode

Every time the forwarded socket is used, you'll get a local prompt asking you to confirm. An attacker exploiting your forwarded agent would trigger this prompt — though if you're away from your machine, you might confirm it without thinking.

Lock down the forwarding to the minimum scope:

# Only forward to a specific, trusted host — not everything
Host trusted-dev-server
    HostName 10.0.0.50
    ForwardAgent yes

Host *
    ForwardAgent no   # Default: off

Enter fullscreen mode Exit fullscreen mode


Auditing and Detecting Agent Forwarding

Find Active Forwarded Sockets

On any server you're connected to:

# Your own forwarded socket
echo $SSH_AUTH_SOCK

# All agent sockets (if you have permission)
ls /tmp/ssh-*/

# As root: find all agent sockets across all users
find /tmp -name "agent.*" -type s 2>/dev/null

Enter fullscreen mode Exit fullscreen mode

Check Who's Using Your Agent

There's no built-in logging for agent use, but you can add it with ssh-agent wrappers or tools like ssh-audit-agent. A simpler approach: use hardware security keys (YubiKey, etc.) which require physical touch for each signing operation — a forwarded socket can't silently use the key without your physical presence.

On the Server: Restrict Agent Forwarding

If you administer the bastion or any intermediate server, disable forwarding for users who don't need it:

# /etc/ssh/sshd_config
AllowAgentForwarding no

Enter fullscreen mode Exit fullscreen mode

This is a global disable. If ProxyJump is your standard access pattern, there's no reason for the bastion to accept agent forwarding at all.


Migrating an Existing Setup

If your team currently uses agent forwarding for bastion access, migration to ProxyJump is straightforward and backward-compatible.

Step 1: Add ProxyJump to ~/.ssh/config

Host bastion
    HostName bastion.example.com
    User ubuntu
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes
    ForwardAgent no          # Explicitly disable

Host *.internal
    ProxyJump bastion
    User ubuntu
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes

Enter fullscreen mode Exit fullscreen mode

Step 2: Verify Target Server Keys Are in known_hosts

With agent forwarding, the bastion performed the second-hop authentication, so only the bastion's key was in your local known_hosts. With ProxyJump, your local client authenticates to every hop, so every server's key needs to be in your local known_hosts.

# Add target server keys through the proxy
ssh-keyscan -J bastion db.internal >> ~/.ssh/known_hosts

Enter fullscreen mode Exit fullscreen mode

Step 3: Disable Agent Forwarding on the Bastion

# /etc/ssh/sshd_config on bastion
AllowAgentForwarding no

Enter fullscreen mode Exit fullscreen mode

sudo systemctl restart ssh

Enter fullscreen mode Exit fullscreen mode

Step 4: Verify

# Connect to target — should work without agent forwarding
ssh db.internal

# Verify agent socket is not present on bastion
ssh bastion "echo \$SSH_AUTH_SOCK"
# Should be empty

Enter fullscreen mode Exit fullscreen mode


The Deeper Principle

The agent forwarding problem illustrates a broader security principle: convenience and security are often in tension, and convenience has a way of winning by default.

Agent forwarding feels seamless precisely because it's transparent. You never see the forwarded socket. You never see it being used. The attack would also be transparent — the attacker uses your agent, achieves their goal, and you see nothing.

ProxyJump is equally transparent from a user experience perspective. ssh db.internal works the same way. The difference is what's happening underneath — and what an attacker on an intermediate server can and cannot access.

When two solutions have the same user-facing behavior and one has a material security advantage, the choice should be obvious. The only reason agent forwarding persists is inertia: it's what tutorials written before 2017 recommended, and those tutorials are still being followed today.


Quick Reference

Replace this:

Host bastion
    ForwardAgent yes

Enter fullscreen mode Exit fullscreen mode

With this:

Host bastion
    ForwardAgent no

Host *.internal
    ProxyJump bastion

Enter fullscreen mode Exit fullscreen mode

And on the bastion server:

# /etc/ssh/sshd_config
AllowAgentForwarding no

Enter fullscreen mode Exit fullscreen mode

That's the migration. It takes five minutes and eliminates an entire category of attack surface.


Follow for more SSH security content. Next: ControlMaster and SSH connection multiplexing for faster deployments.