2011-08-21 149 views
3

HTML Purifier或CSRF Magic如何攔截HTTP請求?它的文檔說它基於Django中間件Python框架的想法,但我還沒有找到一些關於如何攔截HTTP請求的文檔。它的工作原理並不需要安裝任何PHP擴展。HTML淨化器攔截請求

任何人都可以解釋一下這個問題?

問候

回答

1

CSRF Magic使用PHP的output control函數。它捕獲腳本的輸出,修改它,並使用特殊的處理函數修改它在打印之前捕獲的輸出。所以,真正的魔力在於ob_start。如果你有興趣,請閱讀它。此外,由於CSRF Magic是一個開源項目,您可以read the script itself for more detailed information

它最終歸結爲line 371

if ($GLOBALS['csrf']['rewrite'])  ob_start('csrf_ob_handler'); 

該行表示,如果條件爲真(它通常是這樣),當輸出完成後啓動輸出緩衝(ob_start)和, ,在該輸出上運行csrf_ob_handlercsrf_ob_handler修改腳本的原始輸出以添加隱藏的輸入,然後輸出結果。

+0

'$ GLOBALS ['csrf'] ['rewrite']'[什麼是...代碼](http://www.bigbible.org/blog/uploaded_images/242640415_46bf42b3a7-784053.jpg) –

+0

無論如何,這是行XP – Matchu

-1

如果沒有笑話 - 「攔截」 HTTP請求和響應發送什麼是任何Web應用程序應該做的。
而且您不需要任何擴展,因爲PHP的設計正好是創建Web應用程序的工具。
要讀取HTTP請求的數據,請使用數組$ _GET,$ _POST,$ _SERVER(有時需要php://input),併發送響應,您可以僅使用echo "that's my response!";

我甚至可以給你我的兩個班,請求和響應工作,希望這將是有用的(或只是有趣,至少):

<?php 
namespace Jamm\HTTP; 

class Request 
{ 
    protected $method; 
    protected $headers; 
    protected $data; 
    protected $accept; 

    const method_GET = 'GET'; 
    const method_POST = 'POST'; 
    const method_PUT = 'PUT'; 
    const method_DELETE = 'DELETE'; 

    /** 
    * @param bool $parse - parse current input to object's variables (input request) 
    * @return \Jamm\HTTP\Request 
    * 
    */ 
    public function __construct($parse = false) 
    { 
     $this->method = self::method_GET; 
     if ($parse) $this->BuildFromInput(); 
     $this->setHeader('Content-type', 'text/plain'); 
    } 

    public function BuildFromInput() 
    { 
     $this->headers = $_SERVER; 
     $this->accept = isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : ''; 
     $this->method = $_SERVER['REQUEST_METHOD']; 
     switch ($this->method) 
     { 
      case 'HEAD': 
      case 'GET': 
       $this->data = $_GET; 
       break; 
      case 'POST': 
       $this->data = $_POST; 
       break; 
      default: 
       parse_str(file_get_contents('php://input'), $this->data); 
     } 
    } 

    /** 
    * Return header from array by key, or all keys 
    * @param string $key 
    * @return null|array|mixed 
    */ 
    public function getHeaders($key = null) 
    { 
     if (!empty($key)) 
     { 
      return isset($this->headers[$key]) ? $this->headers[$key] : NULL; 
     } 
     else return $this->headers; 
    } 

    /** 
    * Get type of request method 
    * @return string 
    */ 
    public function getMethod() 
    { 
     return $this->method; 
    } 

    /** 
    * Return key or all the keys of request 
    * @param string $key 
    * @return null|array|string|numeric 
    */ 
    public function getData($key = null) 
    { 
     if (empty($key)) return $this->data; 
     else 
     { 
      return isset($this->data[$key]) ? $this->data[$key] : NULL; 
     } 
    } 

    /** 
    * Return HTTP_ACCEPT header 
    * @return string 
    */ 
    public function getAccept() 
    { 
     return $this->accept; 
    } 

    /** 
    * Check, if this type is acceptable 
    * @param string $type 
    * @return bool 
    */ 
    public function isAcceptable($type) 
    { 
     if (empty($type) || (stripos($this->getAccept(), $type)!==false)) return true; 
     return false; 
    } 

    public function setHeader($header, $value) 
    { 
     $this->headers[$header] = $value; 
    } 

    /** 
    * Set the request method 
    * @param $method 
    * @return void 
    */ 
    public function setMethod($method) 
    { 
     $this->method = strtoupper($method); 
     if ($this->method!=self::method_GET) $this->setHeader('Content-type', 'application/x-www-form-urlencoded'); 
    } 

    public function setDataKey($key, $value) 
    { 
     $this->data[$key] = $value; 
    } 

    public function SetAccept($accept) 
    { 
     $this->accept = $accept; 
    } 

    /** 
    * Send request by URL. Pass $Response argument, if you need response 
    * @param $URL 
    * @param IResponse|null $Response 
    * @return bool|IResponse 
    */ 
    public function Send($URL, IResponse $Response = NULL) 
    { 
     $url_data = parse_url($URL); 
     $fp = fsockopen($url_data['host'], 80); 
     if (!$fp) return false; 
     $path = (isset($url_data['path']) ? $url_data['path'] : '/'). 
       (isset($url_data['query']) ? '?'.$url_data['query'] : ''); 
     $data = $this->getData(); 
     if (!empty($data) && is_array($data)) $data = http_build_query($data); 

     if ($this->method==self::method_GET) 
     { 
      fwrite($fp, $this->method." $path?$data HTTP/1.0\r\n"); 
     } 
     else 
     { 
      fwrite($fp, $this->method." $path HTTP/1.0\r\n"); 
      fwrite($fp, "Content-Length: ".strlen($data)."\r\n"); 
     } 
     fwrite($fp, "Host: {$url_data['host']}\r\n"); 
     foreach ($this->getHeaders() as $header_name => $header_value) 
     { 
      fwrite($fp, "$header_name: $header_value\r\n"); 
     } 

     fwrite($fp, "Connection: Close\r\n\r\n"); 

     if ($this->method!=self::method_GET) 
     { 
      fwrite($fp, $data); 
     } 
     if (!empty($Response)) return $this->ReadResponse($fp, $Response); 
     else return true; 
    } 

    /** 
    * @param \resource $fresource 
    * @param IResponse $response 
    * @return IResponse 
    */ 
    protected function ReadResponse($fresource, IResponse $response) 
    { 
     //read headers 
     $status_header = ''; 
     $headers = array(); 
     while (!feof($fresource)) 
     { 
      $header = trim(fgets($fresource)); 
      if (!empty($header)) 
      { 
       if (empty($status_header)) $status_header = $header; 
       if (strpos($header, ':')!==false) 
       { 
        $header = explode(':', $header); 
        $headers[trim($header[0])] = trim($header[1]); 
       } 
       else $headers[] = $header; 
      } 
      else break; 
     } 
     $response->setHeaders($headers); 
     if (!empty($status_header)) 
     { 
      $status_header = explode(' ', $status_header); 
      $response->setStatusCode(intval(trim($status_header[1]))); 
     } 

     //read body 
     $body = ''; 
     while (!feof($fresource)) $body .= fread($fresource, 4096); 
     fclose($fresource); 

     if (!empty($body)) $response->setBody($body); 

     return $response; 
    } 

    /** 
    * Set array of data 
    * @param array $values 
    */ 
    public function setData(array $values) 
    { 
     $this->data = $values; 
    } 
} 

和響應:

<?php 
namespace Jamm\HTTP; 

class Response implements IResponse 
{ 
    protected $status_code; 
    protected $body; 
    protected $headers; 
    protected $serialize_method; 

    const serialize_JSON = 'JSON'; 
    const serialize_XML = 'XML'; 
    const serialize_PHP = 'PHP'; 

    const header_Serialized = 'API-Serialized'; 

    public function __construct($body = '', $status_code = 200) 
    { 
     $this->body = $body; 
     $this->status_code = $status_code; 
     $this->serialize_method = self::serialize_JSON; 
    } 

    public function getStatusCode() 
    { 
     return $this->status_code; 
    } 

    /** @param int $status_code */ 
    public function setStatusCode($status_code) 
    { 
     $this->status_code = (int)$status_code; 
    } 

    /** 
    * Set header for the response 
    * @param string $header 
    * @param string|numeric $value 
    */ 
    public function setHeader($header, $value) 
    { 
     $this->headers[$header] = $value; 
     if ($header==='Location' && $this->status_code==200) $this->setStatusCode(301); 
    } 

    public function getHeader($header) 
    { 
     return isset($this->headers[$header]) ? $this->headers[$header] : NULL; 
    } 

    /** 
    * Get body of the response 
    * @return string 
    */ 
    public function getBody() 
    { 
     return $this->body; 
    } 

    /** 
    * Get Result of response - unpack value of body and headers 
    * @return bool|mixed 
    */ 
    public function getResult() 
    { 
     if ($this->getStatusCode() >= 400) return false; 

     if (($serialization_method = $this->getHeader(self::header_Serialized))) 
     { 
      $this->serialize_method = $serialization_method; 
      return $this->unserialize($this->body); 
     } 
     else return $this->body; 
    } 

    /** 
    * Set body of the response 
    * @param $body 
    */ 
    public function setBody($body) 
    { 
     if (!is_scalar($body)) 
     { 
      $this->body = $this->serialize($body); 
      $this->setHeader(self::header_Serialized, $this->serialize_method); 
     } 
     else $this->body = $body; 
    } 

    public function getHeaders() 
    { 
     return $this->headers; 
    } 

    public function setHeaders(array $headers) 
    { 
     $this->headers = $headers; 
    } 

    public function serialize($content) 
    { 
     switch ($this->serialize_method) 
     { 
      case self::serialize_JSON: 
       return json_encode($content); 
      default: 
       return serialize($content); 
     } 
    } 

    public function unserialize($content) 
    { 
     switch ($this->serialize_method) 
     { 
      case self::serialize_JSON: 
       return json_decode($content, true); 
      default: 
       return unserialize($content); 
     } 
    } 

    /** 
    * Send headers and body to output 
    */ 
    public function Send() 
    { 
     $headers = $this->getHeaders(); 
     if (!empty($headers)) 
     { 
      foreach ($headers as $header_key => $header_value) 
      { 
       header($header_key.': '.$header_value, true, $this->status_code); 
      } 
     } 
     print $this->body; 
    } 
} 
+0

這可能是一個以標準方式處理請求的框架,但問題似乎是「需要」頂部的CSRF Magic腳本如何在發送之前自動重寫腳本的輸出。 – Matchu

+0

@Matchu你說的是迴應,而不是要求。 –

+1

你在談論與o.o問題完全無關的事情。讓我看看我是否可以簡化它:「當我所做的所有事情都包含在頂部時,CSRF Magic這樣的腳本如何攔截我的請求?這些類的有用性似乎與我們正在討論的更具體的案例無關。這不是「我應該如何攔截請求?」這是「他們如何攔截請求?」 – Matchu