功能性的安全保障:输入校验

news2024/9/8 17:29:47

前言

在软件开发过程中,确保系统的安全性是至关重要的一环。它不仅关乎保护用户数据的完整性和隐私性,也是维护系统稳定运行的基石。我认为,从宏观角度审视,软件开发的安全性保障主要可分为两大类:功能性的安全性保障和系统性的安全性校验。

功能性的安全性保障专注于应用程序层面,它着眼于那些直接影响用户数据和交互过程安全的特性。这些特性是构建用户信任和保障数据安全的关键。

而系统性的安全性校验则放眼于更为广阔的视角,它涵盖了整个系统架构和网络层面,确保从服务器到网络的每一环节都具备足够的防御能力,以抵御各种潜在的攻击。

在之前 功能性的安全性保障:实现强制登录和密码加密功能 和 功能性的安全性保障:TOKEN鉴权校验 两文里。我们分别讨论了功能性的安全性保障中的几个核心议题:身份验证和密码加密、TOKEN鉴权校验和认证。在本文中,我们依旧继续功能性的安全性保障这个话题,通过对概念的细致解读、实施策略的全面分析,以及实际代码实现的展示,深入讨论其中的另外一个核心议题:输入校验。

1. 怎么控制输入校验

输入校验,一般是对用户输入进行验证,比说文本框输入、图片文件上传、地址输入等。防止出现SQL注入、跨站脚本(XSS)等攻击。

2. 什么是XSS?

XSS(Cross-Site Scripting),跨站脚本攻击,是一种常见的网络安全漏洞,主要形式是攻击者将恶意脚本注入到用户浏览的页面中。这种攻击通常发生在当一个网站允许用户输入未经适当处理的数据到页面时。XSS攻击的影响范围较广,常常被用来盗取用户Cookie、会话劫持、访问敏感数据、冒充用户执行操作、传播恶意软件等。

3. 代码实现:简单模拟XSS攻击

3.1 前端代码实现

这里书接上文,将 MePage.vue 的 个人简介输入框改成TXT文件上传组件。模拟简单的恶意的TXT文件上传导致系统被攻击的场景。

<!-- MePage.vue -->
<template>
  <div class="me">
    <el-form :model="user" label-width="auto" style="max-width: 600px" enctype="multipart/form-data">
      <el-form-item label="头像">
        <el-upload
            class="avatar-uploader"
            :action="uploadUrl"
            accept="image/*"
            :show-file-list="false"
            :before-upload="beforeUpload"
            :on-success="handleSuccess"
            :on-error="handleError"
            :on-change="handleFileChange"
        >
          <img v-if="imageUrl" style="height: 200px; width: 200px" alt="头像" class="avatar" :src="imageUrl"/>
          <el-icon class="avatar-uploader-icon" v-else-if="!islook"/>
        </el-upload>
      </el-form-item>
      <el-form-item label="用户名">
        <el-input v-model="user.username"/>
      </el-form-item>
      <el-form-item label="性别">
        <el-input v-model="user.sex"/>
      </el-form-item>
      <el-form-item label="年龄">
        <el-input v-model="user.age"/>
      </el-form-item>
      <el-form-item label="邮箱">
        <el-input v-model="user.mailbox"/>
      </el-form-item>
      <el-form-item label="个人简介">
        <el-input v-html="user.introduce"/>
        <el-upload
            :action="uploadTextUrl"
            accept=".txt"
            :show-file-list="false"
            :on-success="handleTextSuccess"
            :on-error="handleTextError">
          <el-button type="primary" style="margin-top: 10px;">上传TXT文件</el-button>
        </el-upload>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
import axios from 'axios';
import {ElMessage} from 'element-plus';

export default {
  name: 'MePage',
  data() {
    return {
      user: {
        userId: '',
        sex: '',
        age: '',
        mailbox: '',
        username: '',
        introduce: '',
        headPortrait: ''
      },
      uploadUrl: 'http://localhost:8081/upload/avatar',
      uploadTextUrl: 'http://localhost:8081/upload/introduce',
      islook: false,
      imgUrl: '../assets/logo.png',
      imageUrl: ''
    }
  },
  components: {},
  created() {
    this.getuser();
  },
  methods: {
    getuser() {
      const token = localStorage.getItem('token');
      console.log('token:', token);
      if (!token) {
        ElMessage.error('请先登录');
        this.$router.push('/login');
      }
      axios.get('http://localhost:8081/user/getById?userId=123', {headers: {Authorization: `Bearer ${token}`}}).then(response => {
        console.log('服务器返回的数据:', response.data);
        this.user = response.data.body;
        this.imageUrl = this.user.headPortrait ? `${this.user.headPortrait}` : '@/assets/logo.png';
        console.log('图片 URL:', this.imageUrl);
        this.islook = true;
      }).catch(error => {
        console.log(error);
        ElMessage.error('获取用户信息失败');
      });
    },
    beforeUpload(file) {
      const isImage = file.type.startsWith('image/');
      if (!isImage) {
        ElMessage.error('只能上传图片文件!');
        return false;
      }
      return isImage;
    },
    handleSuccess(response) {
      console.log('图片 上传成功响应:', response);
      if (response.code === 200) {
        ElMessage.success('上传成功');
        console.log('上传成功');
        // 重新调用 getuser 方法以获取最新的头像地址
        this.getuser();
      } else {
        ElMessage.error('上传失败: ' + response.msg);
      }
    },
    handleError(err) {
      console.error('图片上传失败:', err);
      ElMessage.error('上传失败: ' + (err.message || '未知错误'));
    },
    handleFileChange(file) {
      // 更新显示的图片
      const fileURL = file.raw;
      this.imageUrl = URL.createObjectURL(fileURL);
    },
    handleTextSuccess(response) {
      console.log('TXT文件上传成功响应:', response);
      if (response && response.code === 200) {
        // 直接赋值未编码的内容
        this.user.introduce = decodeURIComponent(response.body);
        console.log('txt文件内容:', this.user.introduce);
        ElMessage.success('TXT文件上传成功');
      } else {
        ElMessage.error('TXT文件上传失败: ' + (response ? response.msg : '未知错误'));
      }
    },
    handleTextError(error) {
      ElMessage.error('上传失败: ' + (error.message || '未知错误'));
    }
  }
}
</script>

<style scoped>
.me {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
}

.avatar-uploader .el-upload {
  border: 1px dashed var(--el-border-color);
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: var(--el-transition-duration-fast);
  height: 200px;
  width: 200px
}

.avatar-uploader .el-upload:hover {
  border-color: var(--el-color-primary);
}
</style>

3.2 后端代码实现

后端只需要提供接口,用于将前端上传的TXT文件进行解析,并将内容回传给前端即可。

/**
     * 个人简介上传
     */
    @PostMapping("/introduce")
    public RestResult<String> uploadIntroduce(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws Exception {
        if (!file.isEmpty()) {
            // 检查文件类型
            if (!Objects.equals(file.getContentType(), "text/plain")) {
                Map<String, Object> extension = new HashMap<>();
                extension.put("error", "上传格式错误,请上传txt文件!");
                return RestResult.buildFailure(extension);
            }

            // 读取文件内容
            StringBuilder content = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    content.append(line).append("\n");
                }
            }

            return RestResult.build(content.toString());
        }
        throw new Exception("文件上传失败");
    }

让我们来看看效果,上传了一个恶意文件,其内容为:<img src="url" onerror="alert('警告,系统正受到Xss攻击!');">。通过定义一个不存在的图片路径,当系统尝试加载该图片时会触发错误事件,从而执行恶意脚本,弹出异常提示。这种行为模拟了实际生产环境中,不法分子利用系统漏洞上传恶意文件或可执行插件,诱使系统执行他们预设的脚本,以达到不法目的。
在这里插入图片描述

4. 防范Xss攻击

防范跨站脚本攻击(XSS)是Web开发中非常重要的一部分。目前市面上有好几种常用的防范措施:

  1. 对输入验证和过滤。这点可以使用正则表达式或其他验证工具来实现。

  2. 前端对后端的输出进行编码。防止浏览器将其解析为可执行的脚本。可以使用模板引擎或手动编码。

  3. 使用安全的文件上传组件,使用经过安全验证的文件上传组件,并对上传的文件进行严格的检查,确保文件类型和内容符合要求。

  4. 配置内容安全策略(CSP),限制网页可以加载的资源,防止执行未经授权的脚本。

  5. 在设置 Cookie 时,使用 HttpOnly 和 Secure 标志,防止通过 JavaScript 访问 Cookie。

4.1 代码实现:防范Xss攻击

针对上面的Xss攻击情况,只需要在后端页面在对文件内容解析后,再对内容进行过滤编码即可,这样传到前端的数据,就不会被当成脚本执行。

/**
     * 个人简介上传
     */
    @PostMapping("/introduce")
    public RestResult<String> uploadIntroduce(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws Exception {
        if (!file.isEmpty()) {
            // 检查文件类型
            if (!Objects.equals(file.getContentType(), "text/plain")) {
                Map<String, Object> extension = new HashMap<>();
                extension.put("error", "上传格式错误,请上传txt文件!");
                return RestResult.buildFailure(extension);
            }

            // 读取文件内容
            StringBuilder content = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    content.append(line).append("\n");
                }
            }
            // 过滤并编码内容,防止恶意脚本被执行
            String filteredContent = filterContent(content.toString());
            return RestResult.build(filteredContent);
        }
        throw new Exception("文件上传失败");
    }

    /**
     * 功能描述 : 过滤内容,防止恶意脚本被执行
     *
     * @param content 文件内容
     * @return {@code String }
     * @author xhb
     * @since 2024/07/29
     */
    private String filterContent(String content) {
        return content.replaceAll("<script>", "")
                .replaceAll("</script>", "")
                .replaceAll("<iframe>", "")
                .replaceAll("</iframe>", "")
                .replaceAll("&", "&amp;")
                .replaceAll("<", "&lt;")
                .replaceAll(">", "&gt;")
                .replaceAll("\"", "&quot;")
                .replaceAll("'", "&#x27;")
                .replaceAll("/", "&#x2F;");
    }

在这里插入图片描述

5. 什么是SQL注入?

SQL注入(SQL Injection)攻击是指攻击者通过在输入字段中注入恶意SQL代码,从而绕过应用程序的身份验证和授权机制,访问或修改数据库中的数据。SQL注入攻击可以导致数据泄露、数据篡改、权限提升等严重后果。

6. 代码实现:简单模拟SQL注入攻击

6.1 后端代码实现

我们将前面的上传接口实现改一改,通过自定义SQL的形式去更新表数据。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.interestplfm.mapper.UserMapper">

    <update id="sqlInjectionToUpdateAvatar">
        UPDATE user
        SET avatar = #{headPortrait}
        WHERE userid = ${userid}
    </update>
</mapper>

为了模拟SQL注入,我们可以传递一个包含恶意SQL代码的 id 参数。这里定义一个测试类模拟恶意入参。

@SpringBootTest
class InterestplfmApplicationTests {

    @Autowired
    private UserRepository userRepository;

    @Test
    void UploadTest() {
        String avatar = "new_avatar.png";
        String id = "1; DROP TABLE user; --";
        userRepository.sqlInjectionToUpdateAvatar(id , avatar);
    }

}

在这种情况下,执行的SQL语句就会变成:
在这里插入图片描述
导致在更新用户头衔的同时,将其他用户的个人信息删除掉。

这里顺便小提一嘴,如果有小伙伴在执行的时候出现了org.springframework.jdbc.BadSqlGrammarException,提示SQL语法错误。且使用的是Mybatis-plus。那么可能是由于MyBatis-Plus在处理SQL语句时,默认会进行参数化查询,不允许多条SQL合并形式,从而防止SQL注入。

解决方法的话,可以在配置文件中,datasource配置的url参数中,加上&allowMultiQueries=true。允许在单个SQL语句中执行多个查询,从而达到我们想要模拟的效果。

7. 防范SQL注入

防范SQL注入同样是Web开发中非常重要的一部分。常见的防范措施有:

  1. 使用参数化查询。通过将SQL语句和参数分开处理,数据库引擎会自动转义输入参数,防止恶意代码的执行。

  2. 对用户输入的数据进行严格的验证和过滤,确保输入的内容符合预期的格式和类型。

  3. 为数据库用户分配最小必要的权限,避免使用具有高权限的用户执行SQL操作。

  4. 使用ORM(对象关系映射)框架,如 MyBatis-Plus、Hibernate等,这些框架通常内置了防止SQL注入的机制。

7.1 代码实现:防范SQL注入攻击

其实措施也很简单,我们只需要使用参数化查询,而不是直接拼接SQL语句。在MyBatis中,可以使用 #{} 来代替 ${},这样MyBatis会自动处理参数化查询。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.interestplfm.mapper.UserMapper">

    <update id="sqlInjectionToUpdateAvatar">
        UPDATE user
        SET avatar = #{headPortrait}
        WHERE userid = #{userid}
    </update>
</mapper>

8. 总结

输入校验是确保应用程序安全的第一道防线。通过对用户输入进行严格的验证,可以阻止恶意数据进入系统,从而减少安全漏洞的风险。有效的输入校验策略可以保护数据的完整性,维护系统稳定性,并增强用户对应用的信任。

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

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

相关文章

GitEval — 预测你的 GitHub 个人资料的质量

使用机器学习来预测你是否擅长编码 可直接在橱窗里购买&#xff0c;或者到文末领取优惠后购买&#xff1a; 如果你曾经申请过技术职位&#xff0c;你可能已经向公司发送了你的 GitHub 个人资料链接。此个人资料中的信息可以很好地表明你的编码能力以及是否适合团队。所有这些信…

【全国大学生电子设计竞赛】2024年C题

&#x1f970;&#x1f970;全国大学生电子设计大赛学习资料专栏已开启&#xff0c;限时免费&#xff0c;速速收藏~

Opencv画出红底白字标准中文显示框

链接&#xff1a;https://pan.baidu.com/s/1iEJKpqt-z_5yBJdenUABbA 提取码&#xff1a;uoox 先把这个文件拿了&#xff0c;这个文件是一个ttf的字体&#xff0c;用于显示中文。 核心代码&#x1f451; def cv2AddChineseText(self, img_ori, text, p1, box_color, textColo…

【JavaScript】函数声明和函数表达式的区别

文章目录 一、函数声明1. 定义方式2. 作用域提升&#xff08;Hoisting&#xff09;3. 块级作用域 二、函数表达式1. 定义方式2. 作用域提升&#xff08;Hoisting&#xff09;3. 自引用 三、其他区别1. 函数名2. 可读性和代码组织3. 使用场景 四、总结函数声明函数表达式 在Java…

昇思MindSpore学习入门-自动混合精度

混合精度&#xff08;Mix Precision&#xff09;训练是指在训练时&#xff0c;对神经网络不同的运算采用不同的数值精度的运算策略。在神经网络运算中&#xff0c;部分运算对数值精度不敏感&#xff0c;此时使用较低精度可以达到明显的加速效果&#xff08;如conv、matmul等&am…

Android 10.0 Launcher3仿ios的folder文件夹widget功能实现一

1.前言 在10.0的系统ROM开发中,在进行一些系统Launcher3定制功能开发中,需要实现folder文件夹widget的功能,由于launcher3 默认不支持folder跨行显示,所以就需要借助自定义的widget小部件功能来实现相关功能,接下来分析实现相关功能 2.Launcher3仿ios的folder文件夹widge…

【保姆级教程】免费域名注册 Cloudflare 域名解析 Ngnix端口转发

前段时间&#xff0c;带大家搞了两台云服务器&#xff1a; 玩转云服务&#xff1a;Oracle Cloud甲骨文永久免费云服务器注册及配置指南玩转云服务&#xff1a;手把手带你薅一台腾讯云服务器&#xff0c;公网 IP 基于这两台云服务器&#xff0c;我们玩转了很多有趣的开源项目&…

ElasticSearch(八)— 聚集查询1

一、总概 聚集查询(Aggregation)提供了针对多条文档的统计运算功能&#xff0c;它不是针对文档本身内容的检索&#xff0c;而是要将它们聚合到一起运算某些方面的特征值。 聚集查询与 SQL 语言中的聚集函数非常像&#xff0c;聚集函数在 Elasticsearch 中相当于是聚集查询的一…

掌控数据流的智能仪表板:Redpanda Console

Redpanda Console&#xff1a; 一站式管理&#xff0c;让数据流尽在掌控之中。- 精选真开源&#xff0c;释放新价值。 概览 Redpanda Console&#xff0c;是一款创新的Web界面工具&#xff0c;专为简化Kafka和Redpanda数据流的监控与管理而设计。它以用户友好的交互方式&#…

浏览器用户文件夹详解 - Favicons(二)

1. 引言 上一篇文章我们深入探讨了Chromium用户文件夹中的Bookmarks文件,了解了它的JSON结构以及如何解析和修改书签数据。 在本文中,我们将继续探索Chromium用户文件夹中的另一个重要文件:Favicons。Favicons,也就是我们常说的网站图标,是浏览器中不可或缺的一部分。它们不仅…

RK3568笔记四十七:PWM 子系统

若该文为原创文章&#xff0c;转载请注明原文出处。 pwm 子系统功能单一&#xff0c;很少单独使用&#xff0c;一般用于控制显示屏的背光、控制无源蜂鸣器、伺服电机、电压调节等等。 一、PWM介绍 PWM(Pulse width modulation)&#xff0c;脉冲宽度调制。在内核中 PWM 驱动较简…

并发编程工具集——并发容器-下(二十五)

List List 里面只有一个实现类就是 CopyOnWriteArrayList。CopyOnWrite&#xff0c;写的时候会将共享变量新复制一份出来&#xff0c;读操作完全无锁&#xff1b;适合读多写少的场景&#xff0c;写操作会复制数组&#xff0c;在新的数组中操作实现原理&#xff1a;CopyOnWriteA…

备考软考高级系统架构设计师,需要买哪些资料?

距离2024下半年软考高级系统架构设计师考试仅剩三个月&#xff01;时间紧迫&#xff0c;单单啃书已经不够了&#xff0c;毕竟是软考高级科目&#xff0c;难度不是那些初、中级可以比拟的。要想短时间速通架构考试&#xff0c;学会抓重点真的很重要&#xff0c;45分说多不多说少…

【音视频】RTSP、RTMP与流式传输

文章目录 前言RTSP与RTMPRTSP&#xff08;Real-Time Streaming Protocol&#xff09;RTMP&#xff08;Real-Time Messaging Protocol&#xff09;主要差异 什么是流式传输&#xff1f;流式传输的特点流式传输与传统下载的区别 使用VLC播放RTSP监控 总结 前言 在现代网络环境中…

C++ //练习 16.2 编写并测试你自己版本的compare函数。

C Primer&#xff08;第5版&#xff09; 练习 16.2 练习 16.2 编写并测试你自己版本的compare函数。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /*********************************************************************…

PyTorch深度学习快速入门(下)

PyTorch深度学习快速入门&#xff08;下&#xff09; 一、现有网络模型的使用及修改&#xff08;一&#xff09;背景知识&#xff08;二&#xff09;修改网络模型的三种方法 二、网络模型的保存与加载&#xff08;一&#xff09;保存网络模型的两种方法&#xff08;二&#xff…

阿里云 服务器安装rabbit

现在我们去服务器安装一个rabbit 进入home 创建一个rabbit文件夹 /home/rabbit vim deployRabbit.sh 脚本内容 #!/bin/bash docker run -d \ --name dev.rabbit \ --network dev-net \ -p 15672:15672 \ -v ./data:/var/lib/rabbitmq \ --hostname dev.rabbit \ rabbitmq:…

css3 红色阴影边框紧急提醒呼吸灯特效

效果截图 代码 <!DOCTYPE html> <html><head><title>红色呼吸灯紧急特效</title><style>keyframes alarm {0% {box-shadow: 0 0 30px #ff0000;}50% {box-shadow: 0 0 60px #ff0000, inset 0 0 60px #ff0000;}100% {box-shadow: 0 0 30px …

八、【Python】基础 - 【Python while 循环全解析】:掌握无限循环的艺术

&#x1f4a1;&#x1f4da;【Python while 循环全解析】&#xff1a;掌握无限循环的艺术&#x1f4da;&#x1f4a1; 目录 1.基本语法 2.示例 3.注意事项 4.嵌套循环与循环控制语句 5.示例&#xff1a;使用 break 和 continue 6.示例&#xff1a;计数器 7.示例&#xf…

Web:Url 编码 -13

URL编码概述 HTTP协议只支持iso8859-1字符集。 而此字符集中只有英文数字常见符号。 所以HTTP原生是无法传输非iso8859-1字符的。 为了解决这个问题&#xff0c;提出了一种称之为URL编码的解决方案。 URL编解码详解 将非iso8859-1字符&#xff0c;进行转换 先将字符按照指定码表…