- 开发工具
- 代码生成
- API设计
【免费下载链接】swagger-codegen
swagger-codegen contains a template-driven engine to generate documentation, API clients and server stubs in different languages by parsing your OpenAPI / Swagger definition.
导读
本文围绕仓库中的samples/client/petstore-security-test/python示例,深入讲解 Swagger Codegen(OpenAPI 2.0 时代的模板驱动代码生成器)如何从一份刻意塞满特殊字符与代码注入攻击载荷的 Swagger 规格文件,生成一套结构完整、可安装、可调用的 Python API 客户端。读者将掌握该安全测试示例的定位与生成物结构、Python 客户端的安装方式(pip / setuptools)、FakeApi端点调用方法、保留字模型的处理机制,以及 API Key 与 OAuth 两种认证方案的生成形态,并理解"特殊字符贯穿整条生成链路"这一测试设计背后的安全含义。
一、示例定位:为什么需要一个"充满特殊字符"的 Petstore 规格
仓库中绝大多数样例都基于常规的petstore规格(见 fixtures/immutable/specifications/v2/petstore.json),而petstore-security-test是专门用于压力测试代码生成器对特殊字符与代码注入攻击的处理能力的规格。这一点在 modules/swagger-codegen/src/test/resources/2_0/petstore-security-test.yaml 的info.description中声明得非常直白:
This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters:
" \ */ ' " =end -- \r\n \n \r
也就是说,这份规格不是给真实业务使用的,而是专门用来验证:当 Swagger 文档的标题、描述、操作名、参数名、认证信息乃至响应类型中混入*/、'、"、=end、\r\n、\n、\r等可能造成注释闭合、引号转义、换行注入的字符时,生成器输出的客户端代码是否仍然语法正确、结构可用。
整个样例配套的 Java 源码版本位于 modules/swagger-codegen/src/test/resources/2_0/petstore-security-test.yaml,它定义了如下"注毒"元素:
| 规格位置 | 注入内容 |
|---|---|
info.title/version/host/basePath | 1.0.0 */ ' " =end -- \r\n \n \r |
paths./fake.put.operationId | testCodeInject */ ' " =end -- \r\n \n \r |
paths./fake.put.parameters[0].name | test code inject */ ' " =end -- \r\n \n \r |
securityDefinitions.api_key.name | api_key */ ' " =end -- \r\n \n \r |
securityDefinitions.petstore_auth.scopes | 每个 scope 描述均带注入后缀 |
definitions.Return.properties.return | 属性名使用 Python/Java 保留字return |
本文所述的 Python 客户端就是由这条 YAML 经io.swagger.codegen.languages.PythonClientCodegen(README 中Build package字段明确标注)生成并提交到仓库中的完整产物。
二、生成物的目录结构与代码骨架
生成的 Python 客户端位于 samples/client/petstore-security-test/python,整体是一个标准的 setuptools 工程:
petstore-security-test/python/ ├── docs/ # 端点与方法级文档(FakeApi.md、ModelReturn.md) ├── petstore_api/ │ ├── api/ # 新式目录:fake_api.py │ ├── apis/ # 兼容目录:fake_api.py │ ├── models/ # 数据模型:model_return.py │ ├── __init__.py │ ├── api_client.py # 底层 HTTP 调用封装 │ ├── configuration.py # 配置对象:host、认证、SSL、日志、代理 │ └── rest.py # REST 层 ├── test/ # unittest 用例(test_fake_api.py、test_model_return.py) ├── git_push.sh # 一键推送到 Git 仓库的辅助脚本 ├── requirements.txt # 运行依赖 ├── setup.py # 打包与安装入口 ├── test-requirements.txt └── tox.ini # tox 多版本测试配置其中 petstore_api/configuration.py 是客户端运行时的核心配置对象,默认host直接继承了规格中被注入的地址:
self.host = "https://petstore.swagger.io */ ' \" =end -- \\r\\n \\n \\r/v2 */ ' \" =end -- \\r\\n \\n \\r"这正体现了该样例的设计意图:连默认服务地址都带着攻击载荷,用于验证生成器不会在配置类里产生语法错误。配置对象还提供了api_key/api_key_prefix/access_token/verify_ssl/ssl_ca_cert/proxy/debug/connection_pool_maxsize(默认cpu_count() * 5)等常用可调项,并实现了get_api_key_with_prefix()与auth_settings()两个认证辅助方法。
三、环境要求与安装
README 明确给出运行环境:
Requirements.Python 2.7 and 3.4+
即该生成版本同时兼容 Python 2.7 与 Python 3.4+,这与生成的代码大量使用six兼容库(如from six import iteritems)保持一致。依赖清单可以从 setup.py 的REQUIRES中看到:
REQUIRES = [ "certifi>=2017.4.17", "python-dateutil>=2.1", "six>=1.10", "urllib3>=1.23" ]3.1 方式一:pip 直接安装(适用于托管在 Git 仓库的场景)
pip install git+https://github.com/GIT_USER_ID/GIT_REPO_ID.git如无写权限,需要提权执行:
sudo pip install git+https://github.com/GIT_USER_ID/GIT_REPO_ID.git(GIT_USER_ID/GIT_REPO_ID为生成模板中的占位符,实际使用时替换为发布该包的真实仓库地址。)
安装完成后即可导入:
import petstore_api3.2 方式二:setuptools 本地安装
前提是已安装 Setuptools,然后:
python setup.py install --user # 仅当前用户 sudo python setup.py install # 为所有用户安装同样以import petstore_api完成导入。从 setup.py 可以看到包名为petstore-api、版本1.0.0,install_requires与上述REQUIRES一致,long_description中同样带入了被注入的info.description文本(以# noqa: E501抑制行长度告警)——注入字符一路渗透到打包元数据中仍不破坏语法,这正是该测试样例要证明的健壮性。
四、快速开始:调用 FakeApi 的注入测试端点
README 给出的入门代码完整如下(configuration需提前定义,通常为petstore_api.Configuration()实例):
from __future__ import print_function import time import petstore_api from petstore_api.rest import ApiException from pprint import pprint # create an instance of the API class api_instance = petstore_api.FakeApi(petstore_api.ApiClient(configuration)) test_code_inject____end____rn_n_r = 'test_code_inject____end____rn_n_r_example' # str | To test code injection */ ' \" =end -- \\r\\n \\n \\r (optional) try: # To test code injection */ ' \" =end -- \\r\\n \\n \\r api_instance.test_code_inject____end__rn_n_r(test_code_inject____end____rn_n_r=test_code_inject____end____rn_n_r) except ApiException as e: print("Exception when calling FakeApi->test_code_inject____end__rn_n_r: %s\n" % e)4.1 方法名的"净化"过程
规格中的operationId是testCodeInject */ ' " =end -- \r\n \n \r。由于这一串字符无法直接成为合法的 Python 标识符,生成器将其清洗为合法的 snake_case 方法名:
testCodeInject */ ' " =end -- \r\n \n \r ↓ PythonClientCodegen 标识符清洗 test_code_inject____end__rn_n_r对应的参数名test code inject */ ' " =end -- \r\n \n \r也被清洗为test_code_inject____end____rn_n_r。读者可以在 petstore_api/apis/fake_api.py 中核对这两个名字,它们分别出现在def test_code_inject____end__rn_n_r(self, **kwargs)的方法签名与all_params = ['test_code_inject____end____rn_n_r']的参数白名单中。
4.2 底层请求是如何构造的
继续阅读 fake_api.py 的test_code_inject____end__rn_n_r_with_http_info实现,可以看到特殊字符在请求构造层仍然被原样保留:
# formData 参数的原始名称就是被注入的名字 form_params.append(('test code inject */ ' " =end -- \r\n \n \r', params['test_code_inject____end____rn_n_r'])) header_params['Accept'] = self.api_client.select_header_accept(['application/json', '*/ \" =end -- ']) header_params['Content-Type'] = self.api_client.select_header_content_type(['application/json', '*/ \" =end -- ']) return self.api_client.call_api('/fake', 'PUT', ...)这里揭示了一个关键事实:标识符清洗只发生在 Python 源码层面(方法名、变量名),而协议层面(HTTP 方法、路径、表单字段名、Content-Type/Accept 值)完整保留规格原始内容。代码生成器对两种上下文采取了不同的转义策略——这是理解整个安全测试样例的核心。
五、API 端点文档
README 的 "Documentation for API Endpoints" 部分记录了唯一的端点:
所有 URL 相对基地址:
https://petstore.swagger.io */ ' " =end -- \r\n \n \r/v2 */ ' " =end -- \r\n \n \r
| Class | Method | HTTP request | Description |
|---|---|---|---|
FakeApi | test_code_inject____end__rn_n_r | PUT/fake | To test code injection */ ' " =end -- \r\n \n \r |
方法级文档 docs/FakeApi.md 进一步补充了调用细节:
- 请求头(Content-Type):
application/json, */ \" =end -- - 请求头(Accept):
application/json, */ \" =end -- - 参数:
test_code_inject____end____rn_n_r(str,可选) - 返回类型:void(空响应体)
- 认证:无(该操作未绑定安全定义)
对应源码中的call_api('/fake', 'PUT', ...)与response_type=None(fake_api.py)完全吻合。
六、数据模型与保留字处理:ModelReturn
README 中 "Documentation For Models" 仅列出ModelReturn。它来源于规格定义:
definitions: Return: description: Model for testing reserved words */ ' " =end -- \r\n \n \r properties: return: type: integer format: int32模型类的实现位于 petstore_api/models/model_return.py。它专门用来测试保留字属性:属性名return是 Python 关键字,生成器将其映射为带下划线的_return:
swagger_types = { '_return': 'int' } attribute_map = { '_return': 'return' # 序列化回 JSON 时仍使用原始字段名 'return' }通过swagger_types(Python 侧类型)与attribute_map(协议侧 JSON 字段名)的双表映射,生成的模型既能在 Python 中合法表达,又能在序列化/反序列化时与规格字段精确对应。这与docs/ModelReturn.md中"Model for testing reserved words"的描述相互印证。
七、认证方式:api_key 与 petstore_auth
README 的 "Documentation For Authorization" 记录了规格中定义的两套安全方案,其源头同样是 petstore-security-test.yaml 的securityDefinitions:
7.1 api_key(API Key)
- 类型:API key
- 参数名:
api_key */ ' " =end -- \r\n \n \r - 位置:HTTP header
注意 API Key 的参数名本身也被注入了特殊字符。生成后,这一名字原样出现在 configuration.py 的auth_settings()字典里,作为 header 的 key 参与请求签名。调用方需要这样注入:
configuration = petstore_api.Configuration() configuration.api_key['api_key */ ' " =end -- \r\n \n \r'] = 'YOUR_API_KEY'7.2 petstore_auth(OAuth 2.0)
- 类型:OAuth
- Flow:implicit
- 授权 URL:
http://petstore.swagger.io/api/oauth/dialog - Scopes:
write:pets:modify pets in your account */ ' " =end -- \r\n \n \rread:pets:read your pets */ ' " =end -- \r\n \n \r
生成代码中以Authorization: Bearer <access_token>形式携带令牌,access_token同样在Configuration上配置。值得注意的是,README 端点表中test_code_inject____end__rn_n_r标注"无需认证"——尽管规格声明了安全定义,但/fake操作并未在security段引用它们,这属于规格本身的设计,读者在阅读时应注意区分"声明了安全方案"与"操作实际启用认证"两个概念。
八、自动化测试:验证生成代码的合法性
与客户端配套的单元测试位于 test/test_fake_api.py:
class TestFakeApi(unittest.TestCase): def setUp(self): self.api = petstore_api.apis.fake_api.FakeApi() def test_test_code_inject____end(self): """Test case for test_code_inject____end""" pass这些用例是典型的生成式 stub,其真正价值在于:只要这些文件能被 Python 解释器正常 import、能被 unittest 收集,就证明注入字符没有破坏包结构。同一逻辑也适用于 test/test_model_return.py。结合 tox.ini 可以看到该项目支持通过 tox 在 Python 2 与 Python 3 多环境下执行测试,进一步扩大了对"生成代码跨版本可编译"的验证覆盖。
九、总结:这个样例教会了我们什么
把 README 与生成源码对照阅读,可以提炼出petstore-security-testPython 客户端验证的四条核心安全能力:
- 标识符清洗:
operationId、参数名、属性名中的特殊字符与保留字被转换为合法标识符(test_code_inject____end__rn_n_r、_return),且文档、测试、实现三处的名字保持一致; - 字符串转义:文档字符串、注释、
long_description中的引号、反斜杠、换行均被正确转义,不会提前闭合字符串或注释块(可对比 setup.py 与 model_return.py 中成片出现、却依然可解析的注入文本); - 协议层保真:HTTP 方法、路径、表单字段名、header 值等协议元素保留规格原文,不受标识符清洗影响(见 fake_api.py);
- 双表映射:
swagger_types/attribute_map让"Python 合法名"与"协议原始名"解耦,为保留字属性提供通用解决范式。
对于正在评估或二次开发 Swagger Codegen(modules/swagger-codegen/src/main/java 为生成器本体源码)的读者,这个样例也是一个现成的回归测试靶场:只要用petstore-security-test.yaml重新生成 Python 客户端并跑通tox,就能快速确认模板改动没有破坏对特殊字符的防御能力。
- 开发工具
- 代码生成
- API设计
【免费下载链接】swagger-codegen
swagger-codegen contains a template-driven engine to generate documentation, API clients and server stubs in different languages by parsing your OpenAPI / Swagger definition.
相关推荐
swagger-codegen 代码注入防护实践:深入解读 petstore-security-test 生成的 Go 客户端 FakeApi 文档与实现
swagger codegen 代码注入防护实践:深入解读 petstore security test 生成的 Go 客户端 FakeApi 文档与实现 本篇
开发工具代码生成API设计Box2DSharp的10种关节完全指南:从RevoluteJoint到WheelJoint,打造小车与 Wrecking Ball
Box2DSharp的10种关节完全指南:从RevoluteJoint到WheelJoint,打造小车与 Wrecking Ball Box2DSharp 是经
开发工具代码生成API设计如何掌握 React Router 表单验证:从入门到精通的完整指南
如何掌握 React Router 表单验证:从入门到精通的完整指南 React Router 是 React 应用中实现声明式路由的核心库,而表单验证则是确保
开发工具代码生成API设计
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考