BLOCK:MIXED-CONTENT报错处理-PHP的HTTPS代理

发布于 2024-03-09  482 次阅读


报错原因

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>测试</title>
    <link rel="stylesheet" href="./layui/css/layui.css">
    <script src="https://s3.pstatp.com/cdn/expire-1-M/jquery/3.2.1/jquery.min.js"></script>
      <!-- Matomo -->

<!-- End Matomo Code -->
    <script>var wrong=0</script>
</head>

<body>
    <div class="loginBox">
        <h2>测试</h2>
        <form action="" onsubmit="handleFormSubmit(event)">
            <div class="item">
                <input id="miao" type="text" required>
                <label for="">用户名</label>
            </div>
            <div class="item">
                <input id="wang" type="password" required>
                <label for="">密码</label>
            </div>
            <div class="item" style="text-align:left;width:250px;display: inline-block;">
                <input id="zheng" type="text" style="width: 100%;" required>
                <label for="">验证码</label>

            </div>

            <div id='img1' style="display: inline-block;">
                <img style="width: 60px; height: 30px;margin-left: auto;" onclick=javascript:refresh_code() id="imgcode" src="https://drinkflower.asia/api/verifycode.php" alt="验证码" />
            </div>

            <button id='bb' class="btn" onclick="gua()">登录
                <span></span>
                <span></span>
                <span></span>
                <span></span>
            </button>

        <a id="res" href="./res.html" style="position: absolute; bottom: 190px; right: 670px;font-size: smaller; color: rgb(255, 255, 255);">还没有账号?立即注册</a>
<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://cloud.drinkflower.asia', true);

xhr.onreadystatechange = function() {
  if (xhr.readyState === XMLHttpRequest.DONE) {
    if (xhr.status === 200) {
      console.log(xhr.responseText);
    } else {
      console.error('There was a problem with your XMLHttpRequest:', xhr.status);
    }
  }
};

xhr.send();

</script>

</body>

</html>

  • 这是因为https的页面加载了http的资源,不管是静态资源还是api的调用,这会导致api的调用失败
  • 最简单的解决办法是直接把提供api的服务加上证书弄成https的,或者弄一个断口转发得到服务,go的这类程序不少,而且很多都能加ssl,但是我的环境不是很方便

问题解决

  • 这里我选择使用php做转发,贴上网上嫖的一个转发的类和调用实例
<?php

class php_proxy{

    // 发送get、post请求
    public function request_option($request_url='', $method='post', $request_data=[], $to_json=false): string{
        if (empty($request_url)) {
            $back = '{"state":0, "msg":"request_url is null", "content":""}';
        }else{
            if ($method == 'post' || $method == 'POST'){
                $body = http_build_query($request_data);
                $options = [
                    'http' => [
                        'method' => 'POST', // 注意要大写
                        'header' => 'Content-type:application/x-www-form-urlencoded',
                        'content' => $body,
                        'ignore_errors'=> true, // 忽略报错,直接返回接口内容
                    ],
                ];
                $context = stream_context_create($options);
                $data = file_get_contents($request_url, false, $context);
            }else if ($method == 'get'|| $method == 'GET'){
                $curl = curl_init();
                curl_setopt($curl, CURLOPT_URL, $request_url);
                curl_setopt($curl, CURLOPT_HEADER, 0); // 不抓取头部信息。只返回数据
                curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
                curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
                curl_setopt($curl, CURLOPT_TIMEOUT, (int)60000); // 超时设置
                curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 1表示不返回bool值
                $data = curl_exec($curl);
                // $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); // 获取接口状态码
                curl_close($curl);
            }else{
                $data = '{"state":0, "msg":"method error. method is only in [get, post], options etc be not supported.", "content":""}';
            }

            $back = $data;
        }

        if ($to_json == true){
            $res = json_encode($back, true);
        }else{
            $res = $back;
        }

        return $res;
    }

    // 获取完整网址
    public function get_url(): string{
        if(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
            $url = 'https://';
        }else{
            $url = 'http://';
        }
        return $url.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].$_SERVER['REQUEST_URI'];
    }

    // 是post
    public function is_post(): bool{
        return isset($_SERVER['REQUEST_METHOD']) && strtoupper($_SERVER['REQUEST_METHOD']) == 'POST';
    }

    // 是get
    public function is_get(): bool{
        return isset($_SERVER['REQUEST_METHOD']) && strtoupper($_SERVER['REQUEST_METHOD']) == 'GET';
    }

    // 生成内网网址:把外网网址解析到内网网址
    public function make_request_url($host, $intranet): string{
        //echo str_replace($host, $intranet, $this->get_url());
        return str_replace($host, $intranet, $this->get_url()); // 实际代理地址(就是替换主网址或路径)
    }

    // 转发接口
    // 请从此处调用
    public function request_intranet($host, $intranet): string{
        // 测试的内网请求地址:$request_url = http://127.0.0.l:8000/api.gen1/admin
        $request_url = $this->make_request_url($host, $intranet);
        if ($this->is_post()){
            $request_array = $_REQUEST; // 请求参数数组
            $back = $this->request_option($request_url, 'post', $request_array, false);
        }else if ($this->is_get()){
            $request_array = [];
            $back = $this->request_option($request_url, 'get', $request_array, false);
        } else{
            $back = '{"state":0, "msg":"method error. method is only in [get, post], options etc be not supported.", "content":""}';
        }
        return $back;
    }

}

?>
  • 脚本可以区分get和post请求(也只能转发这两种),默认是从目标获取text/plain的返回值
  • 比如php的生成
echo 1;
  • python的生成
response = make_response("f")
response.status_code = 200
return response
  • 也可以改request_option方法的to_json参数为true,就能进下面的if,接收json并且解码返回,或者自己对$back变量写一个转码就行
if ($to_json == true){
            $res = json_encode($back, true);
        }else{
            $res = $back;
        }

        return $res;
  • 调用这个类只需要传入下面几个参数
$host = "https://drinkflower.asia:443/test.php"; // 外网网址(主网址或有部分路径)
$intranet = 'https://drinkflower.asia:443/post.php'; // 内网网址(主网址或有部分路径)
$php_proxy = new php_proxy();
$back = $php_proxy->request_intranet($host, $intranet);
  • 我们在可以看到,脚本对请求的处理是接收参数,然后重新对目标网址用curl发送请求,把request_intranet echo出来试试

  • 所以$host参数必须是脚本所在文件的全部路径,否则其它的参数可能会被当作参数传给$intranet网址

完整脚本和一些问题

  • 贴上完整的类和利用
<?php

class php_proxy{

    // 发送get、post请求
    public function request_option($request_url='', $method='post', $request_data=[], $to_json=false): string{
        //echo $request_url;
        if (empty($request_url)) {
            $back = '{"state":0, "msg":"request_url is null", "content":""}';
        }else{
            if ($method == 'post' || $method == 'POST'){
                $body = http_build_query($request_data);
                $options = [
                    'http' => [
                        'method' => 'POST', // 注意要大写
                        'header' => 'Content-type:application/x-www-form-urlencoded',
                        'content' => $body,
                        'ignore_errors'=> true, // 忽略报错,直接返回接口内容
                    ],
                ];
                $context = stream_context_create($options);
                $data = file_get_contents($request_url, false, $context);
            }else if ($method == 'get'|| $method == 'GET'){
                $curl = curl_init();
                curl_setopt($curl, CURLOPT_URL, $request_url);
                curl_setopt($curl, CURLOPT_HEADER, 0); // 不抓取头部信息。只返回数据
                curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
                curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
                curl_setopt($curl, CURLOPT_TIMEOUT, (int)60000); // 超时设置
                curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 1表示不返回bool值
                $data = curl_exec($curl);
                // $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); // 获取接口状态码
                curl_close($curl);
            }else{
                $data = '{"state":0, "msg":"method error. method is only in [get, post], options etc be not supported.", "content":""}';
            }

            $back = $data;
        }

        if ($to_json == true){
            $res = json_encode($back, true);
        }else{
            $res = $back;
        }

        return $res;
    }

    // 获取完整网址
    public function get_url(): string{
        if(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
            $url = 'https://';
        }else{
            $url = 'http://';
        }
        return $url.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].$_SERVER['REQUEST_URI'];
    }

    // 是post
    public function is_post(): bool{
        return isset($_SERVER['REQUEST_METHOD']) && strtoupper($_SERVER['REQUEST_METHOD']) == 'POST';
    }

    // 是get
    public function is_get(): bool{
        return isset($_SERVER['REQUEST_METHOD']) && strtoupper($_SERVER['REQUEST_METHOD']) == 'GET';
    }

    // 生成内网网址:把外网网址解析到内网网址
    public function make_request_url($host, $intranet): string{
        //echo str_replace($host, $intranet, $this->get_url());
        return str_replace($host, $intranet, $this->get_url()); // 实际代理地址(就是替换主网址或路径)
    }

    // 转发接口
    // 请从此处调用
    public function request_intranet($host, $intranet): string{
        // 测试的内网请求地址:$request_url = http://127.0.0.l:8000/api.gen1/admin
        $request_url = $this->make_request_url($host, $intranet);
        if ($this->is_post()){
            $request_array = $_REQUEST; // 请求参数数组
            $back = $this->request_option($request_url, 'post', $request_array, false);
        }else if ($this->is_get()){
            $request_array = [];
            $back = $this->request_option($request_url, 'get', $request_array, false);
        } else{
            $back = '{"state":0, "msg":"method error. method is only in [get, post], options etc be not supported.", "content":""}';
        }
        return $back;
    }

}

// 使用参数化查询构建 SQL 查询语句
$host = "https://drinkflower.asia:443/test.php"; // 外网网址(主网址或有部分路径)
$intranet = 'https://drinkflower.asia:443/post.php'; // 内网网址(主网址或有部分路径)
$php_proxy = new php_proxy();
$back = $php_proxy->request_intranet($host, $intranet);
echo $back;

?>
  • 先发一个get用post.php测试一下效果,内容如下
<?php
header('Content-Type: text/plain');

if(isset($_GET)){
     var_dump($_GET);
    exit;
}

  • 可以看到转发成功了,那么换成post呢
<?php
header('Content-Type: text/plain');
if(isset($_POST)){
    echo var_dump($_POST);
    exit;
}

  • 也成功了,但是如果深入测试一下,在这里再传一个get,就会发现实际的post把get的参数也post进去了

  • 这是因为原作者在request_intranet函数里面处理post的参数的时候,把内容存在了$_REQUEST超全局变量里面,把get的一起处理了,想要避免这个问题只需要把这个函数里面的$request_array = $_REQUEST;改成$request_array = $_POST;就可以了,不过作者这么写可能有他的深意吧,能跑就不动他了😋

  • 这个类还有一个问题,如果我们的脚本里面对post和get都有使用,那么get的请求就失效了(这曾经困扰了我很久😭),比如下面这个post.php,我们发送一个get

<?php
header('Content-Type: text/plain');
if(isset($_POST)){
    echo var_dump($_POST);
    exit;
}
if(isset($_GET)){
    echo var_dump($_GET);
    exit;
}

  • 可以看到get的参数接收不到了,因为函数进了post的if,但是没有post任何参数,所以返回为空就exit了,解决方法是在所有的isset旁边再加上传参是否为空的判定
if(isset($_POST)&&$_POST!=null){
    echo var_dump($_POST);
    exit;
}
if(isset($_GET)&&$_GET!=null){
    echo var_dump($_GET);
    exit;
}

  • 最后,php用curl一定顺手加上curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);,不知道为什么在我的环境里面证书出现了问题但是没有任何报错,导致请求成功,所有状态码都正常,但是请求不到数据
届ける言葉を今は育ててる
最后更新于 2024-03-09