分类 后端 下的文章

美元符$和变量插值

众所众知,PHP中每一个变量,都需要以美元符$开头来表示,这是PHP诸多被诟病的点之一。

然而最近我领悟到美元符也有一点好处,就是:能方便的将一个变量嵌入到字符串中,也就是所谓变量插值

在PHP中,$a='hello';echo "$a world";即可打印出hello world。也就是当读取到$时,解释器知道这是一个变量,从而将变量替换成对应的值。正是有$的存在,使得字符串拼接变得相对自然。

其他语言则不一定有这样功能,将变量嵌入到字符串中,大致就有两种办法:

– 字符串拼接。如:var a = 'hello'; console.log(a+'world')(js)

– 要么就需要使用占位符。如:printf("%s,world","hello") (C语言)

相比之下,变量插值的方式更自然一些。

注:

– 不止是PHP,其他一些带有$的语言也有变量插值的功能,例如Perl?我不太清楚

– JS新标准es6,也提供了变量插值的功能,如:

var a = 'hello'; console.log(`${a} world`)
//hello world

http 请求头 Referer 字段的一点研究

之前对这个字段只有个模糊的认识,这两天集中实验了一下,一些概念也比较清楚了,记录一下。

首先,这个字段表明的是,当前请求来自何处。比如说如果从百度搜索的结果中,点击了到了新网页,这时的请求就会加上Referer字段,表明是从百度跳转来的。

但是这个字段有点古怪,而且并不是每一次跳转都会加上这个字段,另外这个字段是可以伪造的,所以不能尽信。

特性

根据实验,得出两个有趣的现象:
– 通过http状态码来跳转(重定向),不会有此字段。
也就说,访问A页面,然而A页面返回302/301,Location:让你去B页面,这时候在B页面上,是不知道你从A页面来的。
– 通过 JS 跳转会加上这个字段。在控制台中实验:window.location.href = '新页面地址' ,这是的跳转中会有此字段。

也就说,如果想让你的重定向有Referer字段,最好是通过js来跳转,输出一段js脚本,修改window.locaion.href, 等浏览器执行此代码时帮你跳转。

另一个值得关注的现象:通过图片、js脚本去加载的资源,请求中也会带上Referer参数,表明是从哪个站点请求的。

比如很多网站都会使用第三方统计服务,需要加载第三方js,此时会将Referer信息也加载头部,对方是能知道这个请求来自哪个站点(如果对方想的话)。

作用

  • 这就引出了Referer的一大作用:放盗链,特别是图片防盗链。在服务器上判断如果图片请求来自非正常的站点,则可以返回状态码502/404/403等等,或者直接返回一张此图片表明只供内部使用。

  • 加上前面说的,跳转点击会加上Referer信息,那么利用这一特性,可以实现网站统计服务:统计从搜索引擎来的流量(或者其他的来源网站),统计来源的搜索词等信息。

并不可靠

前面说到这个字段并不可靠,主要是这个字段是可以伪造的!(当然还有其他字段可以伪造,比如User-Agent)

比如你在后端发请求时,就可以像修改User-Agent一样轻易修改这个字段,以期骗过服务器的某些策略,或者制造某种假象。

比如在使用第三方统计服务时,有时候就发现来源网站这一栏中,有奇怪的网站,从未听过,更不可能在这类网站有外链,通常还有有些信息:SEO联系QQxxx之类的,无非就是广告。大概也是用的修改Referer的办法来做到的。

如何用PHP实现反向代理

按道理请求转发并不是PHP所擅长的,至少有更好的可以做到的办法能做到,在服务器层面Nginx能很容易做到(http_proxy),在语言层面我感觉node.js会更好写。但有时候环境所限,就是有这种需求,在此记录一下。

梳理下原理

抛开PHP这个语言因素,从反向代理这个需求入手,做的无非就是一个事:转发请求。客户端发送到A的请求,A转发到B,再把从B处得到的响应返回给客户端。
那么就可以大致知道,A想要实现这个请求转发的功能,就需要做的是:

  • 判断这个请求的方法:get/post
  • 根据请求方法的不同,从请求中解析出参数/数据
    • 如果是get请求,就比较简单,从URL中就可以直接拿到参数了
    • 如果是post请求,就先要判断请求的Content-Type:
      • 如果是 application/x-www-form-urlencoded,就可以从$_post中拿数据了,
      • 如果是其他情况,比如:multipart/form-data等设计文件操作,或者是传递是文本数据(raw data),就要另外考虑了。因为很难在几十行的代码里把所有情况列清楚,这里也只着考虑上一种情况。
  • 根据解析出来的数据/参数,构造一个类似的请求,发送到B
  • 将拿到的响应返回给客户端

根据以上步骤就可以开始写代码了,来试试反向代理百度:

<?

$host = "http://www.baidu.com";
$path = $_SERVER['REQUEST_URI'];
$url = $host.$path;

switch ($_SERVER['REQUEST_METHOD']) {
    case "GET":
        $HTML = sendRequest($url,'GET',null);
        break;
    case "POST":
        switch($_SERVER['CONTENT_TYPE']) {
            case 'application/x-www-form-urlencoded':
                $HTML = sendRequest($url,'POST');
                break;        
            //暂不考虑其他数据上传的情况
        }
    default:
        exit;
}
echo $HTML;

function sendRequest($url,$type){
    $curlHanddle = curl_init();
    if($type == 'POST'){
        //如果是post请求,则设置相关post参数和传递数据
        curl_setopt($curlHanddle, CURLOPT_POST,true);
        $strPOST = json_encode($_POST);
        curl_setopt($curlHanddle, CURLOPT_POSTFIELDS,$strPOST);
    }

    curl_setopt($curlHanddle, CURLOPT_URL, $url);
    curl_setopt($curlHanddle, CURLOPT_RETURNTRANSFER, 1 );
    $resultContent = curl_exec($curlHanddle);
    $resultStatus = curl_getinfo($curlHanddle);
    curl_close($curlHanddle);
    if(intval($resultStatus["http_code"])==200){
        return $resultContent;
    }else{
        return false;
    }
}

通过几十行代码,一个PHP反向代理的demo就出来了,当然完成度还比较低,http博大精深很多情况没考虑,但思路大致如此,运行试试:


跑起来了!

cookie的secure属性引发的bug

https化的大潮中,大多网站不是一步到位的,基于各种原因,并不会强制https访问,大多同时存在过http/https均可访问的情况。

前段时间碰到过由此引起的一个cookie传递问题,值得记录一下:
问题的表现是:在用户在https访问的情况下登录了账号,这时候如果回到http版本,则并没有登录,而且重新登录也登不上去!

与登录相关的,首先想到的当然是cookie的问题,仔细调试才发现,在https访问的情况下,设置cookie,会多带了一个secure的属性,而http访问则不会带。

这个属性是一个布尔值,真或者假,为真时表示这个cookie值只在htts的情况下,才会随着请求一起发送给服务器。这个属性不太容易被重视到,因为一般来说设置cookie参数可能只会关注到域名、路径、过期时间等属性,而且这个secure属性,默认是false,也不会引发什么问题。

但如果网站同时允许https和http访问,并且在设置cookie时把访问协议考虑其中,例如通过是https或者443端口访问时,设置cookie是让secure为true。这样的出发点本来是好的,但是却引发一个问题:如果有用户先是通过http访问,并在一些交互过程中,给浏览器种下了cookie,此时secure为true,如果此时由于某种原因,用户通过https访问,则之前的设置的cookie则不会携带。这也就是为什么,在https环境下登录的用户,到了http,却自动退出了,其实并没有退出,只是secure设为了true,cookie没有传递。

需要小心,特记录一下。