接口安全规范
为了提高接口安全,在编写对外接口时候,特别接口安全的控制,否则很容易出现下面的漏洞。
漏洞名称 | 漏洞危害 | 开发建议 |
---|---|---|
越权绕过 | 攻击者可能使用低权限用户请求高权限的接口,如用户添加按钮。 | 后端接口上添加权限控制注解,如: @RequiresPermissions("权限标记") |
文件上传 | 很多网站提供文件上传功能(包括图片上传),如果在服务器端没有对上传文件的类型、大小以及保存的路径及文件名进行严格限制,攻击者就很容易上传后门程序取得WebShell,从而控制服务器。 | 前端和后端使用黑名单、白名单严格限制上传文件的类型和大小。 |
功能权限检查
越权漏洞主要需要后端解决权限控制的问题,就需要解决登录控制和接口鉴权
- 登录控制,是逻辑上的细节控制,比如 单设备登录、登录环境检测、登录异常检测
- 接口鉴权根据不同的框架,有不同的出来方式
Shiro 框架
接口注解 | 权限描述 |
---|---|
@RequiresAuthentication | 要求当前Subject已经在session中验证通过(要求当前用户认证通过:subject.isAuthenticated() 结果为true) |
@RequiresUser | 验证用户是否被记忆,user有两种含义: 1) 一种是成功登录的(subject.isAuthenticated() 结果为true); 2) 另外一种是被记忆的(subject.isRemembered()结果为true)。 |
@RequiresGuest | 用户没有登录认证或被记住过,验证是否是一个guest的请求,与@RequiresUser完全相反。换言之,RequiresUser == !RequiresGuest。此时subject.getPrincipal() 结果为null. |
@RequiresPermissions | 验证用户是否具有一个或多个权限,该注解将会经常在项目中使用,如果不满足条件则抛出AuthorizationException异常 1) 是否具有某一权限 @RequiresPermission(“account:create”) 2) 是否具有多个权限 @RequiresPermission({“account:create”,”account:update”}) |
@RequiresRoles | 验证当前用户是否具有某角色,与验证权限类似 |
@RequiresRoles("super")
public string addAdmin(String username,String pwd){
return adminService.addAdmin(username,pwd);
}
Spring Security 框架
接口注解 | 权限描述 |
---|---|
@PreAuthorize(“authenticated”) | 要求当前用户认证通过 |
@PreAuthorize(“authenticated and hasAuthority(‘权限标记’) “) | 要求当前用户认证通过,且具备指定的权限 |
@PreAuthorize(“authenticated and hasAnyAuthority(‘权限标记’,’*’) “) | 要求当前用户认证通过,且具备指定的任意权限(管理员权限通常是*) |
@PreAuthorize(“authenticated and hasRole(‘角色编码1’) “) | 要求当前用户认证通过,且具备指定的角色 |
@PreAuthorize(“authenticated and hasAnyRole(‘角色编码1’,’角色编码2’) “) | 要求当前用户认证通过,且具备指定的任意角色 |
数据权限检查
数据越权漏洞的核心点在,使用低权限账号可以通过接口获取到不属于自己权限的数据,要求解决这个问题需要从两个方面进行控制。
- 请求参数检查,在
增删改查接口
增加数据权限的校验,防止用户操作非自己权限的数据 - 返回数据过滤,此方式依赖数据权限功能去给指定角色、用户设置某些数据对象的访问范围,以便在数据返回时通过动态修改筛选条件,达到数据过滤的目的。
请求参数检查
访问参数检查注解
接口注解 | 权限描述 |
---|---|
@RequiresDataPermissions(dataType = DataType.SCHOOL, message = “无权限操作该学校数据”) | 要求学校代码字段进行数据权限检查 |
/**
* 学校代码
*/
@ApiModelProperty(value = "学校代码", required = true)
@NotBlank(message = "学校代码不能为空")
@RequiresDataPermissions(dataType = DataType.SCHOOL, message = "无权限操作该学校数据")
private String schoolCode;
而服务内部有实现数据检查的本地逻辑实现:
/**
* 数据权限提供者
*/
@Component("dataScopeProvider")
public class MyDataScopeProvider implements DataScopeProvider {
@Override
public boolean hasPermissions(DataType dataType, Object data) {
String xxdm = SubjectUtils.getProfileString(UserProfiles.XXDM);
if (Objects.requireNonNull(dataType) == DataType.SCHOOL) {
return Objects.toString(data).equals(xxdm);
}
return Boolean.TRUE;
}
}
“文件上传” 检查
文件上传漏洞就需要前端和后端使用黑名单、白名单严格限制上传文件的类型和大小。
- 前端控制,根据业务需求,指定上传文件格式、大小限制
- 后端控制,在上传接口添加文件类型检测、大小限制
- 运维控制,在 Nginx 负载均衡限制上传文件大小
前端控制
前端需要在文件上传组件增加上传之前的回调夯实,进行文件格式、大小的校验!
handleBeforeUpload (file) {
// 控制的文件类型
let allFileType = ['xls', 'doc', 'zip', 'rar', 'xlsx', 'docx', 'jpg', 'png', 'bmp']
const isSuccessFile = allFileType.includes(file.name.split('.')[1])
const is50M = file.size / 1024 / 1024 < 50;
if (!isSuccessFile) {
Message.error('请上传正确的文件格式!');
}
//限制文件上传大小
if (!is50M) {
Message.error("上传文件大小不能超过 50MB!");
}
return isSuccessFile && is50M;
},
后端控制:Jakarta Bean Validation API
后端扩展了基于 jsr303 校验规范的文件校验实现,实现文件格式、大小、mine类型的校验!
属性注解 | 校验描述 | 示例 |
---|---|---|
@FileNotEmpty | 文件非空校验 | @FileNotEmpty(required = true, message = “上传文件必传”) |
@FileNotEmpty | 文件格式校验 | @FileNotEmpty(required = true, extensions = {“zip”, “rar”},message = “仅支持rar、zip两种格式压缩包格式”) |
@FileNotEmpty | 文件大小校验 | @FileNotEmpty(required = true, maxSize = “100MB”, message = “文件大小不得超过100MB。”) |
@FileNotEmpty | 文件非空、格式、大小校验 | @FileNotEmpty(required = true, extensions = {“zip”, “rar”}, maxSize = “100MB”, message = “仅支持rar、zip两种格式压缩包,且文件大小不得超过100MB。”) |
@FileNotEmpty | 文件Mime校验 | @FileNotEmpty(required = true, mimeTypes = {“application/x-rar-compressed”, “application/zip”},message = “仅支持 application/x-rar-compressed、application/zip 的MIME类型”) |
@ApiModel(value = "FileUploadDTO", description = "数据传输对象")
@Data
public class FileUploadDTO {
@ApiModelProperty(value = "上传文件")
@FileNotEmpty(required = true, message = "上传文件必传")
private MultipartFile file1;
@ApiModelProperty(value = "上传文件")
@FileNotEmpty(required = true, extensions = {"zip", "rar"}, message = "仅支持rar、zip两种格式压缩包格式")
private MultipartFile file2;
@ApiModelProperty(value = "上传文件")
@FileNotEmpty(required = true, maxSize = "100MB", message = "文件大小不得超过100MB。")
private MultipartFile file3;
@ApiModelProperty(value = "上传文件")
@FileNotEmpty(required = true, extensions = {"zip", "rar"}, maxSize = "100MB", message = "仅支持rar、zip两种格式压缩包,且文件大小不得超过100MB。")
private MultipartFile file4;
@ApiModelProperty(value = "上传文件")
@FileNotEmpty(required = true, mimeTypes = {"application/x-rar-compressed", "application/zip"},message = "仅支持 application/x-rar-compressed、application/zip 的MIME类型")
private MultipartFile file5;
}
Nginx上传文件大小限制
Nginx默认的上传文件大小是有限制的,一般为2MB,如果你要上传的文件超出了这个值,将可能上传失败。
如果要修改这个大小限制,只需要打开nginx.conf文件。
vim /etc/nginx/nginx.conf
找到”client_max_body_size”,如果没有的话,在http部分加上它。设置你想要修改的大小,例如:
client_max_body_size 30M
然后重启Nginx使配置生效即可。
service restart nginx
作者:Jeebiz 创建时间:2022-11-07 15:11
最后编辑:Jeebiz 更新时间:2024-11-14 21:58
最后编辑:Jeebiz 更新时间:2024-11-14 21:58