javascript - uso - Cómo enviar solicitudes seguras de AJAX con PHP y jQuery
pasar variable de javascript a php por ajax (3)
Respuesta corta: no puedes proteger a tu cliente.
Respuesta larga:
- Usar AJAX es tan seguro como publicar datos con, por ejemplo, un formulario.
- El uso de HTTPS impide que los usuarios intermedios puedan ver sus datos de usuario, por lo que los datos reales enviados por el usuario están seguros.
No puede hacer nada para que el navegador demuestre que en realidad es su código javascript que se ejecuta en el lado del cliente. Entonces, la acción obvia a tomar es la más simple: NUNCA CONFIAR EN LA ENTRADA DEL USUARIO.
Esto significa, como comenzó a hacer, proteger su servidor mediante sesión, limitación de velocidad, validación de datos, fail2ban (prohibir una IP de cliente después de un cierto número de fallas), monitorear el registro ...
El problema
Así que desde hace un tiempo, he estado experimentando con diferentes enfoques AJAX para enviar datos a un servidor que se procesarán y almacenarán dentro de una base de datos MySQL.
La página en la que la solicitud de AJAX llega a api.php
, utiliza las declaraciones preparadas de PHP para guardar los datos, por lo que las inyecciones de MySQL no son realmente un problema y las contraseñas o los datos que deben cifrarse también son manejados por api.php
que no es un problema. t lo que estoy preguntando aquí. Mi pregunta se relaciona más con la forma de garantizar que los datos estén seguros cuando se transfieren del cliente al servidor.
Los enfoques
Actualmente tengo (para el ejemplo de inicio de sesión que he incluido a continuación):
- SSL Cert / HTTPS ejecutándose en el dominio.
- Cierta solicitud de AJAX (obviamente no es este ejemplo de solicitud de inicio de sesión ya que no hay una sesión con la que comenzar) solo funcionará si la sesión de PHP es válida en todo el sitio (se usa en
login.php
yapi.php
en este ejemplo). -
api.php
velocidad enapi.php
al acceder a las funciones. - PHP DOP preparó declaraciones al interactuar con la base de datos dentro de
api.php
. - Cifra los datos confidenciales dentro de
api.php
(no es relevante para la pregunta).
Las preguntas
Finalmente, mis preguntas son:
- ¿Este enfoque para usar solicitudes HTTP asíncronas (Ajax) es lo suficientemente seguro para usar en lugar de simplemente enviar datos a una página PHP y redirigir en adelante? (De esta manera mejora la experiencia del usuario).
- ¿Cómo puedo verificar si los datos que mi usuario está enviando no han sido manipulados?
- ¿Estoy haciendo lo suficiente para proteger los datos de mi usuario? Si no, ¿qué más puedo hacer?
El ejemplo
Entiendo que todos tienen diferentes enfoques para manejar los datos de su sitio y transportar esos datos. También entiendo que no importa lo que hagas, nunca puedes estar protegido al 100%, ya que puede haber vulnerabilidades y formas en torno a tu sistema que no puedes explicar. Estoy buscando comentarios / mejoras en mi enfoque general para enviar datos de forma segura en lugar de criticar el código específico a continuación, ya que es solo un ejemplo. Pero cualquier respuesta constructiva es bienvenida. Gracias por tomarse el tiempo para leer / responder.
function loginUser() {
var process = "loginUser";
var data = $("form").serializeArray();
data[1].value = SHA512(data[1].value); // sha then encrypt on api.php page
data = JSON.stringify(data);
$("#loginButton").html(''<i class="fa fa-spinner fa-pulse fa-lg fa-fw"></i> Login'');
$.ajax({
type: "POST",
url: "api.php",
data: {"process": process, "data": data},
success: function(data) {
if (data.response.state == "success") {
// if api.php returns success, redirect to homepage
} else {
// if api.php returns failure, display error
}
},
error: function(jqXHR, textStatus, errorThrown, data) {
// error handling
},
dataType: "json"
});
}
la mejor manera es seguir asegurando el lado del servidor y, si fuera un usuario registrado en el sitio, también podría verificar el token csrf en mi etiqueta meta y falsificar un script en el servidor. Así que la apuesta segura es la validación del lado del servidor.
1. Revisa el encabezado de ORIGEN
Según lo especificado por OWASP , esto no es suficiente pero se recomienda:
Aunque es trivial falsificar cualquier encabezado desde su propio navegador, en general es imposible hacerlo en un ataque CSRF, excepto a través de una vulnerabilidad XSS. Es por eso que el control de encabezados es un primer paso razonable en su defensa CSRF, pero dado que no siempre están presentes, generalmente no se considera una defensa suficiente por sí sola.
Y por Mozilla :
El encabezado Origin se considera útil contra el robo de datos JSON y los ataques CSRF. La información proporcionada por Origin (un poco de información contextual de creación de solicitudes) debe proporcionar sugerencias a los servidores web sobre la confiabilidad de las solicitudes [...]
La comprobación del encabezado HTTP_ORIGIN
podría escribirse como:
header(''Content-Type: application/json'');
if (isset($_SERVER[''HTTP_ORIGIN''])) {
$address = ''http://'' . $_SERVER[''SERVER_NAME''];
if (strpos($address, $_SERVER[''HTTP_ORIGIN'']) !== 0) {
exit(json_encode([
''error'' => ''Invalid Origin header: '' . $_SERVER[''HTTP_ORIGIN'']
]));
}
} else {
exit(json_encode([''error'' => ''No Origin header'']));
}
1. (bis) Verifique el encabezado de REFERER
Otra vez desde OWASP :
Si el encabezado de Origen no está presente , verifique que el nombre de host en el encabezado del Referente coincida con el origen del sitio. La verificación del remitente es un método comúnmente usado para prevenir CSRF en dispositivos de red incorporados porque no requiere un estado por usuario. Este método de mitigación de CSRF también se usa comúnmente con solicitudes no autenticadas [...]
La comprobación de HTTP_REFERER
también es bastante simple en PHP con $_SERVER[''HTTP_REFERER'']
, simplemente puede actualizar el código anterior con él.
TENGA CUIDADO con la comprobación, que siempre debe ser realmente específica: no marque solo example.com o api.example.com sino la https://example.com completa. Por qué ? Porque podría falsificar esta comprobación con un origen como api.example.com.hacker.com .
2. Generar tokens CSRF
Una respuesta bien explicada específica para PHP se ha dado allí , en resumen:
Generar el token:
session_start(); if (empty($_SESSION[''csrf_token''])) { $_SESSION[''csrf_token''] = bin2hex(random_bytes(32)); }
Agrégalo a tus vistas generadas a través de un meta (como Github):
<meta name="csrf-token" content="<?= $_SESSION[''csrf_token''] ?>">
Configura las llamadas ajax de jQuery para incluir este token:
$.ajaxSetup({ headers : { ''CsrfToken'': $(''meta[name="csrf-token"]'').attr(''content'') } });
Verifique el lado del servidor de sus solicitudes AJAX:
session_start(); if (empty($_SESSION[''csrf_token''])) { $_SESSION[''csrf_token''] = bin2hex(random_bytes(32)); } header(''Content-Type: application/json''); $headers = apache_request_headers(); if (isset($headers[''CsrfToken''])) { if ($headers[''CsrfToken''] !== $_SESSION[''csrf_token'']) { exit(json_encode([''error'' => ''Wrong CSRF token.''])); } } else { exit(json_encode([''error'' => ''No CSRF token.''])); }
La mayoría de los marcos PHP tienen su propia implementación CSRF, que se basa más o menos en el mismo principio.
3. Desinfectar validar la entrada del usuario.
Siempre debes filtrar Espace las entradas y valídalos .
4. Protege tu servidor
- Limite el número de sus solicitudes .
- Utilice https tanto como sea posible .
- Bloquear malas consultas .
- Proteger las solicitudes POST .
5. Nunca confíes en la entrada del usuario
Como dijo @ blue112, es uno de los principios de seguridad más elementales.