fastpai如何保护api文档的安全,防止恶意访问

fastpai如何保护api文档的安全,防止恶意访问

前言

在使用fastapi的时候,项目默认提供非常标准的api文档,默认路径包括:

  • /docs:查看Swagger UI形式的文档
  • /redocs:查看ReDoc形式的文档
  • /openapi.json:提供文档的具体信息,换句话说,只要有了这个json的内容,就可以知道关于api的全部细节,Swagger UIReDoc都是在此基础上提供了美观的UI的查看方式。

但有时候这种默认的设置会带来一个问题:api的安全性。

如果外界知道我们的api是什么样的结构,包括数据结构,参数形式,就更容易通过各种方式来破坏或者滥用api,在某些情况下我们不希望其他人看到api的具体信息,就需要将fastapi文档给隐藏起来,或者只给能看的人看。

主要方法列举

一类方法是在fastapi之外的,比如我们使用nginx来代理后端服务,那么就可以通过设置ip黑白名单,设置basic auth来阻止对上述三个路径的访问,这种方法虽然可以,但是不建议这样做————这样增加了系统的耦合性,如果哪一天fastapi路径变化了,还需要重新配置nginx,或者nginx配置错了,反而影响fastapi。

其实fastapi也有很多方式来让你实现安全的访问:

方法一:关门大吉法

如前所述,fastapi默认向外提供三个访问接口,这三个接口都是有对应的参数进行设置的:

1
2
3
4
5
6
7
8
9
from fastapi import FastAPI

app = FastAPI(
title=...,
description=...,
docs_url=..., # 默认的设置对应着 /docs
redoc_url=..., # 默认的设置对应着 /redocs
openapi_url=..., # 默认的设置对应着 /openapi.json
)

我们可以对这三个参数设置为None来完全关闭这三个接口。

好处是谁都看不到了,接口都给干没了,绝对安全,坏处就是自己人也被拒之门外,某些情况下是可以考虑这种方法的。

方法二:Basic Auth验证法

Basic Auth就是http基本验证,使用这种验证方式将要求你输入用户名和密码,然后将会以base64的形式将用户名+密码放在headers中的Authorization字段中,形式如下:

1
Authorization: Basic base64encode(username+":"+password)

好处是,基本上所有流行的网页浏览器都支持基本认证,使用起来非常简便。但是由于用户名和密码是明文传输,所以该方案创建在以下的假设的基础上,即:客户端和服务器主机之间的连接是安全可信的。特别是,如果没有使用SSL/TLS这样的传输层安全的协议,那么以明文传输的密钥和口令很容易被拦截。该方案也同样没有对服务器返回的信息提供保护。

如果你的网站已经使用了ssl加密,那么Basic Auth也是一种选择之一,总比完全开放要好吧?

那么我们的基本思路就是,要在fastapi的三个默认接口上,加上一层认证,或者只把最核心的/openapi.json给加上一层认证。

fastapi本身是不能说直接配置一个参数就实现这样的功能的,我们需要先屏蔽默认的三个接口,再自行添加上这三个接口,当然,在自行添加的时候,额外添加一层认证。

第一步:关闭默认接口

1
2
3
4
5
6
7
8
9
from fastapi import FastAPI, Depends

app = FastAPI(
title=...,
description=...,
docs_url=None,
redoc_url=None,
openapi_url=None,
)

第二步:自行定义三个接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from fastapi import Depends
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.openapi.docs import get_swagger_ui_html, get_redoc_html
from fastapi.openapi.utils import get_openapi

security = HTTPBasic()
username = 'test'
password = 'test_password'
OPENAPI_PATH = '/openapi.json' # 这里还可以定义成其他字符串,比如:hgdugsaphbaoibh.json


@app.get(OPENAPI_PATH, include_in_schema=False) # include_in_schema=False是不让这个接口出现在文档里面
async def get_documentation(credentials: HTTPBasicCredentials = Depends(security)):
if credentials.username != username or credentials.password != password:
raise HTTPException(
status_code=401,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Basic"},
)
else:
return JSONResponse(get_openapi(title=app.title, version=app.version, routes=app.routes))


@app.get("/docs", include_in_schema=False) # include_in_schema=False是不让这个接口出现在文档里面
async def get_documentation(credentials: HTTPBasicCredentials = Depends(security)):
if credentials.username != username or credentials.password != password:
raise HTTPException(
status_code=401,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Basic"},
)
else:
return get_swagger_ui_html(openapi_url=OPENAPI_PATH, title="docs") # openapi_url一定要传入


@app.get("/redoc", include_in_schema=False)
async def get_documentation(credentials: HTTPBasicCredentials = Depends(security)):
if credentials.username != username or credentials.password != password:
raise HTTPException(
status_code=401,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Basic"},
)
else:
return get_redoc_html(openapi_url=OPENAPI_PATH, title="redoc") # openapi_url一定要传入,尤其是修改了默认地址的话

最重要的是要将/openapi.json这个接口保护起来,get_openapi(title=app.title, version=app.version, routes=app.routes)就是fastapi中生成openapi.json的方法,这个函数还有一些其他参数,也可以自行设置。

这样处理之后,再此访问的时候就会变成这个样子:

开启Basic Auth后访问的样子

方法三:其他的验证方式比如oauth2等

这些方式只是在方式2的基础上,将basic auth替换成更加安全的验证方式,怎么顺手怎么来,具体的建议直接查看文档,套用进来就行了。

有关链接:

https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/

https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/

其他

安全无小事,在我开发的过程中也看到过其他人的处理方式,发现有时候大家只处理/docs/redocs两个接口,却把最主要的openapi.json给忽略了,这属实是把锁安装到了窗户上,大门愣是虚掩着

希望我的经验可以帮助到更多的人。

作者

Haoran

发布于

2022-11-18

更新于

2022-11-18

许可协议

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×