整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

script标签使用和输出语句(二)

script标签使用和输出语句(二)

录:

  1. script标签
  2. 输出语句

一、script标签

  1. 在html中直接写:
<!DOCTYPE html>
<html>
<head>
	<title></title>
</head>
<body>

<script type="text/javascript">
//这里可以写js代码了
</script>

</body>
</html>

2. 创建js文件与html文件分离:

<!DOCTYPE html>
<html>
<head>
	<title></title>
</head>
<body>

<script type="text/javascript" src='script.js'></script>

</body>
</html>

然后就可以在script.js中写js代码了。

二、js输出语句

js输出语句比较多,比如document.write、console.log等。

1.document.write:在页面中输出文本内容。

代码:

<!DOCTYPE html>
<html>
<head>
	<title></title>
</head>
<body>


<script type="text/javascript">
document.write("输出文本内容了")
</script>

</body>
</html>

效果图:

2.console.log:在控制台输出内容。

代码:

<!DOCTYPE html>
<html>
<head>
	<title></title>
</head>
<body>


<script type="text/javascript">
console.log("输出文本内容");
</script>

</body>
</html>

效果图:

console是在控制台中显示,在浏览器右击有一个审查元素(或者F12),就可以看到Console这个内容了。

文地址:Script Integrity[1]

原文作者:Chris Coyier[2]

最近有很多关于 polyfill.io 的新闻。几年前,这个项目很热门,因为你只需引用它,它就会根据浏览器 UA 及其 API 特性支持情况,自动为你添加相对应的 polyfill。现在如果你按他们官网主页的建议,从他们的域名加载了脚本,你的网页可能会被注入一些恶意代码。

安全漏洞情报公司说,恶意代码可能是基于用户当前位置,然后把他们重定向到体育博彩网站或成人域名。 SecurityWeek[3]

该项目现已下线,但截至 2024 年 6 月 1 日,他们建议采用这种直接链接的方式加载脚本。

当你加载一个你无法控制的域名上的任何资源时,这就是一种风险。这是一种可预期的风险,例如,很多网站都会从 google.com 上加载脚本,用于 Google Analytics;或者从 adobe.com 上下载字体用于排版。这些都是值得信任的资源,但这种信任完全取决于你。

防止资源变化

web 中有个属性可以防止第三方更改他们提供的代码。这就是 <script><link> 标签(rel="stylesheet", rel="preload"rel="modulepreload")上的 integrity 属性[4]。完整的名称是“子资源完整性”。

举个例子:

<script
  src="https://third-party.com/script.js"
  integrity="sha384-[hash value here]">
</script>

现在,如果 script.js 有任何变化,哪怕只是一个字符,浏览器都会拒绝执行脚本(或样式表)。非常安全!

一些负责任的第三方会直接提供这一功能,这一点很好。

instant.page 提供的脚本,并提供完整性属性以确保安全。

CDNjs 会将 integrity 属性作为你复制和粘贴时默认代码的一部分。

我特别喜欢完整性提供的保护,它还能防止一些可能的中间人攻击。如果你的酒店 WiFi 不正规,它会拦截请求并更改响应(我见过!),完整性验证就会阻止被篡改的脚本执行。除非他们也篡改 HTML 并更改属性值,这当然是有可能的,因为更改 HTML 正是侵入式应用所做的事情。不过,大多数安全措施都是 “能做多少就做多少”,这样做还是有帮助的。

何时不使用脚本完整性

上述两个例子的脚本链接地址比较好,因为它们的地址都是有版本控制的。它们发布的是确切的版本,而且该版本永远不会更改。这是开源社区的版本管理约定,当公共库发布后,该版本的代码就会被锁定,任何更改都需要升级版本。如果代码在没有版本更新的情况下发生了变化,那将是非常可疑的,很可能是恶意的,也是使用 <script integrity="..."> 的最佳情况。此外,发布库的主要仓库(如 npm)无论如何都不允许更改已发布版本的代码。

虽然脚本完整性通常是一个很好的方案,但它是专门用于不会改变的版本化资源。如果出现以下情况,就不能使用它:

  • 你链接的资源内容会更改
  • 你提供了一个打算能即时更改的资源

也许你正在使用某个分析服务提供商提供的脚本。他们在给你提供脚本时,很可能不会使用 integrity 属性。这很可能是因为他们希望能够积极开发该脚本及其功能,而不必告诉每一位客户他们需要更新脚本的版本,否则脚本就会停止工作。

具有讽刺意味的是,公司能够即时更新脚本意味着他们有可能修补安全问题。

脚本完整性能否阻止 Polyfill.io 问题?

也许可以,这取决于攻击者做了什么。有些报道[5]是这么说的:

恶意代码根据 HTTP 请求头动态生成响应内容,如仅在特定移动设备上激活,从而躲避检测、避开管理员用户并延迟执行,同时代码还被混淆。

所以如果脚本出现了问题,它的响应内容与创建完整性值时的内容不同,那么浏览器就会阻止这些恶意更改的运行。

但它们也可能躲过这种阻止。

polyfill.io 的工作方式是根据需要去加载附加内容(即 polyfill 本身)。这些额外加载的内容可以被改成恶意内容,而且不会受到子资源完整性的限制。我并不想让坏人活得更轻松,只是说说而已。

如何自行操作

你不用一定要求第三方库提供此属性来给你使用,这只是 web 的一项功能,你想用就用。

也许更简单的方法是访问 SRI 哈希值生成器网站,输入要保护的资源的 URL,然后点击按钮获取代码:

注意:我看到很多地方推荐这个网站,但当我使用时,它似乎并不适合我。例如,上面的代码:

<script src="https://assets.codepen.io/3/log-something.js" integrity="sha384-ZTxYWn5UcuOi7Xt1wWg/QFcsZJEXjJg7tgCnVbx0+ssBvbi1Sw/hY5GzPGHSD1NW" crossorigin="anonymous"></script>

在 Chrome 浏览器中打开,我的 integrity 属性并不对。我不得不让 Chrome 浏览器在控制台中显示错误信息,并寻找错误信息中提供正确的哈希值,然后使用修正后的哈希值才能正常工作:

<script src="https://assets.codepen.io/3/log-something.js" integrity="sha384-H7W+IxM2qbwMSJYRqmcgYXq0TRko/BIFtURUjh2QG0Z8MM9I6t2f4n+2BOCmbjvD" crossorigin="anonymous"></script>

所以,你的情况可能会有所不同。这里有个代码演示地址[6]

值得注意的是,其中还涉及 CORS。如果没有 crossorigin="anonymous",上面的代码中就会看到了 CORS 错误,尽管我们在提供资源时使用了 Access-Control-Allow-Origin: * 响应头。耸耸肩 - 网站很难做。

参考资料

[1] Script Integrity: https://frontendmasters.com/blog/script-integrity/

[2] Chris Coyier: https://frontendmasters.com/blog/author/chriscoyier/

[3] SecurityWeek: https://www.securityweek.com/polyfill-supply-chain-attack-hits-over-100k-websites/

[4] integrity 属性: https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity

[5] 有些报道: https://cside.dev/blog/more-than-100k-websites-targeted-in-web-supply-chain-attack

[6] 代码演示地址: https://codepen.io/chriscoyier/pen/qBzWMxQ?editors=1000

明:文章内容仅用于学习交流,切勿不当使用。

上网的时候经常会遇到网页禁止复制文本或者禁止文档下载的情况。今天结合案例分析下实现这些限制的手法和解除办法。

1、文档复制

首先创建一个demo.html的文档,文档内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DEMO</title>
    <style>
        body {background-color: aqua;}
    </style>
</head>
<body>
    <div class="content">测试文本测试文本测试文本</div>
    <script>
        let htmlEle=document.documentElement;
        let bodyEle=document.querySelector('body');
        htmlEle.addEventListener('selectstart', (e)=> {
            e.preventDefault();
        });
        bodyEle.addEventListener('copy', (e)=> {
            e.preventDefault();
        });
        bodyEle.addEventListener('selectstart', (e)=> {
            e.preventDefault();
        });
        bodyEle.addEventListener('contextmenu', (e)=> {
            e.preventDefault();
        });
    </script>
</body>
</html>

打开文档可以发现,页面文本是无法选择并复制的。因为如代码所示,页面元素被绑定了几个事件(选择、拷贝、右键菜单),阻止了用户对文本的复制企图。

怎么办呢?

打开浏览器的开发者工具,切换到Elements标签下,选择文本元素,这时可以在下方的“事件监听器”中查看到目标元素及其祖先元素上绑定的事件。展开这些相关的事件,可以看到“移除”按钮,接下来只需点击按钮将这些限制事件移除便可以复制了。

上述办法是在事件绑定后,再将它们移除。此外,也可以通过抓包改包的方式移除绑定事件相关的代码,从源头阻止事件的绑定。这需要抓包工具的辅助,这里用到的是Fiddler。

假设网页部署在本地服务器上,首先,打开Fiddler,在右侧切换到“自动转发”面板(带闪电图标的),勾选图中两个选项;然后添加规则,填上要更改的请求地址以及要替换的本地文件(假设为demo1文件,内容如下),填好后保存。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DEMO</title>
    <style>
        body {background-color: #dedede;}
    </style>
</head>
<body>
    <div class="content">测试文本测试文本测试文本</div>
</body>
</html>

设置好以后再次刷新网页,会发现页面的背景色发生了变化,而且文本也可以直接复制了。

2、文档下载

此处以网上的一个文档为例,文档可以在线查看,但是无法直接下载。通过开发者工具选中文档,可以看到它对应了一个img标签(以前是canvas),我们可以将图片保存到本地,但是如果页面比较多,手动操作就很麻烦,所以可以用代码帮我们自动执行,将图片合并成pdf文件并下载到本地。

以下代码可做参考:

function loadScript (url) {
  let ele=document.createElement('script')
  ele.src=url
  document.body.appendChild(ele)
}
function img2dataUrl (options) {
  let result=''
  let img=options.img || ''
  let width=options.width || img.naturalWidth || img.clientWidth
  let height=options.height || img.naturalHeight || img.clientHeight
  let quality=options.quality || 100
  let mimeType=options.mimeType || 'image/png'
  let canvas=document.createElement('canvas')
  canvas.width=width
  canvas.height=height
  let ctx=canvas.getContext('2d')
  ctx.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight, 0, 0, width, height)
  result=canvas.toDataURL(mimeType, quality / 100)
  return result
}

loadScript('https://cdn.bootcdn.net/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js')

function img2pdf () {
  let imgArr=[...document.querySelectorAll('.reader_inner img')]
  if (imgArr.length===0) return
  let doc=new jspdf.jsPDF({unit:'px'});
  imgArr.forEach((v,i)=> {
    v.setAttribute("crossOrigin",'anonymous');
    let pxPermm=v.width / 210 / 2.2;
    let imgData=img2dataUrl({img:v}).slice('data:image/png;base64,'.length);
    (i > 0) && doc.addPage();
    doc.addImage(imgData, 'png', 0, 0, v.width / pxPermm, v.height / pxPermm);
  })
  doc.save('img2pdf.pdf');
}

将代码放到控制台或者代码段中执行,执行后,再在控制台调用 img2pdf() 方法即可将可浏览的页面合并成PDF下载到本地。