使用PHP代理脚本加载iframe的实现方案是否存在安全隐患?
我们网站使用某可信第三方提供的工具,原有流程如下:
- 用户点击链接
- 打开模态窗口(modal window),其中包含指向第三方域名的iframe
- 用户与工具交互完成后,iframe向我方发送消息(iframe message)
- 我方接收消息后关闭模态窗口并执行后续操作
这套流程运行正常,但第三方录屏服务常因iframe跨域无法录制其中的交互内容,导致录屏里的iframe区域显示空白。虽然部分录屏服务商支持跨域iframe录制,但我们不想受限于此类服务商。
因此计划部署自有域名下的PHP代理脚本作为中转,让iframe指向自有域名以解决录屏问题。我们信任第三方来源,不担心其恶意行为,重点关注黑客利用代理脚本实施攻击的风险,现询问以下代码是否存在需修复的安全漏洞?
相关代码片段
1. 创建iframe的JavaScript代码
window.openProxiedIframe = href => { let url = new URL('https:' + href); let queryString = "?"; for (const [key, value] of url.searchParams) { queryString = `${queryString}${key}=${value}&`; } let proxyURL = "https://www.example.com/proxy.php" + queryString; if ( jQuery("#designTool").length && jQuery("#designTool").hasClass("ui-dialog-content") ) { jQuery("#designTool").dialog("destroy"); jQuery("#designTool").remove(); } jQuery(`<div id='designTool'><iframe style='height: 100%; width: 100%; margin: 0px; border: none;' src='${proxyURL}' /></div>`).appendTo("body").dialog({ modal: true, title:"Design Your Item" }); };
2. PHP代理脚本(proxy.php)
// it's not important what a and b are, what matters is that they are required, so // any legit request will have them. Also note that example.com here is really // a third-party domain, not us. Again, the third-party is trusted, and // I hardcoded their domain to prevent anyone from trying to force this // script to open some other page on the internet if ( isset( $_REQUEST["a"] ) && isset( $_REQUEST["b"] ) ) { $url = "https://www.example.com/designTool?a=" . urlencode($_REQUEST["a"]) . "&b=" . urlencode($_REQUEST["b"]); $contents = file_get_contents( $url ); echo $contents; }
3. 监听iframe消息的JavaScript代码
// usingProxy is defined elsewhere as true if we are using the proxy.php script. // In case we are NOT using the proxy, I check that the origin is // the third-party domain. Otherwise, I check that the origin is our domain window.addEventListener("message", function(event){ if ( event.origin === "https://www.example.com" || ( usingProxy && (window.location.protocol + '//' + window.location.host).indexOf(event.origin) === 0 ) ) { // ok then, this is our message, go ahead and do stuff with it, close the modal, and stop listening for more messages } });
一、PHP代理脚本的风险
SSRF(服务器端请求伪造)风险
虽然硬编码了第三方域名,但file_get_contents默认会跟随重定向。如果第三方的designTool接口存在跳转逻辑,黑客可构造a/b参数,诱导请求跳转到服务器本地IP或内网服务,进而探测、攻击内部系统。DoS攻击隐患
未对请求的资源大小、响应时长做限制。黑客可反复请求大体积资源耗尽服务器带宽,或请求耗时极长的接口占用服务器进程,拖慢整体服务。参数校验缺失
仅检查a/b参数是否存在,未验证参数格式。即使urlencode避免了URL注入,恶意参数仍可能导致第三方返回异常内容,引发前端潜在风险。
二、前端代码的风险
XSS注入风险
直接用模板字符串拼接proxyURL到iframe的src属性中,若proxyURL包含单引号等特殊字符,会导致属性闭合,触发XSS攻击(比如构造proxyURL为https://example.com/proxy.php?a=' onload='alert(1))。消息监听逻辑漏洞
使用indexOf判断origin的写法存在缺陷:若攻击者构造类似https://www.example.com.attacker.com的域名,indexOf会返回0,误判为可信来源,导致恶意消息被接收,触发后续逻辑操作。
三、修复建议
PHP代理脚本修复
- 限制重定向与超时
配置file_get_contents禁止跟随重定向,并设置超时时间,阻断SSRF路径:
$context = stream_context_create([ 'http' => [ 'follow_location' => false, 'timeout' => 5 ] ]); $contents = file_get_contents($url, false, $context);
- 添加资源大小限制
返回内容前检查大小,超过阈值则拒绝响应:
$maxSize = 10 * 1024 * 1024; // 限制为10MB if(strlen($contents) > $maxSize) { http_response_code(413); exit("Request entity too large"); }
- 严格校验参数格式
根据第三方接口要求,对a/b做格式校验(比如字符范围、长度限制):
if(!preg_match('/^[a-zA-Z0-9]{1,32}$/', $_REQUEST['a']) || !preg_match('/^[a-zA-Z0-9]{1,32}$/', $_REQUEST['b'])) { http_response_code(400); exit("Invalid parameters"); }
前端代码修复
- 安全设置iframe的src属性
改用jQuery的attr方法设置src,避免字符串拼接带来的注入风险:
let $iframe = jQuery('<iframe style="height: 100%; width: 100%; margin: 0; border: none;">'); $iframe.attr('src', proxyURL); jQuery('<div id="designTool"></div>').append($iframe).appendTo('body').dialog({ modal: true, title: "Design Your Item" });
- 修复origin判断逻辑
使用全等匹配替代indexOf,确保origin完全一致:
window.addEventListener("message", function(event){ const ourOrigin = window.location.protocol + '//' + window.location.host; if ( event.origin === "https://www.example.com" || (usingProxy && event.origin === ourOrigin) ) { // 执行后续操作 } });
内容的提问来源于stack exchange,提问作者Mike Willis

