JS 基本语法和 JS DOM

参考:『web前端开发』javascript学习

JS原生开发

什么是JS原生开发?

JS原生开发是指:在不依赖任何第三方框架或库(如React、Vue、Angular等)的情况下,使用原生的JavaScript语言进行开发。

案例:文件上传过滤

  1. 功能:在前端通过JS实现后缀过滤,后端PHP实现上传处理

  2. 代码

    1)前端 JS + HTML 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    <script>
    function CheckFileExt(filename){
    var flag=false;
    //规定白名单上传后缀
    var exts=['png','gif','jpg'];
    //1.php 1.php.jpg 接受传递的后缀名
    var index=filename.lastIndexOf(".");
    var ext = filename.substr(index+1);
    //进行后缀检测
    for(i=0;i<exts.length;i++){
    if(ext==exts[i]){
    var flag=true;
    alert('文件后缀正确!');
    break;
    }
    }
    if(!flag){
    alert('文件后缀错误!')
    location.reload(true);
    }
    }
    </script>

    <html>
    <!-- 表单用于文件上传,指定了上传的目标地址为 "upload.php",使用 POST 方法提交,并设置 enctype 为 "multipart/form-data" -->
    <form action="upload.php" method="POST" enctype="multipart/form-data">
    <!-- 为文件上传输入框添加标签 -->
    <label for="file">选择文件:</label>
    <br>
    <!-- 这是一个包含文件上传输入框的 HTML 代码,并且在用户选择文件时触发 CheckFileExt 函数 -->
    <input type="file" id="file" name="f" onchange="CheckFileExt(this.value)">
    <br>
    <!-- 提交按钮 -->
    <button type="submit">上传文件</button>
    </form>
    </html>

    2)后端PHP代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?php
    $name=$_FILES['f']['name'];
    $type=$_FILES['f']['type'];
    $size=$_FILES['f']['size'];
    $tmp_name=$_FILES['f']['tmp_name'];
    $error=$_FILES['f']['error'];

    if(move_uploaded_file($tmp_name,'upload/'.$name)){
    echo '<script>alert("上传成功!")</script>';
    }
  3. 安全问题

    可以直接在前端看到JS文件上传过滤代码,从而分析并绕过

    • 1)利用开发者工具绕过:利用浏览器的开发者工具实时修改网页前端JS代码(实现绕过)

      注意:不能直接在元素中修改,浏览器一般都会已经在内存里加载了这段代码,要重新加载代码只能靠刷新,然而刷新会丢失我们所作的修改。

    • 2)禁用 JS

    • 3)抓包

      JS代码的原理是在浏览器端执行,执行后发送数据包。所以如果我对这个功能接口进行安全测试,使用Burp suite抓包,在数据包中修改文件拓展,并不会被前端JS中的后缀检测代码拦截。

      所以测试时不仅要关注前端的校验,还要确保后端有足够的安全控制来防止恶意文件上传。

JS导入库开发

JS第三方库

什么是JS导入库开发?

其实就是导入第三方库来JS代码开发(和react、flask这类框架不同),将外部的JavaScript代码或模块引入到项目中,以便在项目中使用这些代码或功能。通过导入库,你可以利用这些已有的功能和工具,从而提高开发效率,避免重复造轮子。

常用的JS第三方库:

  • jQuery:一个常用的 DOM 操作库,简化了浏览器中的 DOM 操作和事件处理
  • Lodash:提供了很多常用的 JavaScript 工具函数,用于数组、对象、函数等操作
  • Axios:一个用于处理 HTTP 请求的库,基于 Promise API

JS 导入库开发案例

案例1:登录验证

  1. 功能:基于Query库和Ajax技术异步请求实现用户的登录验证

    Ajax 是一种在网页中与服务器进行异步交互的技术,允许网页在不重新加载整个页面的情况下,后续触发某些操作(如点击按钮)后再异步发送请求,网页接收到服务器返回的数据后动态更新内容,避免页面刷新。

    例如:谷歌翻译,用户输入文本的时候动态更新翻译内容

  2. 代码

    1)前端 JS + HTML 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    <!-- 引入 jQuery 库 -->
    <script src="js/jquery-1.12.4.js"></script>

    <!-- JavaScript 代码 -->
    <script>
    // 这段代码通过 $("button") 选择页面上的所有 <button> 元素,并为这些按钮添加一个 点击事件监听器。当用户点击按钮时,事件处理器中的回调函数会被执行。
    $("button").click(function (){
    // 当按钮被点击时,AJAX 请求会被触发
    $.ajax({
    type: 'POST', // HTTP请求方法(GET、POST等)
    url: 'logincheck.php', // 请求的URL
    data: { // 发送到服务器的数据(可选)
    myuser:$('.user').val(), // 获取类名为 .user 的元素的值
    mypass:$('.pass').val()
    },

    // AJAX 请求成功时执行的回调函数。
    success: function (res){
    console.log(res); // 服务器响应的数据
    if(res['infoCode']==1){
    alert('登录成功');
    // 登录成功处理事件
    //location.href='index.php';
    }else{
    alert('登录失败');
    }
    },

    // 请求失败时的回调函数
    error: function(xhr, status, error) {
    console.log("请求失败!");
    console.log(error); // 错误信息
    }

    // 指定服务器响应的数据类型
    dataType: 'json',
    });
    });
    </script>

    <!-- HTML代码 -->
    <div class="login">
    <!-- 登录标题 -->
    <h2>后台登录</h2>
    <!-- 用户名标签和输入框 -->
    <label for="username">用户名:</label>
    <input type="text" name="username" id="username" class="user">
    <!-- 密码标签和输入框 -->
    <label for="password">密码:</label>
    <input type="password" name="password" id="password" class="pass">
    <!-- 登录按钮 -->
    <button>登录</button>
    </div>

    2)后端php代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <?php
    // 从 POST 请求中获取用户提交的用户名和密码
    $user = $_POST['myuser'];
    $pass = $_POST['mypass'];

    // $success 是一个关联数组变量,通过使用 'msg' 作为键,将 'ok' 作为值存储在其中。
    $success = array('msg' => 'ok');**

    // 检查用户名和密码是否匹配,注意:真实情况下,应该在数据库中进行验证获取用户信息
    if ($user == 'xiaodi' && $pass == '123456') {
    // 如果匹配,设置信息代码为1表示登录成功,并进行相应的处理
    $success['infoCode'] = 1;
    echo '<script>location.href="index.php";</script>'
    } else {
    // 如果不匹配,设置信息代码为0表示登录失败
    $success['infoCode'] = 0;
    }

    // 将结果以 JSON 格式输出
    //必须要回调输出,不然前端无法获取infocode的值
    echo json_encode($success);**

  3. 安全问题

    注意登录后的跳转代码:

    • 前端JS中:location.href='index.php';
    • 后端PHP中:echo '<script>location.href="index.php";</script>'

    这两种方法实际只有一个存在,如果这个跳转代码写在了JS中则可以进行绕过,如果是后端就不可以。

    如果写在前端JS中的话:这里我们直接随便输入一个用户名密码,用bp发送并截取服务器返回的数据包,将数据包中的 res['infoCode'] 改成1再放开,浏览器执行JS脚本并成功登录。

案例2:商品购买

  1. 功能:基于Query库和Ajax技术异步请求实现商品购买

  2. 代码

    1)前端 JS + HTML 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <!-- 设置文档的字符集为UTF-8 -->
    <meta charset="UTF-8">
    <!-- 设置页面标题 -->
    <title>商品购买</title>
    </head>
    <body>
    <!-- 商品图片 -->
    <img src="iphone.jpg" width="300" height="300" alt=""><br>
    <!-- 当前拥有的金钱 -->
    金钱:10000<br>
    <!-- 商品价格 -->
    商品价格:8888<br>
    <!-- 输入购买数量的文本框 -->
    数量:<input type="text" name="number" class="number">
    <!-- 购买按钮 -->
    <button>购买</button>
    </body>
    </html>

    <!-- 引入 jQuery 库 -->
    <script src="js/jquery-1.12.4.js"></script>
    <!-- JavaScript 代码 -->
    <script>
    // 当购买按钮被点击时执行以下函数
    $("button").click(function (){
    // 使用 AJAX 发送 POST 请求到 'shop.php'
    **$.ajax({
    type: 'POST',
    url: 'shop.php',
    // 发送的数据,包括购买数量
    data: {
    num: $('.number').val(),
    },
    // 请求成功时执行的函数
    success: function (res){
    // 在控制台输出返回的数据
    console.log(res);
    // 如果返回的信息代码为1,表示购买成功
    if(res['infoCode'] == 1){
    // 弹出成功提示
    alert('购买成功');
    // 购买成功的流程(你可以在这里添加额外的处理)
    } else {
    // 如果信息代码不为1,表示购买失败
    // 弹出失败提示
    alert('购买失败');
    }
    },
    // 指定返回的数据类型为 JSON
    dataType: 'json',
    });**
    });
    </script>

    2)后端PHP代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?php
    // 从 POST 请求中获取购买数量
    $num = $_POST['num'];

    // 假设购物车中已有的金钱数为10000,商品价格为8888
    // 真实情况下,应该在数据库中获取用户的金钱数等信息

    // 初始化一个关联数组变量,通过使用 'msg' 作为键,将 'ok' 作为值存储在其中。
    $success = array('msg' => 'ok');

    **// 检查购买是否合法(金钱是否足够支付)
    if (10000 >= ($num * 8888)) {
    // 如果购买合法,设置信息代码为1表示购买成功
    $success['infoCode'] = 1;
    } else {
    // 如果购买不合法,设置信息代码为0表示购买失败
    $success['infoCode'] = 0;
    }

    // 将结果以 JSON 格式输出
    echo json_encode($success);**
    ?>

  3. 安全问题

    将抓到的包,设置其返回包也抓取,并将访问失败返回包的改为1发送,后购买成功。

案例3:编码加密

  1. MD5

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <!-- 引入 md5.js 脚本 -->
    <script src="js/md5.js"></script>

    <!-- JavaScript 代码 -->
    <script>
    // 定义字符串变量
    var str1 = 'xiaodi jichu No1';

    // 使用 md5.js 中的 md5 函数对字符串进行加密
    var str_encode = md5(str1);

    // 输出加密后的字符串到控制台
    console.log(str_encode);
    </script>

  2. SHA1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <!-- 引入 crypto-js.js 脚本 -->
    <script src="js/crypto-js.js"></script>

    <!-- JavaScript 代码 -->
    <script>
    // 定义字符串变量
    var str1 = 'xiaodisec';

    // 使用 CryptoJS.SHA1 函数对字符串进行 SHA-1 加密,并将结果转为字符串
    var str_encode = CryptoJS.SHA1(str1).toString();

    // 输出加密后的字符串到控制台
    console.log(str_encode);
    </script>

  3. HMAC

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <!-- 引入 crypto-js.js 脚本 -->
    <script src="js/crypto-js.js"></script>

    <!-- JavaScript 代码 -->
    <script>
    // 定义密钥和字符串变量
    var key = 'key';
    var str1 = 'xiaodisec';

    // 使用 CryptoJS.HmacSHA256 函数生成 HMAC-SHA256 散列
    var hash = CryptoJS.HmacSHA256(key, str1);

    // 将散列结果转为十六进制字符串
    var str_encode = CryptoJS.enc.Hex.stringify(hash);

    // 输出加密后的字符串到控制台
    console.log(str_encode);
    // 输出示例:'11a7960cd583ee2c3f1ed910dbc3b6c3991207cbc527d122f69e84d13cc5ce5c'
    </script>

  4. AES

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <script src="js/crypto-js.js"></script>
    <script type="text/javascript">
    var aseKey = "12345678" // 定制秘钥,长度必须为:8/16/32位, 长度不一致也没问题
    var message = "xiaodisec"; // 需要加密的内容
    // 加密 DES/AES切换只需要修改 CryptoJS.AES <=> CryptoJS.DES
    var encrypt = CryptoJS.AES.encrypt(message, CryptoJS.enc.Utf8.parse(aseKey), // 参数1=密钥, 参数2=加密内容
    {
    mode: CryptoJS.mode.ECB, // 为DES的工作方式
    padding: CryptoJS.pad.Pkcs7 // 当加密后密文长度达不到指定整数倍(8个字节、16个字节)则填充对应字符
    }
    ).toString(); // toString=转字符串类型

    console.log(encrypt);
    var decrypt = CryptoJS.AES.decrypt(encrypt, CryptoJS.enc.Utf8.parse(aseKey), // 参数1=密钥, 参数2=解密内容
    {
    mode: CryptoJS.mode.ECB,
    padding: CryptoJS.pad.Pkcs7
    }
    ).toString(CryptoJS.enc.Utf8); // toString=转字符串类型,并指定编码
    console.log(decrypt); // "xiaodisec"
    </script>

  5. DES

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    <script src="js/crypto-js.js"></script>
    <script type="text/javascript">
    var aseKey = "12345678" // 定制秘钥,长度必须为:8/16/32位, 长度不一致也没问题
    var message = "xiaodisec"; // 需要加密的内容
    // 加密 DES/AES切换只需要修改 CryptoJS.AES <=> CryptoJS.DES
    var encrypt = CryptoJS.DES.encrypt(message, CryptoJS.enc.Utf8.parse(aseKey), // 参数1=密钥, 参数2=加密内容
    {
    mode: CryptoJS.mode.ECB, // 为DES的工作方式
    padding: CryptoJS.pad.Pkcs7 // 当加密后密文长度达不到指定整数倍(8个字节、16个字节)则填充对应字符
    }
    ).toString(); // toString=转字符串类型

    console.log(encrypt); // 控制台打印 CDVNwmEwDRM

    //解密
    var decrypt = CryptoJS.DES.decrypt(encrypt, CryptoJS.enc.Utf8.parse(aseKey), // 参数1=密钥, 参数2=解密内容
    {
    mode: CryptoJS.mode.ECB,
    padding: CryptoJS.pad.Pkcs7
    }
    ).toString(CryptoJS.enc.Utf8); // toString=转字符串类型,并指定编码
    console.log(decrypt); // 控制台打印 "i am xiaozhou ?"

    </script>

  6. RSA

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <script src="js/jsencrypt.js"></script>
    <script type="text/javascript">
    // 公钥 私匙是通过公匙计算生成的,不能盲目设置
    var PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALyBJ6kZ/VFJYTV3vOC07jqWIqgyvHulv6us/8wzlSBqQ2+eOTX7s5zKfXY40yZWDoCaIGk+tP/sc0D6dQzjaxECAwEAAQ==-----END PUBLIC KEY-----';
    //私钥
    var PRIVATE_KEY = '-----BEGIN PRIVATE KEY-----MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAvIEnqRn9UUlhNXe84LTuOpYiqDK8e6W/q6z/zDOVIGpDb545NfuznMp9djjTJlYOgJogaT60/+xzQPp1DONrEQIDAQABAkEAu7DFsqQEDDnKJpiwYfUE9ySiIWNTNLJWZDN/Bu2dYIV4DO2A5aHZfMe48rga5BkoWq2LALlY3tqsOFTe3M6yoQIhAOSfSAU3H6jIOnlEiZabUrVGqiFLCb5Ut3Jz9NN+5p59AiEA0xQDMrxWBBJ9BYq6RRY4pXwa/MthX/8Hy+3GnvNw/yUCIG/3Ee578KVYakq5pih8KSVeVjO37C2qj60d3Ok3XPqBAiEAqGPvxTsAuBDz0kcBIPqASGzArumljkrLsoHHkakOfU0CIDuhxKQwHlXFDO79ppYAPcVO3bph672qGD84YUaHF+pQ-----END PRIVATE KEY-----';
    //使用公钥加密
    var encrypt = new JSEncrypt();//实例化加密对象
    encrypt.setPublicKey(PUBLIC_KEY);//设置公钥
    var message = 'xiaodisec' // 需要加密的数据
    var encrypted = encrypt.encrypt(message);//对指定数据进行加密
    console.log(encrypted) // 'JQ83h8tmJpsSZcb4BJ3eQvuqIAs3ejepcUUnoFhQEvum8fA8bf1Y/fG+DO1bSIVNJF6EOZKe4wa0njv6aOar9w=='
    //使用私钥解密
    var decrypt = new JSEncrypt(); // 创建解密对象
    decrypt.setPrivateKey(PRIVATE_KEY); //设置私钥
    var uncrypted = decrypt.decrypt(encrypted); //解密 'xiaodisec'
    console.log(uncrypted);
    </script>

JS 第三方库安全

JQuery 安全

JQuery版本对应漏洞在线查询网站:https://research.insecurelabs.org/jquery/test/

Javascript框架库漏洞验证

JS DOM

  1. 具体概念与语法:仍然是参考我的那篇javascript学习博客

  2. DOM技术修改前端内容的安全问题

    如果前端的代码允许用户提供数据(例如通过输入框、URL参数、HTTP请求等),并且这些数据被直接插入到DOM中,就存在潜在的风险:如DOM-XSS攻击等。

    举个例子,假设有一个网站让用户输入姓名并将其显示在页面上。如果这段输入没有被适当处理(如没有进行HTML编码或过滤),攻击者就可以通过输入恶意的JavaScript代码来操控页面。

  3. 实战案例

    • 网易云翻译:可以使用带外dns,造成数据库ip泄露

      见:小迪安全第29集0:39