블로그 • Agent Security
AI Agent는 왜 JSON Schema만 믿으면 안 되나: 검증, 신뢰할 수 없는 입력, 권한 결정
도구 호출이 JSON Schema를 통과하는 것은 인자 모양이 맞다는 뜻이다. 메일을 보내거나, 파일을 바꾸거나, 밖으로 나가려면 JSON이 하나 더 필요하다. 권한 결정이다. decision이 allow가 아니면 그 호출은 멈춘다.
출력에 모양이 필요한 이유는 AI에 JSON Schema가 필요한 이유. 도구 서버가 많을 때 누가 보안을 맡는지는 MCP 보안과 권한. 이 글은 모양이 통과한 뒤의 결정만 다룬다. 허용 목록, grant, allow / deny / ask.
- Schema는 모양이 맞는지만 답한다
- 도구 이름은 먼저 허용 목록에 있어야 한다
- 권한 결정은 별도의 JSON이다
- grant에 그 능력이 없으면 실행하지 않는다
먼저 이것만: 모양이 맞는 호출도 실행하면 안 되는 호출일 수 있다. 아래는 Schema가 증명하는 것, 결정의 모양, 통과해도 거부하는 경우, 세 경우가 어디서 멈추는지의 순이다.
Schema가 증명하는 것
JSON Schema는 타입, 열거, 필수를 본다. 설명은 JSON Schema. 이 세션이 메일을 보내도 되는지, 이 도구 이름을 당신이 검토했는지는 증명하지 않는다.
{
"tool": "send_email",
"arguments": {
"to": "ops@example.com",
"subject": "status",
"body": "ok"
}
}
to, subject, body가 문자열이면 인자 Schema가 보는 것은 타입뿐이다. 이번 세션의 grant가 read_inbox만 있고 send가 없다는 것은 보지 못한다.
결정은 다른 JSON
MCP 도구 명세는 입력 검증과, 사람이 그 호출을 거부할 수 있게 할 것을 요구한다. 설명은 MCP Tools. 인자 JSON이 통과한 뒤 결정을 쓴다. toolAllowed는 이 도구 이름이 허용 목록에 있다는 뜻이다. grant는 이번 세션이 이미 가진 능력이다. decision은 allow, deny, ask만 된다.
{
"tool": "send_email",
"argumentsValid": true,
"toolAllowed": true,
"grant": ["read_inbox"],
"decision": "deny",
"reason": "grant_missing"
}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["tool", "argumentsValid", "toolAllowed", "decision", "reason"],
"properties": {
"tool": { "enum": ["send_email"] },
"argumentsValid": { "type": "boolean" },
"toolAllowed": { "type": "boolean" },
"grant": {
"type": "array",
"items": { "enum": ["read_inbox", "send"] }
},
"decision": { "enum": ["allow", "deny", "ask"] },
"reason": {
"enum": ["ok", "grant_missing", "tool_not_allowlisted", "arguments_invalid"]
}
}
}
이 Schema가 막는 것은 decision이 세 값 밖인 경우, argumentsValid가 불리언이 아닌 경우, decision이 없는 경우, grant에 목록 밖 능력이 있는 경우다. allow가 toolAllowed와 필요한 grant를 함께 요구한다는 것까지는 표현하지 못한다. 그 규칙은 앱에 남긴다.
통과해도 거부한다
위의 결정을 decision은 allow, reason은 ok로 바꾸고 grant는 read_inbox로 둔다. 이 Schema는 통과한다. 통과가 메일 발송 허가는 아니다. 앱의 규칙은 argumentsValid와 toolAllowed가 모두 true이고 grant에 send가 있을 때만 decision을 allow로 둘 수 있다는 것이다. 하나라도 없으면 deny나 ask로 되돌리고 도구를 호출하지 않는다.
| 앱의 규칙 | Schema가 보나 | 못 할 때 |
|---|---|---|
| allow일 때 argumentsValid는 true | 이 조합은 보지 못한다 | deny로 하고 reason은 arguments_invalid |
| allow일 때 toolAllowed는 true | 이 조합은 보지 못한다 | deny로 하고 reason은 tool_not_allowlisted |
| allow일 때 grant에 send가 있어야 한다 | 이 조합은 보지 못한다 | deny 또는 ask로 하고 reason은 grant_missing |
세 경우가 멈추는 곳
도구 설명의 주석은 힌트다. 명세는 신뢰할 수 없는 출처의 주석을 이미 적용된 안전 제어로 다루지 말라고 적는다. 설명은 도구 주석은 계약이 아니다. 호출을 실제로 멈추는 것은 자신의 Schema와 이 결정이다.
| 경우 | 모양은 어떨 수 있나 | 어디서 멈추나 |
|---|---|---|
| 인자 모양이 다르다 | Schema가 실패한다 | allow를 쓰지 않고 호출하지 않는다 |
| 도구가 허용 목록에 없다 | 인자는 여전히 맞을 수 있다 | toolAllowed는 false, decision은 deny |
| 세션에 그 능력이 없다 | 인자는 맞고 도구도 목록에 있다 | grant에 필요한 능력이 없어 deny 또는 ask |
신뢰할 수 없는 글이 모델에게 호출을 제안하게 하거나, 신뢰할 수 없는 도구 설명이 위험한 능력을 평범한 도구처럼 보이게 할 수 있다. 둘 다 모양이 맞는 JSON을 낼 수 있다. 방어는 그 글을 재현하는 것이 아니다. 도구를 먼저 허용 목록에 넣고, 호출을 Schema에 통과시킨 뒤, grant로 allow, deny, ask를 정한다. 사람은 여전히 거부할 수 있다.
다른 글과의 역할 나누기
| 왜 Schema가 필요한가 | MCP에서 누가 보안을 맡나 | 이 글 | |
|---|---|---|---|
| 답하는 것 | 출력에 왜 모양이 필요한지 | 서버가 많을 때 누가 책임지나 | 모양이 통과한 뒤 어떤 권한 JSON이 아직 필요한지 |
| 여기서 찾지 말 것 | Schema 입문을 다시 | 조직에서 누가 서명하는지 | 따라 할 수 있는 공격 절차 |
JSONNote에서 allow를 막는다
- 1동작을 고른다
JSON Schema를 연다. 작동하다 를 Schema로 확인하세요 로 바꾼다. 첫 칸에 권한 결정, 둘째 칸에 위 Schema를 붙인다. decision은 allow, deny, ask만. grant의 각 항목은 read_inbox 또는 send만.
- 2나쁜 데이터 넷을 부딪힌다
decision을 yes로 하거나, argumentsValid를 문자열 true로 하거나, decision을 지우거나, grant를 send_all로 한다. 이 넷은 실패한다. decision을 allow, reason을 ok로 하고 grant를 read_inbox로 두면 통과한다. 통과한 뒤에도 deny나 ask로 되돌리고 도구를 호출하지 않는다.
- 3deny와 allow를 비교한다
JSON 비교 Diff. 첫 칸에 deny 결정, 둘째 칸은 decision만 allow, reason만 ok로 바꾼다. grant는 read_inbox다. 둘 다 Schema는 통과한다. 발송을 막는 것은 앱 규칙이지 이번 모양 확인이 아니다.
자주 묻는 질문
Schema를 통과하면 도구를 실행해도 되나?
실행하면 안 된다. Schema가 증명하는 것은 인자 모양뿐이다. 도구가 허용 목록에 있는지, grant에 그 능력이 있는지를 아직 본다. 결정이 allow가 아니면 호출하지 않는다.
권한 결정의 Schema를 통과하면 allow로 써도 되나?
그것만으로는 allow가 아니다. decision이 allow, reason이 ok, grant가 read_inbox만이어도 모양은 맞다. 앱은 toolAllowed가 true인지, grant에 이번에 필요한 능력이 있는지를 아직 확인한다. 하나라도 없으면 allow가 아니다.
여기서 prompt injection과 tool poisoning은 무엇을 가리키나?
둘은 위험의 이름이지, 이 글이 재현하는 절차가 아니다. 하나는 신뢰할 수 없는 글이 모델에게 호출을 제안하게 하는 것이다. 다른 하나는 신뢰할 수 없는 도구 설명이 위험한 능력을 평범한 도구처럼 보이게 하는 것이다. 둘 다 모양이 맞는 JSON을 낼 수 있다. 멈추는 것은 허용 목록, Schema, grant이지, 그 글을 다시 쓰는 것이 아니다.
왜 JSON Schema가 필요한지 쓴 글과 같은 글인가?
같은 글이 아니다. 그 글들은 출력에 왜 모양이 필요한지 설명한다. 이 글은 모양이 이미 통과한 뒤에서 시작한다. 허용 목록과 grant는 decision을 여전히 deny나 ask로 만들 수 있다.
ask와 deny의 차이는 무엇인가?
deny는 이번에는 호출하지 않는다는 뜻이다. ask는 먼저 사람에게 인자를 보여주고, 사람이 거부하면 여전히 호출하지 않는다는 뜻이다. 둘 다 allow가 아니다. 사람이 루프 안에 있는 것은 MCP 도구 명세가 정한 거부 능력이다.
정리
JSON Schema는 모양이 틀린 도구 인자를 멈춘다. 권한 결정은 모양은 이미 맞지만 능력이 틀린 호출을 멈춘다. JSON 둘을 남겨야 실행에 문이 생긴다.
결론: Schema 통과는 모양이 맞다는 뜻만 가진다. 도구는 먼저 허용 목록에 넣는다. grant에는 이번 능력이 있어야 한다. decision이 allow가 아니면 호출하지 않는다. allow라도 grant가 부족하면 모양이 통과했어도 deny나 ask로 바꾼다.
다음: 권한 결정이 Schema를 통과하게 한 뒤, grant가 이번 allow를 허용하는지 본다.