PHP脚本可以';无法打开某些URL

PHP脚本可以';无法打开某些URL,php,iframe,referrer-policy,Php,Iframe,Referrer Policy,我通过Axios调用一个PHP脚本,检查作为参数传递给它的URL是否可以嵌入iframe中。该PHP脚本首先使用$\u GET[]打开URL 奇怪的是,一个带有跨源开放策略:同源(like)的页面可以用$\u GET[]打开,而一个带有引用者策略:严格源代码(当跨源(like)时)的页面无法打开 我不明白为什么,这很烦人,因为对于无法使用$\u GET[]打开的页面,我无法对它们执行检查-脚本只是失败了(这意味着我没有得到响应,Axios调用运行catch()块) 因此,基本上有三种类型的页面:

我通过
Axios
调用一个PHP脚本,检查作为参数传递给它的URL是否可以嵌入iframe中。该PHP脚本首先使用
$\u GET[]
打开URL

奇怪的是,一个带有
跨源开放策略:同源
(like)的页面可以用
$\u GET[]
打开,而一个带有
引用者策略:严格源代码(当跨源
(like)时)的页面无法打开

我不明白为什么,这很烦人,因为对于无法使用
$\u GET[]
打开的页面,我无法对它们执行检查-脚本只是失败了(这意味着我没有得到响应,
Axios
调用运行
catch()
块)

因此,基本上有三种类型的页面:(1)允许iframe嵌入的页面,(2)不允许的页面,(3)讨厌的页面,它们不仅不允许,甚至无法打开来执行此检查

有没有一种方法可以用PHP打开任何页面,如果没有,我可以做些什么来防止脚本在几秒钟后失败

PHP脚本:

$source = $_GET['url'];
$response = true;

try {
  $headers = get_headers($source, 1);
  $headers = array_change_key_case($headers, CASE_LOWER);

  if (isset($headers['content-security-policy'])) {
    $response = false;
  }
  else if (isset($headers['x-frame-options']) &&
    $headers['x-frame-options'] == 'DENY' ||
    $headers['x-frame-options'] == 'SAMEORIGIN'
  ) {
    $response = false;
  }
} catch (Exception $ex) {
  $response = $ex;
}

echo $response;
编辑:下面是控制台错误

Access to XMLHttpRequest at 'https://path.to.cdn/iframeHeaderChecker?url=https://calia.order.liven.com.au/' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
CustomLink.vue?b495:61 Error: Network Error
    at createError (createError.js?2d83:16)
    at XMLHttpRequest.handleError (xhr.js?b50d:84)
VM4758:1 GET https://path.to.cdn/iframeHeaderChecker?url=https://calia.order.com.au/ net::ERR_FAILED

这只是我对你的代码可能有什么问题的粗略猜测

我注意到你是这样做的:

比较
$headers
中的值,但不包括 确保它们与您比较的价值具有相同的资本案例。应用:
strtoupper()

使用isset()检查,但不测试之前是否存在密钥 应用:
key\u exist()

使用isset()进行检查,但也许您应该使用
!空()
而不是
isset()
比较结果:

$value = "";
var_dump(isset($value)); // (bool) true
var_dump(!empty($value)); // (bool) false

$value = "something";
var_dump(isset($value)); // (bool) true
var_dump(!empty($value)); // (bool) true

unset($value);
var_dump(isset($value)); // (bool) false
var_dump(!empty($value)); // (bool) false
已应用更改的代码:

<?php

error_reporting(E_ALL);
declare(strict_types=1);

header('Access-Control-Allow-Origin: *');

ob_start();

try {

    $response = true;

    if (!key_exists('url', $_GET)) {
        $msg = '$_GET does not have a key "url"';
        throw new \RuntimeException($msg);
    }
    $source = $_GET['url'];

    if ($source !== filter_var($source, \FILTER_SANITIZE_URL)) {
        $msg = 'Passed url is invaid, url: ' . $source;
        throw new \RuntimeException($msg);
    }

    if (filter_var($source, \FILTER_VALIDATE_URL) === FALSE) {
        $msg = 'Passed url is invaid, url: ' . $source;
        throw new \RuntimeException($msg);
    }

    $headers = get_headers($source, 1);

    if (!is_array($headers)) {
        $msg = 'Headers should be array but it is: ' . gettype($headers);
        throw new \RuntimeException($msg);
    }

    $headers = array_change_key_case($headers, \CASE_LOWER);

    if (  key_exists('content-security-policy', $headers) &&
            isset($headers['content-security-policy'])
        ) {
            $response = false;
    }
    elseif (  key_exists('x-frame-options', $headers) && 
                (
                    strtoupper($headers['x-frame-options']) == 'DENY' ||
                    strtoupper($headers['x-frame-options']) == 'SAMEORIGIN'
                )
    ) {
            $response = false;
    }

} catch (Exception $ex) {
    $response = "Error: " . $ex->getMessage() . ' at: ' . $ex->getFile() . ':' . $ex->getLine();
}

$phpOutput = ob_get_clean();
if (!empty($phpOutput)) {
    $response .= \PHP_EOL . 'PHP Output: ' . $phpOutput;
}

echo $response;
但是

因此对于
$response=false
您将得到一个空字符串,而不是
0
如果您希望将
0
用于
false
1
用于
true
,则更改
$response=true
$response=1
为真,而
$response=false
$response=0适用于
false
所有位置


我希望这会有所帮助。您显示的错误来自Javascript,而不是PHP。在失败时返回
false
,它不会抛出异常-
catch()
从不发生
get_headers()
只是发出一个http请求,比如浏览器或curl,失败的唯一原因是URL格式不正确,或者远程站点关闭等

它是从
http://localhost:3000
https://path.to.cdn/iframeHeaderChecker
使用已被阻止的Javascript,而不是PHP访问您在
$\u GET['url']
中作为参数传递的url

当您尝试访问与Javascript运行所在域不同的域时,您看到的是一个标准CORS错误。这意味着在一台主机上运行的Javascript无法向另一台主机发出http请求,除非另一台主机明确允许。在本例中,运行在
http://localhost:3000
正在向远程站点发出http请求
https://path.to.cdn/
。这是一个跨源请求(localhost!==path.to.cdn),在
path.to.cdn
上接收该请求的服务器/脚本没有返回允许该请求的任何特定CORS头,因此请求被阻止

请注意,如果是,它将实际运行。因此,您的PHP已经一直在工作,但不会返回正确的标题,结果将被阻止在浏览器中显示。这可能会导致混淆bcs,例如,您可能会注意到,当它从慢速站点获取标题时会出现延迟,而对于快速站点,它是超高速的。或者,尽管浏览器中没有显示任何内容,您仍然可以看到日志一直在工作

我的理解是
https://path.to.cdn/iframeHeaderChecker
是您的PHP脚本,您在问题中展示了其中的一些代码?如果是,您有两个选择:

  • 更新
    iframeHeaderChecker
    以返回适当的CORS头,以便允许跨源JS请求。作为一种快速、不安全的黑客,允许任何人和任何地方访问从长远来看,这不是一个好主意!),您可以添加:

    header("Access-Control-Allow-Origin: *");
    
    但最好更新一下,更明确地限制只访问你的应用程序,而不是其他所有人。您必须根据应用程序和基础架构的具体情况来评估实现这一点的最佳方法。关于CORS/PHP/AJAX,这里有很多问题可供参考。您还可以在web服务器级别而不是应用程序级别配置它,例如

  • 如果
    iframeHeaderChecker
    与调用它的Javascript是同一应用程序的一部分,那么它在
    http://localhost:3000
    ?如果是这样,请更新JS以使用本地版本,而不是
    path.to.cdn
    上的远程版本,这样就可以避免整个问题


  • “剧本失败了”这意味着什么?你怎么知道它失败了?对不起,我的意思是我没有得到任何响应,Axios调用移动到
    catch()
    块。我将对问题进行编辑以使其更清楚,谢谢。请提供消息和/或异常类型。我们猜不到。你是对的,我在问题中添加了这些信息。因此,如果我们讨论PHP部分,可能有一个服务器规则在阻止没有指定特定头的请求。它可以是反DoS保护之类的东西,所以您可以尝试修改您的请求头(比如从浏览器中小心地取出它们),并假装是真正的访问者以通过此保护。谢谢,假设问题是由以下几点引起的,我是否仍然应该从电话中得到响应?(在我的例子中,对于某些页面,它直接进入
    catch()
    块)@drake035这可能是因为您
    $response = false;
    echo $response; // prints ""
    
    header("Access-Control-Allow-Origin: *");