readonly仅防用户键盘输入和删除,无法防御xss、csrf等脚本注入攻击;它不拦截粘贴、拖拽、js赋值或开发者工具修改,且值照常提交,本质是ui提示而非安全机制。

readonly 属性根本不能防脚本注入
它对 XSS、CSRF 或任何脚本注入类攻击完全无效。readonly 只影响用户在页面上的键盘输入行为,不拦截 document.getElementById("x").value = "恶意脚本" 这类 DOM 操作,更不阻止通过 fetch 直接伪造请求提交篡改数据。
常见误判场景包括:把订单金额字段设为 readonly 就认为“金额安全了”,结果攻击者用控制台一行代码改掉值再点提交——后端若没校验,就直接入库。
- readonly 不过滤、不解析、不转义输入内容,它只是禁用部分交互
- 所有前端限制(包括
disabled、pattern、required)都可被绕过 - 真正防注入必须靠后端:输入验证 + 参数化查询 + 输出转义
readonly 防用户篡改的边界在哪
它只在“普通用户不打开开发者工具”的前提下起作用,本质是 UI 层的友好提示,不是防护机制。
具体能拦住的行为只有:keydown、keypress、delete、backspace 和光标选中后的键盘覆盖;其余全部放行:
- 右键 → 粘贴(
paste事件未被拦截) - 拖拽文本到输入框(
drop事件未被拦截) -
input.value = "xxx"或input.setAttribute("value", "xxx") - 开发者工具里删掉
readonly属性,立刻可编辑
所以它适合的场景非常明确:防止用户手误改错,而不是对抗恶意行为。
哪些 input 类型加 readonly 才生效
浏览器只对特定类型解析 readonly 属性,其他类型写了等于没写。
✅ 生效类型:text、password、email、tel、url、search、date、number、datetime-local、textarea
❌ 无效类型:checkbox、radio、file、hidden、select、button、range
特别注意:<select></select> 加了 readonly 浏览器直接忽略,必须用 disabled 或 JS 控制 onchange 阻断,但后者仍可能被绕过。
和 disabled 混用时最常丢的字段值
如果同时写 <input readonly disabled>,disabled 会完全压制 readonly 的语义:该字段既不可编辑,也不会出现在 FormData、serializeArray() 或 URL 查询参数中。
典型后果就是后端收不到关键字段,比如 user_id 或 order_no,导致 400 错误或空指针异常。
- 需要值参与校验或业务逻辑?→ 必须用
readonly,绝不能加disabled - 字段纯属展示占位、不参与流程?→ 才考虑
disabled - 动态控制时,优先用
element.removeAttribute("readonly")而非element.readOnly = false,尤其在 Safari 15 及更早版本中后者不可靠
真正容易被忽略的点是:readonly 字段依然可聚焦、可复制、可选中——这种“半锁定”状态,恰恰是它设计意图的体现,也是很多人误以为“没起作用”的原因。











