伪造身份请求怎么办?看这篇就够了

news2024/9/21 16:49:24

您好, 如果喜欢我的文章或者想上岸大厂,可以关注公众号「量子前端」,将不定期关注推送前端好文、分享就业资料秘籍,也希望有机会一对一帮助你实现梦想

JWT身份鉴权方案,token会作为主要的鉴权方式来作为前后端通信校验的凭证,当该token被篡改或者直接被第三方拿到,就可以伪造该用户做一系列业务操作,是一种非常严重的安全漏洞。

那,JWT是啥?如果你不知道,借助AI的力量,看看它的解释:

JWT(JSON Web Tokens)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息作为JSON对象。由于其信息可以被验证和信任,JWT通常用于身份验证和信息交换。由于其较小的尺寸,它们特别适合于空间受限的环境,例如HTTP头部。

JWT主要由三个部分组成:

  1. Header(头部):Header通常由两部分组成,令牌的类型(即JWT)以及所使用的签名算法(如HMAC SHA256或RSA)。
  2. Payload(负载):其中包含所谓的Claims(声明),Claims是有关实体(通常是用户)和其他数据的声明。有三种类型的Claims:注册的声明、公共的声明和私有的声明。
  3. Signature(签名):为了获取这部分的签名,必须有编码过的header(头部)、编码过的payload(负载)、一个密钥,通过header中指定的算法进行签名。签名用于验证消息在传输途中没有被更改,并且,对于使用私有秘钥签署的token,还可以验证请求方是否为token的合法发布者。

JWT的使用流程一般如下:

  1. 用户使用各种方法(如用户名和密码)向认证服务器进行身份验证。
  2. 一旦服务器验证了用户的身份,它将生成一个JWT,并将其作为响应返回给用户。
  3. 用户随后将此令牌用作每次向服务器请求资源时的凭据。通常,这个令牌放在HTTP请求的Authorization头部中,带上’Bearer’前缀。
  4. 服务器会检验这个JWT的signature来确认其有效性,然后返回请求的数据。

JWT使用上述机制使得用户状态无需在服务器持久存储,从而更容易实现无状态、可扩展的应用,这在分布式微服务架构中特别有用。同时,JWT还提供了一种简单的方式,能够保证数据传输过程中的数据安全性和完整性。

image.png

介绍完JWT,那防止token伪造有没有什么好的方案?对于安全防御问题,核心策略就是“提升破解难度”,我们可以在客户端和服务端的通信中加入一个额外的约定签名,签名可以由当前时间戳信息、设备ID、日期、双方约定好的秘钥经过一些加密算法构造而成加密解密方式互相约定好,这样攻击者就算拿到了Token,还需要知道签名的生成规则和加密方式,才可以达成攻击,就像这样:

image.png

其实就相当于多了一层校验逻辑判断。

那接下来我们来实现一下吧。

我们先起个Nest项目,执行命令:

nest new jwt-project --strict

要做身份验证,那我们就准备好两个接口————logingetUserInfo

interface loginDTO {
  access_token: string;
  msg: string;
}

interface getUserInfoDTO {
  name: string;
  id: number;
}

@Controller()
export class AppController {
  constructor(
    private readonly appService: AppService,
  ) {}

  @Post('login')
  login(): loginDTO {
    return this.appService.login();
  }

  @Post('getUserInfo')
  getUserInfo(): getUserInfoDTO {
    return this.appService.getUserInfo();
  }
}

启动项目:

npm run start

浏览器上访问一下login接口,项目已经跑起来了

image.png

接口准备好了,我们实现login的逻辑,数据库这里就不准备了,对于用户信息判断我们直接在Nest维护一个数组来存储,并且引入NestJWT服务。

image.png

我们提前准备一下,首先安装依赖包。

npm i @nestjs/jwt

在模块中引入:

image.png

这样login接口就会基于user的信息生成一个JWT字符串秘钥返回,调用一次接口试一下:

image.png

有了Token就可以验完整的会话流程了,客户端通常我们会把Token存在本地存储,我们起一个客户端项目试一下,后端项目我们先开一下cors,支持跨域访问。

image.png

我们起一个前端项目,用umi.js来作为主框架,执行命令:

npx create-umi@latest

找一个页面文件,写入调用login的代码:

image.png

为了方便测试,页面刷新我们就用调一次login,更新一次Token,并把它存在本地存储中,接下来我们准备一下业务接口。

image.png

对于Token做了为空和真实性两层校验,我们测试一下,在前端页面先把Token给删了,调一次:

image.png

image.png

请求头没有authorization字段,后端报了401,符合预期,我们再篡改一下Token试试:

image.png

调一次getUserInfo,抛了NestJWT校验失败的异常:

image.png

OK,至此JWT的登录身份验证部分已经实现了,但现在有一个问题,我们的Token是暴露在浏览器的,如果我复制了这个Token,去postman调一下,是不是也可以调通?

测试一下。

image.png

毫无意外,翻车了。这也回到了文章标题,那如何解决这种情况呢?开头也提到了,加一层认证即可,在传Token的鉴权接口我们再多加一个sign字段,也放在header,这里的加解密我们用crypto-js,我们先实现下前端代码,首先安装依赖包。

npm i crypto-js --save-dev

然后在测试页面中引入,我们采用客户端加密sign、服务端解密sign的方案,因此我们实现个加密方法:

image.png

加密需要秘钥和秘钥偏移量,这两个参数前端和后端保持一致,这点非常重要。然后我们在getUserInfo接口中的请求头给它带上去。

image.png

加密的基准字符串采用调用时间、用户名、Token前6位组成,中间用冒号隔开,就像这样:

"1707449642869:jack:eyJhbG"

然后我们实现一下后端代码,同样也是先把依赖包安装,这是通用JS包,所以安装方式一样,然后在Nest中实现下解密验证的逻辑即可:

image.png

在后端我们加上解密的方法,同样基于密钥、密钥偏移量,与客户端一致,我们在业务接口中对于Token验证的基础上,再增加一层对于sign的验证,加强了接口安全,我们测试一下。

首先只传Token现在已经不行了:

image.png

后端默认使用第一个user来返回的,那前端我们传一个不一样的username作为sign,加上一层sign定制判断逻辑:

 if (userName !== 'aaa') {
    throw new UnauthorizedException('用户信息错误');
  }

再调一下,header都传上:

image.png

命中了我们加上的判断逻辑,看一眼打印出来解密的sign日志:

image.png

打印出来的用户名是jack,不是aaa,当然校验失败了,这下我们的伪造解决方案实现了,我们可以把这一层逻辑全部集成在一个中间件上。

封装一下代码,新增middlewares/auth.middleware.ts

import {
  Injectable,
  NestMiddleware,
  UnauthorizedException,
} from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { JwtService } from '@nestjs/jwt';
import * as CryptoJS from 'crypto-js';

@Injectable()
export class AuthMiddleware implements NestMiddleware {
  constructor(private readonly jwtService: JwtService) {}

  private readonly SECRET_KEY = CryptoJS.enc.Utf8.parse('3333e6e143439161'); //十六位十六进制数作为密钥
  private readonly SECRET_IV = CryptoJS.enc.Utf8.parse('e3bbe7e3ba84431a'); //十六位十六进制数作为密钥偏移量

  private decrypt(data: string): string {
    //解密
    const encryptedHexStr = CryptoJS.enc.Hex.parse(data);
    const str = CryptoJS.enc.Base64.stringify(encryptedHexStr);
    const decrypt = CryptoJS.AES.decrypt(str, this.SECRET_KEY, {
      iv: this.SECRET_IV,
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7,
    });
    const decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);
    return decryptedStr.toString();
  }
  use(req: Request, res: Response, next: NextFunction) {
    try {
      const token = req.headers['authorization'];
      const sign = req.headers['sign'];
      if (!token) {
        throw new UnauthorizedException('登录态失效,请重新登录');
      }
      if (!sign) {
        throw new UnauthorizedException('身份验证失败');
      }
      //   token校验
      this.jwtService.verify(token);
      //   sign校验
      const signRes = this.decrypt(sign as string);
      const [time, userName, tokenStr] = signRes.split(':');
      console.log('time:', time);
      console.log('userName:', userName);
      console.log('tokenStr:', tokenStr);
      if (userName === '' || time === '' || tokenStr === '') {
        throw new UnauthorizedException('身份验证失败');
      }
      if (userName !== 'aaa') {
        throw new UnauthorizedException('用户信息错误');
      }
      return next();
    } catch (error) {
      throw new UnauthorizedException(
        error.message || 'Unauthorized: Invalid token',
      );
    }
  }
}

然后在Nest中开启这个中间件,我们目前login接口不需要校验,所以只开getUserInfo接口。

image.png

再跑一遍,没问题,结束。

我们简单总结一下。

JWT是目前主流的会话鉴权方案,但是不做安全方案的情况下,直接把Token暴露在请求体中会引发伪造身份请求的问题。

我们通过在请求中多一层加解密的约定式校验在通信中增加了网络请求的安全性。

如果喜欢我的文章或者想上岸大厂,可以关注公众号「量子前端」,将不定期关注推送前端好文、分享就业资料秘籍,也希望有机会一对一帮助你实现梦想。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1441274.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux下的crontab定时执行任务命令详解

在LINUX中,周期执行的任务一般由cron这个守护进程来处理[ps -ef|grep cron]。cron读取一个或多个配置文件,这些配置文件中包含了命令行及其调用时间。 cron的配置文件称为“crontab”,是“cron table”的简写。 一、cron服务   cron是一个…

[职场] 进入大数据领域需要掌握哪些软件 #其他#职场发展#职场发展

进入大数据领域需要掌握哪些软件 学习大数据首先我们要学习Java语言和Linux操作系统,这两个是学习大数据的基础,学习的顺序不分前后。 Java 大家都知道Java的方向有JavaSE、JavaEE、JavaME,学习大数据要学习那个方向呢? 只需要学习Java的…

Oracle 面试题 | 19.精选Oracle高频面试题

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…

前后端通讯:前端调用后端接口的五种方式,优劣势和场景

Hi,我是贝格前端工场,专注前端开发8年了,前端始终绕不开的一个话题就是如何和后端交换数据(通讯),本文先从最基础的通讯方式讲起。 一、什么是前后端通讯 前后端通讯(Frontend-Backend Commun…

对象存储minio

参考Linux搭建免费开源对象存储 创建一个data目录 --address和--console-address是MinIO服务器启动命令中的两个参数,它们具有以下区别: --address参数:用于指定MinIO服务器监听的S3 API访问地址。S3 API是用于与MinIO进行对象存储操作的…

数据库管理-第148期 最强Oracle监控EMCC深入使用-05(20240208)

数据库管理148期 2024-02-08 数据库管理-第148期 最强Oracle监控EMCC深入使用-05(20240208)1 性能主页2 ADDM Spotlight3 实时ADDM4 数据库的其他5 主机总结 数据库管理-第148期 最强Oracle监控EMCC深入使用-05(20240208) 作者&am…

Red Hat安装Red Hat OpenShift Local

文章目录 环境安装需求硬件操作系统软件包 安装 使用Red Hat OpenShift Local预设置设置Red Hat OpenShift Local启动实例访问OpenShift集群访问OpenShift web console使用OpenShift CLI访问OpenShift集群访问内部 OpenShift registry 使用odo部署示例应用安装odo 停止实例删除…

PointBeV:A Sparse Approach to BeV Predictions

参考代码:PointBeV 动机与出发点 常见显式构建BEV特征的算法会稠密设置BEV网格,这样就会引入背景像素上的无效计算,对应内存与计算资源使用也会变大。这篇文章通过前景点筛选、由粗到精细化、窗口时序融合方式构建一种稀疏化表达的BEV特征表…

陪诊系统|陪诊小程序|陪诊服务让就医更容易

陪诊系统已经出现了好几年。尤其是这两年,它得到了人们的广泛认可。陪诊行业的快速发展主要是因为人们对这个行业的需求非常大。目前,我国面临着严重的老龄化问题,生活节奏也越来越快,有时候无法亲自陪伴在老人的身边。陪诊工作人…

Web课程学习笔记--CSS-Sprite的应用

雪碧图CSS Sprite的应用 CSS雪碧,即CSS Sprite,也有人叫它CSS精灵,是一种CSS图像合并技术,该方法是将小图标和背景图像合并到一张图片上,然后利用css的背景定位来显示需要显示的图片部分。例如常见的商品分类导航其实所…

使用深度学习对视频进行分类

目录 加载预训练卷积网络 加载数据 将帧转换为特征向量 准备训练数据 创建 LSTM 网络 指定训练选项 训练 LSTM 网络 组合视频分类网络 使用新数据进行分类 辅助函数 此示例说明如何通过将预训练图像分类模型和 LSTM 网络相结合来创建视频分类网络。 要为视频…

【Vue】Vue基础入门

📝个人主页:五敷有你 🔥系列专栏:Vue ⛺️稳重求进,晒太阳 Vue概念 是一个用于构建用户界面的渐进式框架优点:大大提高开发效率缺点:需要理解记忆规则 创建Vue实例 步骤: …

Spinnaker多云持续交付平台: 部署Minio存储服务

目录 一、实验 1.环境 2.K8S storage节点部署NFS 3.K8S 动态创建PV 4.K8S master节点部署HELM3 4.K8S master节点部署Minio存储服务(第一种方式安装) 5.Minio客户端安装MC命令 6.K8S master节点使用Docker 部署Minio存储服务(第二种方…

雨云宿迁云服务器测评

我本打算趁着暑假买台云服务器开mc服务器,但由于没有试用且直接完结导致白废20块钱。 在此提醒大家,买用于开mc服务器的云服务器前能试用一定要试用!不然鬼知道它性能够不够用! 服务器配置如下: cpu:2v gold61332.5Ghz ram:2GiB…

Unity学习笔记之【IK反向动力学操作】

反向动力学Inverse Kinematics 反向动力学,简称IK。相较于正向动力学,反向动力学旨在子级对父级产生的影响。 使用IK,可以实现根据目标位置或方向来计算并调整角色的关节(骨骼)链,以使角色的末端&#xff…

C++基础知识点预览

一.绪论: 1.1 C简史: 与C的关系: 被设计为C语言的继任者,C语言是一种过程型语言,程序员使用它定义执行特定操作的函数,而C是一种面向对象的语言,实现了继承、抽象、多态和封装等概念。C支持类&…

WiFi 6 和WiFi 6e 的核心要点

目录 WiFi 6 是什么? WiFi 6/6e 的主要feature功能: 80Mhz and 160Mhz channel 1K QAM WiFi6 支持2.4G band OFDMA:Orthogonal frequency division multiple access OFDMA先把频段分为:Resource Units (RUs) Subcarriers …

51单片机编程应用(C语言):篮球比赛计分器

设计思路 1.LCD1602显示A 000:B 000 右下角显示24的数字,显示一节时间12:00. 2.规定矩阵键盘每一位表示的含义 s1按下,A队加一分 s2按下,A队加两分 s3按下,A队加三分 s4按下,A队减一分 如…

政安晨:快速学会~机器学习的Pandas数据技能(五)(分组和排序)

提升您的洞察力水平,数据集越复杂,这一点就越重要。 概述 映射允许我们逐个值地转换DataFrame或Series中的数据,针对整个列进行操作。然而,通常我们希望对数据进行分组,然后对所在组进行特定操作。 正如你将学到的&a…

Java stream 流的基本使用

Java stream 的基本使用 package com.zhong.streamdemo.usestreamdemo;import jdk.jfr.DataAmount; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;import java.util.ArrayList; import java.util.Comparator; import java.util.Li…