fix(security): strip Azure HTTP headers from thread config to prevent IP leak
Deploy LangGraph Server to Azure Web App / build-and-deploy (push) Failing after 16s
Deploy LangGraph UI to Azure Static Web Apps / build-and-deploy (push) Failing after 43s

The LangGraph API server injects all x-* request headers into
thread.config.configurable, which gets persisted and exposed via
POST /threads/search. This leaked Azure-injected headers (x-forwarded-for,
x-client-ip, x-arr-ssl, etc.) to any user.

Adds a Hono middleware (http.app) that strips sensitive infrastructure
headers at the HTTP layer before they reach the LangGraph API routes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
gongzhiyong
2026-04-12 20:48:06 +08:00
co-authored by Claude Sonnet 4.6
parent 569e157f25
commit 60735de884
4 changed files with 100 additions and 10 deletions
+3
View File
@@ -6,6 +6,9 @@
"ui": {
"agent": "./src/agent-uis/index.tsx"
},
"http": {
"app": "./src/agent/http.ts:app"
},
"env": ".env",
"dependencies": ["."]
}
+1
View File
@@ -84,6 +84,7 @@
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.18",
"globals": "^15.14.0",
"hono": "^4.12.12",
"prettier": "^3.5.2",
"tailwind-scrollbar": "^4.0.1",
"tailwindcss": "^4.0.6",
+13 -10
View File
@@ -201,6 +201,9 @@ importers:
globals:
specifier: ^15.14.0
version: 15.15.0
hono:
specifier: ^4.12.12
version: 4.12.12
prettier:
specifier: ^3.5.2
version: 3.5.2
@@ -2397,8 +2400,8 @@ packages:
highlightjs-vue@1.0.0:
resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==}
hono@4.7.2:
resolution: {integrity: sha512-8V5XxoOF6SI12jkHkzX/6aLBMU5GEF5g387EjVSQipS0DlxWgWGSMeEayY3CRBjtTUQYwLHx9JYouWqKzy2Vng==}
hono@4.12.12:
resolution: {integrity: sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==}
engines: {node: '>=16.9.0'}
html-url-attributes@3.0.1:
@@ -4519,13 +4522,13 @@ snapshots:
'@google/generative-ai@0.21.0': {}
'@hono/node-server@1.13.8(hono@4.7.2)':
'@hono/node-server@1.13.8(hono@4.12.12)':
dependencies:
hono: 4.7.2
hono: 4.12.12
'@hono/zod-validator@0.2.2(hono@4.7.2)(zod@3.24.2)':
'@hono/zod-validator@0.2.2(hono@4.12.12)(zod@3.24.2)':
dependencies:
hono: 4.7.2
hono: 4.12.12
zod: 3.24.2
'@humanfs/core@0.19.1': {}
@@ -4613,8 +4616,8 @@ snapshots:
'@langchain/langgraph-api@0.0.30(@langchain/core@0.3.45(openai@4.95.1(zod@3.24.2)))(@langchain/langgraph-checkpoint@0.0.17(@langchain/core@0.3.45(openai@4.95.1(zod@3.24.2))))(@langchain/langgraph-sdk@0.0.73(@langchain/core@0.3.45(openai@4.95.1(zod@3.24.2)))(react@19.0.0))(@langchain/langgraph@0.2.64(@langchain/core@0.3.45(openai@4.95.1(zod@3.24.2)))(react@19.0.0)(zod-to-json-schema@3.24.3(zod@3.24.2)))(openai@4.95.1(zod@3.24.2))(typescript@5.7.3)':
dependencies:
'@babel/code-frame': 7.26.2
'@hono/node-server': 1.13.8(hono@4.7.2)
'@hono/zod-validator': 0.2.2(hono@4.7.2)(zod@3.24.2)
'@hono/node-server': 1.13.8(hono@4.12.12)
'@hono/zod-validator': 0.2.2(hono@4.12.12)(zod@3.24.2)
'@langchain/core': 0.3.45(openai@4.95.1(zod@3.24.2))
'@langchain/langgraph': 0.2.64(@langchain/core@0.3.45(openai@4.95.1(zod@3.24.2)))(react@19.0.0)(zod-to-json-schema@3.24.3(zod@3.24.2))
'@langchain/langgraph-checkpoint': 0.0.17(@langchain/core@0.3.45(openai@4.95.1(zod@3.24.2)))
@@ -4624,7 +4627,7 @@ snapshots:
dedent: 1.5.3
dotenv: 16.4.7
exit-hook: 4.0.0
hono: 4.7.2
hono: 4.12.12
langsmith: 0.2.15(openai@4.95.1(zod@3.24.2))
open: 10.1.0
semver: 7.7.1
@@ -6308,7 +6311,7 @@ snapshots:
highlightjs-vue@1.0.0: {}
hono@4.7.2: {}
hono@4.12.12: {}
html-url-attributes@3.0.1: {}
+83
View File
@@ -0,0 +1,83 @@
/**
* Custom HTTP middleware for LangGraph API server.
*
* Strips sensitive HTTP headers (injected by Azure Front Door / App Service)
* before they reach the LangGraph API routes. Without this, headers like
* x-forwarded-for, x-client-ip, x-arr-ssl etc. get persisted into
* thread.config.configurable and are exposed via POST /threads/search.
*
* Registered in langgraph.json as: { "http": { "app": "./src/agent/http.ts:app" } }
*/
import { Hono } from "hono";
/**
* Headers injected by Azure infrastructure that must NOT leak into
* config.configurable. The LangGraph API server copies every `x-*`
* header (except x-api-key / x-tenant-id / x-service-key) into the
* run config, so we strip them at the HTTP layer.
*/
const SENSITIVE_HEADER_PREFIXES = [
"x-forwarded-",
"x-client-",
"x-arr-",
"x-waws-",
"x-original-",
"x-site-",
"x-azure-",
"x-ms-",
"x-appservice-",
"x-liveupgrade",
] as const;
const SENSITIVE_HEADER_EXACT = new Set([
"x-client-ip",
"x-client-port",
"x-forwarded-for",
"x-forwarded-host",
"x-forwarded-proto",
"x-forwarded-port",
"x-forwarded-scheme",
"x-forwarded-tlsversion",
"x-arr-ssl",
"x-arr-log-id",
"x-waws-unencoded-url",
"x-original-url",
"x-original-host",
"x-site-deployment-id",
"x-azure-requestchainv2",
"x-azure-socketip",
"x-azure-ref",
"x-azure-fdid",
"x-azure-clientip",
"x-real-ip",
"client-ip",
"disguised-host",
"was-default-hostname",
"max-forwards",
"user-agent",
]);
function isSensitiveHeader(key: string): boolean {
const lower = key.toLowerCase();
if (SENSITIVE_HEADER_EXACT.has(lower)) return true;
for (const prefix of SENSITIVE_HEADER_PREFIXES) {
if (lower.startsWith(prefix)) return true;
}
return false;
}
export const app = new Hono();
app.use("*", async (c, next) => {
// Collect header names to delete (can't mutate while iterating)
const toDelete: string[] = [];
for (const [key] of c.req.raw.headers.entries()) {
if (isSensitiveHeader(key)) {
toDelete.push(key);
}
}
for (const key of toDelete) {
c.req.raw.headers.delete(key);
}
await next();
});