soarli

通过JS向剪切板写入数据
6月12号晚上,辅助温春阳调试请假系统bug期间,留意到复制绑定密钥时自动复制的需求,想起两年前给电脑120网站配...
扫描右侧二维码阅读全文
18
2021/06

通过JS向剪切板写入数据

6月12号晚上,辅助温春阳调试请假系统bug期间,留意到复制绑定密钥时自动复制的需求,想起两年前给电脑120网站配置复制自动复制QQ群号的场景,扒出了当年的代码:


<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="keywords" content="电脑120,河南农业大学,学生社团,信息与管理科学学院,电脑问题,计算机">
<meta name="description" content="河南农业大学电脑120,电脑问题就找电脑120!">
<title>电脑120</title>
<meta http-equiv="Content-Language" content="zh-cn">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> 
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="format-detection" content="telephone=no" />
<link rel="icon" href="/image/logo.png" >
<link rel="stylesheet" href="css/style.css">

<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?9d665843674b57854ba63a6609a4f43d";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();


function qun1() {
  var input = document.getElementById("input");
     input.value = "322935908"; // 修改文本框的内容
  input.select(); // 选中文本
  document.execCommand("copy"); // 执行浏览器复制命令
  alert("服务群①群号复制成功,请打开QQ粘贴群号申请入群!");
}

function qun2() {
  var input = document.getElementById("input");
     input.value = "181626714"; // 修改文本框的内容
  input.select(); // 选中文本
  document.execCommand("copy"); // 执行浏览器复制命令
  alert("服务群②群号复制成功,请打开QQ粘贴群号申请入群!");
}

</script>

<style type="text/css">
   .wrapper {position: relative;}
   #input {position: absolute;top: 0;left: 0;opacity: 0;z-index: -10;}
</style>

<!--<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">-->
</head>

<body>


<div class="box box7">
        <span>
<p align="center">
<img border="0" src="120pic/120logo.jpg" width="249" height="110"></p>
<p align="center">

<marquee direction="left" scrollamount="7" scrolldelay="10" border="10" width="88%" >
    <font style="font-size: 22px;" color="black">
    欢迎加入服务群和大家交流计算机问题,群1:322935908,群2:181626714。期待在春暖花开的时节与大家在兰亭116重逢...
    </font>
    <br>
</marquee>

</p>
<p align="center">
&nbsp;<a title="有关电脑120的新闻动态" href="https://www.diannao120.top/news/index.html"><img border="0" src="120pic/xwdt.png" width="120" height="120"></a>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
<a title="电脑120网站的内容发布页" href="https://m.diannao120.top/blog/">
<img border="0" src="120pic/zhuye.png" width="120" height="120"></a></p>
        <p align="center">
<a title="分为“常见问题、学习考试、艺术宣传、软件安装”四个板块" href="normal/index.htm">
<img border="0" src="120pic/cjwt.png" width="120" height="120"></a>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
<a href="http://i.diannao120.top">
<img border="0" src="120pic/xnb.png" width="120" height="120"></a></p>
        <p align="center">
&nbsp;</p>
<blockquote>
    <p align="center" style="line-height: 16px"><font color="#000000">
    <a href="http://www.diannao120.top/blog/2019/11/15/191115liao01/" style="text-decoration: none">
    <font size="2" color="#000000">使用方法</font></a></font><font size="2">
    &nbsp;&nbsp;&nbsp;<a TITLE="电脑120服务群① 群号:322935908" href="javascript:qun1();" rel="noopener noreferrer" style="text-decoration: none"><font color="#000000">①群</font></a><span>&nbsp; 
    |&nbsp; </span>
    <a TITLE="电脑120服务群② 群号:181626714" href="javascript:qun2();" rel="noopener noreferrer" style="text-decoration: none">
    <font color="#000000">②群&nbsp;&nbsp;&nbsp; </font></a>
    </font>
    <a href="http://www.diannao120.top/blog/about/" style="text-decoration: none">
    <font size="2" color="#000000">关于我们</font></a></p>
</blockquote>
<p align="center" style="line-height: 16px"><font size="2">Copyright&nbsp; © 2019<script>new Date().getFullYear()>2019&&document.write("-"+new Date().getFullYear());</script>&nbsp; 电脑120&nbsp; 版权所有</font></p>

</span>
</div>

<div class="wrapper">
   <textarea id="input"></textarea>
</div>

</body>

</html>

然而没想到这段复制代码在其请假系统上针对iOS设备无论如何都不能生效,于是乎,和学长一起并肩开始了干到近凌晨3点的debug之旅。

直接贴出结论吧,最后问题在“缩量”和“断点”等各种神仙调试过后仍然无法解决后由“增量”大法得以找出。

问题核心在于iOS针对ajax异步请求做出了“非交互不得复制”的安全措施。(也就是说除非搞个复制按钮上去,否则你的数据就是“自动”进不了它的剪切板)

解决方法:对单个页面指定ajax请求指定为同步。

前些时候刚做出PHP上传功能,就有了落地需求-->多人同步调试CSS

于是乎:上传插件拿来开干,同时希望大家上传css文件后可以直接手动复制上传的文件地址,复制功能拿来开干:

index.html

<!DOCTYPE html>
<html>
<head>
    <title>上传CSS入口</title>
    <meta charset="utf-8">
</head>
<body>
<form enctype="multipart/form-data" action="upload.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="2097152"><!--优先通过浏览器引导用户上传指定大小文件,为后端检测提供第一道防线,但是本行可删,2*1024*1024-->
    上传CSS文件(不得超过2M):<input type="file" id='myfile' name="userfile" accept="text/css" onchange="fileChange(this);"><!--优先通过浏览器善意引导用户上传指定类型的文件,但是本行可删,用户也可在浏览器中选择“所有文件”-->
    <input type="submit" id="btn" value="上传">
<!--以下代码优先通过浏览器善意引导用户上传指定大小的文件同时节省不必要的带宽浪费,但是JS可禁用-->
<!-- <script type="text/javascript">
    document.getElementById("btn").onclick=function(){
        if (document.getElementById("myfile").files[0].size > 2097152) {alert('请上传小于2MB的文件!(由前端JS发现)');return false;}
    };</script> -->

 <!--以下代码优先通过浏览器善意引导用户上传指定类型和大小的文件同时节省不必要的带宽浪费,但是JS可禁用-->
  <script type="text/javascript">
    var isIE = /msie/i.test(navigator.userAgent) && !window.opera;

    function fileChange(target, id) {
      var fileSize = 0;
      var filetypes = [".css"];
      var filepath = target.value;
      var filemaxsize = 1024 * 2;//2M
      if (filepath) {
        var isnext = false;
        var fileend = filepath.substring(filepath.indexOf("."));
        if (filetypes && filetypes.length > 0) {
          for (var i = 0; i < filetypes.length; i++) {
            if (filetypes[i] == fileend) {
              isnext = true;
              break;
            }
          }
        }
        if (!isnext) {
          alert("上传失败,请选择css文件上传。(由前端发现)");
          target.value = "";
          return false;
        }
      } else {
        return false;
      }
      if (isIE && !target.files) {
        var filePath = target.value;
        var fileSystem = new ActiveXObject("Scripting.FileSystemObject");
        if (!fileSystem.FileExists(filePath)) {
          alert("上传失败,附件不存在,请重新选择(由前端发现)!");
          return false;
        }
        var file = fileSystem.GetFile(filePath);
        fileSize = file.Size;
      } else {
        fileSize = target.files[0].size;
      }

      var size = fileSize / 1024;
      if (size > filemaxsize) {
        alert("上传失败,请上传小于" + filemaxsize / 1024 + "MB的文件!(由前端发现)!");
        target.value = "";
        return false;
      }
      if (size <= 0) {
        alert("上传失败,附件大小不能为0M(由前端发现)!");
        target.value = "";
        return false;
      }
    }
  </script>
</form>
</body>
</html>

upload.php

<?php


// 后端验证防止上传超过2M的文件,定义最大值常量
define('MAX_SIZE',2097152);
define('MuLu','css/');

// echo $_FILES;  返回Array
// print_r($_FILES);   // 返回这种结构:
/*

Array
(
    [userfile] => Array
        (
            [name] => 1.jpg
            [type] => image/jpeg
            [tmp_name] => C:\Users\soarli\AppData\Local\Temp\php5449.tmp
            [error] => 0
            [size] => 376151
        )

)

*/

// 只允许上传jpg格式的图片
// if ($_FILES['userfile']['type'] != 'image/jpeg' && $_FILES['userfile']['type'] != 'image/png' && $_FILES['userfile']['type'] != 'image/gif') {
//     echo "<script>alert('本站只允许上传jpg/png/gif格式的图片,请重新选择!');history.back();</script>";
//     exit;
// }

// 使用以下方法设置格式白名单更加直观
// switch ($_FILES['userfile']['type']){
//     case 'image/jpeg':
//         break;
//     case 'image/png':
//         break;
//     case 'image/gif':
//         break;
//     default: echo "<script>alert('本站只允许上传jpg/png/gif格式的图片,请重新选择!');history.back();</script>";
//     exit;
// }


// 如果上传失败,根据$_FILES的error值判断报错类型并在前端提示
if ($_FILES['userfile']['error'] > 0) {
    switch ($_FILES['userfile']['error']) {
        case 1: echo "<script>alert('上传失败,上传文件超过PHP配置文件限定值大小(后端的限制,由后端发现)!');history.back();</script>上传失败,上传文件超过PHP配置文件限定值大小(后端的限制,由后端发现)!";
            break;
        case 2: echo "<script>alert('上传失败,上传文件超过HTML表单限定值大小(前端的限制,由后端发现)!');history.back();</script>上传失败,上传文件超过HTML表单限定值大小(前端的限制,由后端发现)!";
            break;
        case 3: echo "<script>alert('上传失败(部分被上传)!');history.back();</script>上传失败(部分被上传)!";
            break;    
        case 4: echo "<script>alert('上传失败,没有任何部分被上传!');history.back();</script>上传失败,没有任何部分被上传!";
            break;    
    }
    exit;
}

// 后端判断上传文件的大小
if($_FILES['userfile']['size'] > MAX_SIZE){
    echo "<script>alert('上传失败,上传文件超过限定值大小(由后端大小检测模块发现)!');history.back();</script>上传失败,上传文件超过限定值大小(由后端大小检测模块发现)!";
    exit;
}

// 只允许上传图片
$file_Extension_string = strrev($_FILES['userfile']['name']); // 从$_FILES数组中获取name字段并反转字符串
$file_Extension_array = explode('.',$file_Extension_string); // 以.号为分割拆分成为新的数组
$houZhui = strrev($file_Extension_array[0]); // 反转反转过的扩展名字符串存为$houZhui
// 以下两行代表的两种方法在实际应用中仅保留第二种即可(第一种抓包篡改Content-Type即可绕过,第二种即便抓包篡改Content-Disposition绕过检测也无法执行后续恶意操作‘后缀名都变了’)
$fileMimes = array('text/css'); //允许上传的文件类型,实际应用时记得写前面(方便维护人员调试)
$accepted_houzhui = array('css'); //允许上传的文件扩展名类型,实际应用时记得写前面(方便维护人员调试)
// 判断类型/后缀是否同时分别包含在两个数组
if (is_array($fileMimes)) {
    if (!in_array($_FILES['userfile']['type'],$fileMimes) || !in_array($houZhui,$accepted_houzhui)) {
        echo "<script>alert('上传失败,请选择css文件上传。(由后端类型检测模块发现)');history.back();</script>上传失败,请选择css文件上传。(由后端类型检测模块发现)";
        exit;
    }
}

// 判断目录是否存在,若无则创建
if(!is_dir(MuLu)){
    mkdir(MuLu,0777);
}

// 以上模块均没异常,开始将接收到的临时文件按照本规则重命名并移动到指定目录
if (is_uploaded_file($_FILES['userfile']['tmp_name'])) {

    if(!move_uploaded_file($_FILES['userfile']['tmp_name'],MuLu.$_FILES['userfile']['name'])){
        echo "<script>alert('【后端错误】文件移动失败!');history.back();</script>【后端错误】文件移动失败!";
        exit;
    }
    
} else {
    echo "<script>alert('【后端错误】临时文件夹找不到上传的文件!');history.back();</script>【后端错误】临时文件夹找不到上传的文件!";
    exit;
}

// 以上都正常执行,则认为文件上传成功,并显示上传成功的提示
$success = 'http://172.28.21.166:20020/upload/css/'.$_FILES['userfile']['name'];
echo " <script>    function CopyToClipboard(text) {
    var input = document.createElement(\"input\");
    var currentFocus = document.activeElement;
    document.body.appendChild(input);
    input.readOnly = 'readonly';
    input.value = text;
    input.focus();
    if (input.setSelectionRange)
      input.setSelectionRange(0, input.value.length);
    else
      input.select();
    try {
      var flag = document.execCommand(\"copy\");
    } catch (eo) {
      var flag = false;
    }
    input.blur();
    document.body.removeChild(input);
    currentFocus.focus();
    currentFocus.blur();
    if (flag){
        alert('复制成功!');
        location.href='index.html';
    } else{
        alert('复制失败');
        location.href='index.html';
    }
    return flag;
  }
   </script>";

echo "文件上传成功,地址如下:<br>";
echo $success;
echo "<br><br><a href=\"javascript:location.href='index.html';\">直接返回</a>&nbsp;&nbsp;&nbsp;<a href=\"javascript:CopyToClipboard('$success');\">复制并返回</a>&nbsp;&nbsp;&nbsp;<a href=\"$success\" target=\"_blank\">点击查看</a><style type=\"text/css\">a{text-decoration: none;color: blue;font-size:16px;}</style>";
// echo "<script>alert('文件上传成功!');location.href='index.html';</script>文件上传成功!";

// 图片上传成功的提示,随即显示图片预览
// echo "<script>alert('文件上传成功!');location.href='upload_view.php?url=".htmlspecialchars($_FILES['userfile']['name'])."';</script>";

?>
最后修改:2021 年 06 月 25 日 10 : 40 PM

发表评论