This is an automated email from the ASF dual-hosted git repository.
nic-6443 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new b5b50fa71 fix(oas-validator): verify the TLS certificate of the
spec_url host (#14146)
b5b50fa71 is described below
commit b5b50fa71fd4fc9e7ef6d3bd3eba793f603a4dd1
Author: Shreemaan Abhishek <[email protected]>
AuthorDate: Fri Nov 6 09:54:48 2026 +0800
fix(oas-validator): verify the TLS certificate of the spec_url host (#14146)
---
apisix/plugins/oas-validator.lua | 9 +-
docs/en/latest/plugins/oas-validator.md | 3 +-
docs/zh/latest/plugins/oas-validator.md | 3 +-
t/plugin/oas-validator3.t | 147 ++++++++++++++++++++++++++++++++
4 files changed, 154 insertions(+), 8 deletions(-)
diff --git a/apisix/plugins/oas-validator.lua b/apisix/plugins/oas-validator.lua
index dd3ec5ecd..5b20efebe 100644
--- a/apisix/plugins/oas-validator.lua
+++ b/apisix/plugins/oas-validator.lua
@@ -62,7 +62,7 @@ local schema = {
ssl_verify = {
description = "whether to verify SSL certificate when fetching
spec_url",
type = "boolean",
- default = false,
+ default = true,
},
timeout = {
description = "HTTP request timeout in milliseconds for fetching
spec_url",
@@ -156,7 +156,7 @@ local function fetch_and_compile(conf)
local params = {
method = "GET",
- ssl_verify = conf.ssl_verify or false,
+ ssl_verify = conf.ssl_verify,
}
if conf.spec_url_request_headers then
params.headers = conf.spec_url_request_headers
@@ -198,6 +198,8 @@ function _M.check_schema(conf, schema_type)
return false, err
end
+ core.utils.check_tls_bool({"ssl_verify"}, conf, plugin_name)
+
if conf.spec and not secret.is_secret_ref(conf.spec) then
local _, decode_err = core.json.decode(conf.spec)
if decode_err then
@@ -223,8 +225,7 @@ local function get_validator(conf)
end
local lrucache = get_spec_url_lrucache()
- local ssl_verify = conf.ssl_verify or false
- local cache_key = conf.spec_url .. "#ssl_verify=" .. tostring(ssl_verify)
+ local cache_key = conf.spec_url .. "#ssl_verify=" ..
tostring(conf.ssl_verify)
if conf.spec_url_request_headers then
local sorted_keys = {}
for k in pairs(conf.spec_url_request_headers) do
diff --git a/docs/en/latest/plugins/oas-validator.md
b/docs/en/latest/plugins/oas-validator.md
index 9cfe9dd5a..bc2ede3ea 100644
--- a/docs/en/latest/plugins/oas-validator.md
+++ b/docs/en/latest/plugins/oas-validator.md
@@ -47,7 +47,7 @@ The OpenAPI spec can be provided as an inline JSON string or
fetched from a remo
| spec | string | No* | | | Inline OpenAPI 3.x specification in JSON format.
Required if `spec_url` is not set. |
| spec_url | string | No* | | `^https?://` | URL to fetch the OpenAPI
specification from. Required if `spec` is not set. |
| spec_url_request_headers | object | No | | | Custom HTTP request headers
sent when fetching `spec_url`. Useful for authenticated specification
endpoints. |
-| ssl_verify | boolean | No | false | | Whether to verify the TLS certificate
when fetching `spec_url`. |
+| ssl_verify | boolean | No | true | | Whether to verify the TLS certificate
when fetching `spec_url`. Disabling it lets an on-path attacker serve a
permissive specification and receive any credential configured in
`spec_url_request_headers`. |
| timeout | integer | No | 10000 | [1000, 60000] | HTTP request timeout in
milliseconds for fetching `spec_url`. |
| verbose_errors | boolean | No | false | | When `true`, include detailed
validation error messages in the response body. |
| skip_request_body_validation | boolean | No | false | | Skip validation of
the request body. |
@@ -147,7 +147,6 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
"plugins": {
"oas-validator": {
"spec_url": "https://petstore3.swagger.io/api/v3/openapi.json",
- "ssl_verify": false,
"verbose_errors": true
}
},
diff --git a/docs/zh/latest/plugins/oas-validator.md
b/docs/zh/latest/plugins/oas-validator.md
index 4a05f3985..860ad306a 100644
--- a/docs/zh/latest/plugins/oas-validator.md
+++ b/docs/zh/latest/plugins/oas-validator.md
@@ -46,7 +46,7 @@ OpenAPI 规范可以以内联 JSON 字符串的形式提供,也可以从远程
| spec | string | 否* | | | 内联 OpenAPI 3.x 规范(JSON 格式)。未设置 `spec_url` 时必填。 |
| spec_url | string | 否* | | `^https?://` | 获取 OpenAPI 规范的 URL。未设置 `spec` 时必填。
|
| spec_url_request_headers | object | 否 | | | 获取 `spec_url` 时附带的自定义 HTTP
请求头,适用于需要鉴权的规范接口。 |
-| ssl_verify | boolean | 否 | false | | 获取 `spec_url` 时是否校验 TLS 证书。 |
+| ssl_verify | boolean | 否 | true | | 获取 `spec_url` 时是否校验 TLS
证书。关闭后,中间人可以返回宽松的规范,并拿到 `spec_url_request_headers` 中配置的凭证。 |
| timeout | integer | 否 | 10000 | [1000, 60000] | 获取 `spec_url` 的 HTTP
请求超时时间(毫秒)。 |
| verbose_errors | boolean | 否 | false | | 为 `true` 时,在响应体中返回详细的校验错误信息。 |
| skip_request_body_validation | boolean | 否 | false | | 跳过请求体校验。 |
@@ -146,7 +146,6 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
"plugins": {
"oas-validator": {
"spec_url": "https://petstore3.swagger.io/api/v3/openapi.json",
- "ssl_verify": false,
"verbose_errors": true
}
},
diff --git a/t/plugin/oas-validator3.t b/t/plugin/oas-validator3.t
index 8f457e4e1..d17ba6a14 100644
--- a/t/plugin/oas-validator3.t
+++ b/t/plugin/oas-validator3.t
@@ -84,6 +84,21 @@ add_block_preprocessor(sub {
}
}
}
+
+ # serves a certificate that is not in the trust store
+ server {
+ listen 1972 ssl;
+ ssl_certificate cert/test2.crt;
+ ssl_certificate_key cert/test2.key;
+ location /spec.json {
+ content_by_lua_block {
+ local file = io.open("t/spec/spec.json", "r")
+ local content = file:read("*a")
+ file:close()
+ ngx.print(content)
+ }
+ }
+ }
_EOC_
$block->set_value("http_config", $http_config);
@@ -1163,3 +1178,135 @@ live: 51
--- response_body
fetches: 2
--- timeout: 15
+
+
+
+=== TEST 31: ssl_verify defaults to true
+--- config
+ location /t {
+ content_by_lua_block {
+ local plugin = require("apisix.plugins.oas-validator")
+ local conf = {spec_url = "https://127.0.0.1:1972/spec.json"}
+ local ok, err = plugin.check_schema(conf)
+ if not ok then
+ ngx.say(err)
+ return
+ end
+ ngx.say("ssl_verify: ", conf.ssl_verify)
+ }
+ }
+--- response_body
+ssl_verify: true
+
+
+
+=== TEST 32: opting out of ssl_verify is logged
+--- config
+ location /t {
+ content_by_lua_block {
+ local plugin = require("apisix.plugins.oas-validator")
+ local ok, err = plugin.check_schema({
+ spec_url = "https://127.0.0.1:1972/spec.json",
+ ssl_verify = false,
+ })
+ if not ok then
+ ngx.say(err)
+ return
+ end
+ ngx.say("done")
+ }
+ }
+--- response_body
+done
+--- error_log
+Keeping ssl_verify disabled in oas-validator configuration is a security risk
+
+
+
+=== TEST 33: create route with an https spec_url served by an untrusted
certificate
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin")
+ local code, body = t.test('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "uri": "/*",
+ "plugins": {
+ "oas-validator": {
+ "spec_url": "https://127.0.0.1:1972/spec.json"
+ }
+ },
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:1970": 1
+ }
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- response_body
+passed
+
+
+
+=== TEST 34: the spec fetch fails because the certificate is not trusted
+--- request
+GET /api/v3/pet/1
+--- error_code: 500
+--- response_body_like: failed to parse openapi spec
+--- error_log
+failed to fetch spec from URL
+
+
+
+=== TEST 35: ssl_verify false fetches the same spec_url
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin")
+ local code, body = t.test('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "uri": "/*",
+ "plugins": {
+ "oas-validator": {
+ "spec_url": "https://127.0.0.1:1972/spec.json",
+ "ssl_verify": false
+ }
+ },
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:1970": 1
+ }
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- response_body
+passed
+
+
+
+=== TEST 36: request validation works with ssl_verify disabled
+--- request
+POST /api/v3/pet
+{"id": 10, "name": "doggie", "category": {"id": 1, "name": "Dogs"},
"photoUrls": ["string"], "tags": [{"id": 1, "name": "tag1"}], "status":
"available"}
+--- more_headers
+Content-Type: application/json
+--- no_error_log
+[error]