it-swarm-ko.tech

$ _GET 또는 $ _POST 배열에서 '.'문자 교체를 중지하려면 PHP 가져 오기?

PHP 변수를 .를 통해 $ _GET PHP를 통해 이름을 _ 자 예를 들면 다음과 같습니다.

<?php
echo "url is ".$_SERVER['REQUEST_URI']."<p>";
echo "x.y is ".$_GET['x.y'].".<p>";
echo "x_y is ".$_GET['x_y'].".<p>";

... 다음을 출력합니다 :

url is /SpShipTool/php/testGetUrl.php?x.y=a.b
x.y is .
x_y is a.b.

... 내 질문은 이것입니다 : any 이걸 막을 수있는 방법이 있습니까? 내 인생에서 내가 할 자격이 무엇인지 알아낼 수 없다

내가 실행중인 PHP 버전은 5.2.4-2ubuntu5.3입니다.

70
Dave Carpeneto

PHP.net의 이유는 다음과 같습니다.

들어오는 변수 이름의 점

일반적으로 PHP 변수가 스크립트에 전달 될 때 변수 이름을 변경하지 않지만 점 (마침표, 마침표)은 유효한 문자가 아닙니다. PHP 변수 이름입니다. 이유는 다음과 같습니다.

<?php
$varname.ext;  /* invalid variable name */
?>

이제 파서가 보는 것은 $ varname이라는 변수, 문자열 연결 연산자, 그 다음에 맨 스트링 (즉, 알려진 키나 예약어와 일치하지 않는 따옴표없는 문자열) 'ext'라는 변수입니다. 분명히 이것은 의도 한 결과가 아닙니다.

이러한 이유로 PHP는 들어오는 변수 이름의 점을 밑줄로 자동으로 바꿉니다.

http://ca.php.net/variables.external 에서 온 것입니다.

또한 this comment 에 따르면 이러한 다른 문자는 밑줄로 변환됩니다.

PHP _ (밑줄)로 변환되는 필드 이름 문자의 전체 목록은 다음과 같습니다 (점뿐만 아니라).

  • chr (32) () (공간)
  • chr (46) (.) (도트)
  • chr (91) ([) (대괄호 열기)
  • chr (128)-chr (159) (다양한)

따라서 붙어있는 것처럼 보이므로 dawnerd 's suggestion (단지 str_replace 를 사용하여 스크립트에서 밑줄을 점으로 다시 변환해야합니다. 그러나.)

63
Jeremy Ruten

오랫동안 답변 된 질문이지만 실제로 더 나은 답변 (또는 해결 방법)이 있습니다. PHP 원시 입력 스트림 에서 가능하게하므로 다음과 같이 할 수 있습니다 :

$query_string = file_get_contents('php://input');

쿼리 문자열 형식의 마침표로 $ _POST 배열을 제공합니다.

필요한 경우 ( POSTer 's comment )에 따라 구문 분석 할 수 있습니다

<?php
// Function to fix up PHP's messing up input containing dots, etc.
// `$source` can be either 'POST' or 'GET'
function getRealInput($source) {
    $pairs = explode("&", $source == 'POST' ? file_get_contents("php://input") : $_SERVER['QUERY_STRING']);
    $vars = array();
    foreach ($pairs as $pair) {
        $nv = explode("=", $pair);
        $name = urldecode($nv[0]);
        $value = urldecode($nv[1]);
        $vars[$name] = $value;
    }
    return $vars;
}

// Wrapper functions specifically for GET and POST:
function getRealGET() { return getRealInput('GET'); }
function getRealPOST() { return getRealInput('POST'); }
?>

'.'를 모두 포함하는 OpenID 매개 변수에 매우 유용합니다. 그리고 '_'는 각각 특정한 의미를 갖습니다!

57
crb

위의 의견에서 Johan의 실제 답변을 강조 표시했습니다. 방금 처리하지 않아도 문제를 완전히 우회하는 최상위 배열로 전체 게시물을 포장했습니다.

당신이하는 형태로

<input name="data[database.username]">  
<input name="data[database.password]">  
<input name="data[something.else.really.deep]">  

대신에

<input name="database.username"> 
<input name="database.password"> 
<input name="something.else.really.deep">  

사후 처리기에서 포장을 풉니 다.

$posdata = $_POST['data'];

내 관점이 완전히 템플릿 화되었으므로 이것은 두 줄로 바뀌 었습니다.

참고로 필드 이름에 점을 사용하여 그룹화 된 데이터 트리를 편집하고 있습니다.

26
scipilot

마침표는 변수 이름에 유효하지 않은 문자이기 때문에 발생합니다. reason 는 PHP 구현에 매우 깊이 있기 때문에 쉬운 수정이 없습니다 (아직).

그 동안 다음을 통해이 문제를 해결할 수 있습니다.

  1. POST 데이터의 경우 php://input 또는 GET 데이터의 경우 $_SERVER['QUERY_STRING']를 통해 원시 쿼리 데이터에 액세스
  2. 변환 기능 사용.

아래의 변환 함수 (PHP> = 5.4)는 각 키-값 쌍의 이름을 16 진수로 인코딩 한 다음 정규 parse_str(); 완료되면 16 진 이름을 원래 형식으로 되돌립니다.

function parse_qs($data)
{
    $data = preg_replace_callback('/(?:^|(?<=&))[^=[]+/', function($match) {
        return bin2hex(urldecode($match[0]));
    }, $data);

    parse_str($data, $values);

    return array_combine(array_map('hex2bin', array_keys($values)), $values);
}

// work with the raw query string
$data = parse_qs($_SERVER['QUERY_STRING']);

또는:

// handle posted data (this only works with application/x-www-form-urlencoded)
$data = parse_qs(file_get_contents('php://input'));
6
Ja͢ck

이 접근 방식은 Rok Kralj의 변경된 버전이지만 효율성을 향상시키고 (불필요한 콜백, 영향을받지 않는 키에 대한 인코딩 및 디코딩을 피하고) 배열 키를 올바르게 처리하기 위해 약간의 조정이 필요합니다.

테스트 대상 을 사용할 수 있으며, 피드백이나 제안을 환영합니다.

public function fix(&$target, $source, $keep = false) {                        
    if (!$source) {                                                            
        return;                                                                
    }                                                                          
    $keys = array();                                                           

    $source = preg_replace_callback(                                           
        '/                                                                     
        # Match at start of string or &                                        
        (?:^|(?<=&))                                                           
        # Exclude cases where the period is in brackets, e.g. foo[bar.blarg]
        [^=&\[]*                                                               
        # Affected cases: periods and spaces                                   
        (?:\.|%20)                                                             
        # Keep matching until assignment, next variable, end of string or   
        # start of an array                                                    
        [^=&\[]*                                                               
        /x',                                                                   
        function ($key) use (&$keys) {                                         
            $keys[] = $key = base64_encode(urldecode($key[0]));                
            return urlencode($key);                                            
        },                                                                     
    $source                                                                    
    );                                                                         

    if (!$keep) {                                                              
        $target = array();                                                     
    }                                                                          

    parse_str($source, $data);                                                 
    foreach ($data as $key => $val) {                                          
        // Only unprocess encoded keys                                      
        if (!in_array($key, $keys)) {                                          
            $target[$key] = $val;                                              
            continue;                                                          
        }                                                                      

        $key = base64_decode($key);                                            
        $target[$key] = $val;                                                  

        if ($keep) {                                                           
            // Keep a copy in the underscore key version                       
            $key = preg_replace('/(\.| )/', '_', $key);                        
            $target[$key] = $val;                                              
        }                                                                      
    }                                                                          
}                                                                              
5
El Yobo

이런 일이 발생하는 이유는 PHP의 이전 register_globals 기능 때문입니다. . 문자는 변수 이름에 유효한 문자가 아니므로 PHP는 밑줄로 표시하여 호환성이 있는지 확인하십시오.

즉, URL 변수에서 마침표를 사용하는 것은 좋지 않습니다.

4
Jeremy Privett

Rok의 솔루션을 살펴본 후 아래 답변, 제한 사항 및 crk 솔루션의 제한 사항을 해결하는 버전을 생각해 냈습니다. 내 개선 된 버전 을 참조하십시오.


@ crb 's answer above 좋은 시작이지만 몇 가지 문제가 있습니다.

  • 그것은 모든 것을 재 처리합니다. "."가있는 필드 만 이름을 다시 처리해야합니다.
  • 네이티브 PHP 처리와 같은 방식으로 배열을 처리하지 못합니다 (예 : "foo.bar []"와 같은 키의 경우).

아래의 솔루션은 현재 이러한 두 가지 문제를 모두 해결합니다 (원래 게시 된 이후 업데이트 됨). 이것은 테스트에서 위의 답변보다 약 50 % 빠르지 만 데이터에 동일한 키가있는 상황 (또는 foo.bar 및 foo_bar가 모두 foo_bar로 추출되는 키)이있는 상황은 처리하지 않습니다.

<?php

public function fix2(&$target, $source, $keep = false) {                       
    if (!$source) {                                                            
        return;                                                                
    }                                                                          
    preg_match_all(                                                            
        '/                                                                     
        # Match at start of string or &                                        
        (?:^|(?<=&))                                                           
        # Exclude cases where the period is in brackets, e.g. foo[bar.blarg]
        [^=&\[]*                                                               
        # Affected cases: periods and spaces                                   
        (?:\.|%20)                                                             
        # Keep matching until assignment, next variable, end of string or   
        # start of an array                                                    
        [^=&\[]*                                                               
        /x',                                                                   
        $source,                                                               
        $matches                                                               
    );                                                                         

    foreach (current($matches) as $key) {                                      
        $key    = urldecode($key);                                             
        $badKey = preg_replace('/(\.| )/', '_', $key);                         

        if (isset($target[$badKey])) {                                         
            // Duplicate values may have already unset this                    
            $target[$key] = $target[$badKey];                                  

            if (!$keep) {                                                      
                unset($target[$badKey]);                                       
            }                                                                  
        }                                                                      
    }                                                                          
}                                                                              
2
El Yobo

이 문제에 대한 나의 해결책은 빠르고 더러 웠지만 여전히 마음에 든다. 양식에서 확인 된 파일 이름 목록을 게시하고 싶었습니다. 나는 base64_encode를 사용하여 마크 업 내의 파일 이름을 인코딩 한 다음 base64_decode 사용하기 전에.

2
Jason

내 현재 솔루션 (이전 주제 답변을 기반으로 함) :

function parseQueryString($data)
{
    $data = rawurldecode($data);   
    $pattern = '/(?:^|(?<=&))[^=&\[]*[^=&\[]*/';       
    $data = preg_replace_callback($pattern, function ($match){
        return bin2hex(urldecode($match[0]));
    }, $data);
    parse_str($data, $values);

    return array_combine(array_map('hex2bin', array_keys($values)), $values);
}

$_GET = parseQueryString($_SERVER['QUERY_STRING']);
0
sasha-ch

Crb를 사용하여 $_POST 배열 전체를 기억하지만 클라이언트와 서버 모두에서 올바르게 인코딩하고 디코딩해야합니다. 문자가 truly 유효하지 않고 진정으로 valid 인 경우를 이해하는 것이 중요합니다. 또한 사람들은 정지항상 클라이언트 데이터를 any 데이터베이스 명령 예외없이와 함께 사용하기 전에 이스케이프해야합니다.

<?php
unset($_POST);
$_POST = array();
$p0 = explode('&',file_get_contents('php://input'));
foreach ($p0 as $key => $value)
{
 $p1 = explode('=',$value);
 $_POST[$p1[0]] = $p1[1];
 //OR...
 //$_POST[urldecode($p1[0])] = urldecode($p1[1]);
}
print_r($_POST);
?>

나는 이것을 개별 경우에만 사용하는 것이 좋습니다. 오프 라인 기본 헤더 파일의 맨 위에 이것을 놓는 부정적인 점에 대해서는 확실하지 않습니다.

0
John

아래에 포함 된 "getRealPostArray ()"함수는 그다지 좋은 해결책은 아니지만 배열을 처리하고 "alpha_beta"와 "alpha.beta"라는 이름을 모두 지원합니다.

  <input type='text' value='First-.' name='alpha.beta[a.b][]' /><br>
  <input type='text' value='Second-.' name='alpha.beta[a.b][]' /><br>
  <input type='text' value='First-_' name='alpha_beta[a.b][]' /><br>
  <input type='text' value='Second-_' name='alpha_beta[a.b][]' /><br>

반면 var_dump ($ _ POST)는 다음을 생성합니다.

  'alpha_beta' => 
    array (size=1)
      'a.b' => 
        array (size=4)
          0 => string 'First-.' (length=7)
          1 => string 'Second-.' (length=8)
          2 => string 'First-_' (length=7)
          3 => string 'Second-_' (length=8)

var_dump (getRealPostArray ())는 다음을 생성합니다.

  'alpha.beta' => 
    array (size=1)
      'a.b' => 
        array (size=2)
          0 => string 'First-.' (length=7)
          1 => string 'Second-.' (length=8)
  'alpha_beta' => 
    array (size=1)
      'a.b' => 
        array (size=2)
          0 => string 'First-_' (length=7)
          1 => string 'Second-_' (length=8)

가치있는 기능 :

function getRealPostArray() {
  if ($_SERVER['REQUEST_METHOD'] !== 'POST') {#Nothing to do
      return null;
  }
  $neverANamePart = '~#~'; #Any arbitrary string never expected in a 'name'
  $postdata = file_get_contents("php://input");
  $post = [];
  $rebuiltpairs = [];
  $postraws = explode('&', $postdata);
  foreach ($postraws as $postraw) { #Each is a string like: 'xxxx=yyyy'
    $keyvalpair = explode('=',$postraw);
    if (empty($keyvalpair[1])) {
      $keyvalpair[1] = '';
    }
    $pos = strpos($keyvalpair[0],'%5B');
    if ($pos !== false) {
      $str1 = substr($keyvalpair[0], 0, $pos);
      $str2 = substr($keyvalpair[0], $pos);
      $str1 = str_replace('.',$neverANamePart,$str1);
      $keyvalpair[0] = $str1.$str2;
    } else {
      $keyvalpair[0] = str_replace('.',$neverANamePart,$keyvalpair[0]);
    }
    $rebuiltpair = implode('=',$keyvalpair);
    $rebuiltpairs[]=$rebuiltpair;
  }
  $rebuiltpostdata = implode('&',$rebuiltpairs);
  parse_str($rebuiltpostdata, $post);
  $fixedpost = [];
  foreach ($post as $key => $val) {
    $fixedpost[str_replace($neverANamePart,'.',$key)] = $val;
  }
  return $fixedpost;
}
0
ChrisNY