He estado leyendo sobre REST y hay muchas preguntas sobre SO al respecto, así como en muchos otros sitios y blogs. Aunque nunca he visto esta pregunta específica ... por alguna razón, no puedo envolver mi mente en este concepto ...
Si estoy construyendo una API RESTful y quiero protegerla, uno de los métodos que he visto es usar un token de seguridad. Cuando he usado otras API, ha habido un token y un secreto compartido ... tiene sentido. Lo que no entiendo es que las solicitudes para una operación de servicio de descanso se realizan a través de javascript (XHR / Ajax), ¿qué es lo que impide que alguien lo detecte con algo simple como FireBug (o "ver código fuente" en el navegador) y copiar la clave de API y luego hacerse pasar por esa persona usando la clave y el secreto?
fuente
Respuestas:
api el secreto no se pasa explícitamente, el secreto se usa para generar un signo de solicitud actual, en el lado del servidor, el servidor genera el signo siguiendo el mismo proceso, si los dos signos coinciden, entonces la solicitud se autentica con éxito, por lo que solo el el signo se transmite a través de la solicitud, no el secreto.
fuente
Estamos exponiendo una API que los socios solo pueden usar en los dominios que han registrado con nosotros. Su contenido es parcialmente público (pero preferiblemente solo para mostrarse en los dominios que conocemos), pero es principalmente privado para nuestros usuarios. Entonces:
Para determinar qué se muestra, nuestro usuario debe iniciar sesión con nosotros, pero esto se maneja por separado.
Para determinar dónde se muestran los datos, se utiliza una clave API pública para limitar el acceso a los dominios que conocemos y, sobre todo, para garantizar que los datos privados del usuario no sean vulnerables a CSRF .
Esta clave de API es visible para cualquier persona, no autenticamos a nuestro socio de ninguna otra manera y no necesitamos REFERIR . Aún así, es seguro:
Cuando
get-csrf-token.js?apiKey=abc123
se solicita nuestro :Busque la clave
abc123
en la base de datos y obtenga una lista de dominios válidos para esa clave.Busque la cookie de validación CSRF. Si no existe, genere un valor aleatorio seguro y colóquelo en una cookie de sesión solo HTTP . Si la cookie existió, obtenga el valor aleatorio existente.
Cree un token CSRF a partir de la clave de API y el valor aleatorio de la cookie, y fírmelo . (En lugar de mantener una lista de tokens en el servidor, estamos firmando los valores. Ambos valores serán legibles en el token firmado, eso está bien).
Configure la respuesta para que no se almacene en caché, agregue la cookie y devuelva un script como:
Notas:
Lo anterior no evita que un script del lado del servidor falsifique una solicitud, sino que solo garantiza que el dominio coincida si lo solicita un navegador.
La misma política de origen para JavaScript garantiza que un navegador no pueda usar XHR (Ajax) para cargar y luego inspeccionar la fuente de JavaScript. En cambio, un navegador normal solo puede cargarlo usando
<script src="https://our-api.com/get-csrf-token.js?apiKey=abc123">
(o un equivalente dinámico), y luego ejecutará el código. Por supuesto, su servidor no debe admitir el uso compartido de recursos entre orígenes ni JSONP para el JavaScript generado.Un script del navegador puede cambiar el valor de
document.domain
antes de cargar el script anterior. Pero la misma política de origen solo permite acortar el dominio eliminando prefijos, como reescribirsubdomain.example.com
a soloexample.com
, omyblog.wordpress.com
awordpress.com
, o en algunos navegadores inclusobbc.co.uk
aco.uk
.Si el archivo JavaScript se obtiene mediante algún script del lado del servidor, el servidor también obtendrá la cookie. Sin embargo, un servidor de terceros no puede hacer que el navegador de un usuario asocie esa cookie a nuestro dominio. Por lo tanto, un token CSRF y una cookie de validación que se han obtenido mediante un script del lado del servidor solo se pueden usar en llamadas posteriores del lado del servidor, no en un navegador. Sin embargo, dichas llamadas del lado del servidor nunca incluirán la cookie del usuario y, por lo tanto, solo pueden obtener datos públicos. Estos son los mismos datos que un script del lado del servidor podría extraer directamente del sitio web del socio.
Cuando un usuario inicia sesión, configure alguna cookie de usuario de la forma que desee. (Es posible que el usuario ya haya iniciado sesión antes de que se solicitara JavaScript).
Todas las solicitudes de API posteriores al servidor (incluidas las solicitudes GET y JSONP) deben incluir el token CSRF, la cookie de validación CSRF y (si está conectado) la cookie del usuario. El servidor ahora puede determinar si la solicitud es confiable:
La presencia de un token CSRF válido garantiza que JavaScript se cargó desde el dominio esperado, si lo cargó un navegador.
La presencia del token CSRF sin la cookie de validación indica una falsificación.
La presencia tanto del token CSRF como de la cookie de validación CSRF no asegura nada: esto podría ser una solicitud del lado del servidor falsificada o una solicitud válida de un navegador. (No puede ser una solicitud de un navegador realizada desde un dominio no admitido).
La presencia de la cookie del usuario asegura que el usuario esté conectado, pero no asegura que el usuario sea miembro del socio dado, ni que el usuario esté viendo el sitio web correcto.
La presencia de la cookie del usuario sin la cookie de validación CSRF indica una falsificación.
La presencia de la cookie del usuario garantiza que la solicitud actual se realice a través de un navegador. (Suponiendo que un usuario no ingrese sus credenciales en un sitio web desconocido, y asumiendo que no nos importa que los usuarios usen sus propias credenciales para realizar alguna solicitud del lado del servidor). Si también tenemos la cookie de validación CSRF, entonces esa cookie de validación CSRF era también se recibió mediante un navegador. A continuación, si también tenemos un token CSRF con una firma válida, yel número aleatorio en la cookie de validación CSRF coincide con el de ese token CSRF, luego el JavaScript para ese token también se recibió durante esa misma solicitud anterior durante la cual se configuró la cookie CSRF, por lo que también se usó un navegador. Esto también implica que el código JavaScript anterior se ejecutó antes de que se estableciera el token, y que en ese momento el dominio era válido para la clave API dada.
Entonces: el servidor ahora puede usar de manera segura la clave API del token firmado.
Si en algún momento el servidor no confía en la solicitud, se devuelve un 403 Forbidden. El widget puede responder a eso mostrando una advertencia al usuario.
No es necesario firmar la cookie de validación CSRF, ya que la estamos comparando con el token CSRF firmado. No firmar la cookie hace que cada solicitud HTTP sea más corta y la validación del servidor sea un poco más rápida.
El token CSRF generado es válido indefinidamente, pero solo en combinación con la cookie de validación, de manera efectiva hasta que se cierra el navegador.
Podríamos limitar la vida útil de la firma del token. Podríamos eliminar la cookie de validación CSRF cuando el usuario cierra la sesión, para cumplir con la recomendación de OWASP . Y para no compartir el número aleatorio por usuario entre varios socios, se podría agregar la clave API al nombre de la cookie. Pero incluso entonces no se puede actualizar fácilmente la cookie de validación CSRF cuando se solicita un nuevo token, ya que los usuarios pueden estar navegando por el mismo sitio en varias ventanas, compartiendo una sola cookie (que, al actualizar, se actualizaría en todas las ventanas, después de lo cual el El token de JavaScript en las otras ventanas ya no coincidiría con esa única cookie).
Para aquellos que usan OAuth, consulte también OAuth y Widgets del lado del cliente , de donde obtuve la idea de JavaScript. Para el uso de la API en el lado del servidor , en el que no podemos confiar en el código JavaScript para limitar el dominio, estamos usando claves secretas en lugar de las claves públicas de la API.
fuente
OPTIONS
solicitud previa al vuelo con alguna clave API pública en la URL, el servidor puede indicarle a un navegador qué dominios están permitidos (o cancelar la solicitud). Sin embargo, tenga en cuenta que algunas solicitudes no requieren una solicitud previa al vuelo, o no usarán CORS en absoluto , y que CORS necesita IE8 +. Si se usa algún respaldo de Flash para IE7, entonces quizás alguna dinámicacrossdomain.xml
pueda ayudar a lograr lo mismo para eso. Todavía no hemos probado CORS / Flash.Esta pregunta tiene una respuesta aceptada, pero solo para aclarar, la autenticación secreta compartida funciona así:
fuente
Supongo que te refieres a la clave de sesión, no a la clave API. Ese problema se hereda del protocolo http y se conoce como secuestro de sesión . La "solución alternativa" normal es, como en cualquier sitio web, cambiar a https.
Para ejecutar el servicio REST de forma segura, debe habilitar https y probablemente la autenticación del cliente. Pero después de todo, esto está más allá de la idea REST. REST nunca habla de seguridad.
fuente
Lo que desea hacer en el lado del servidor es generar una identificación de sesión que expira que se envía al cliente al iniciar sesión o registrarse. Luego, el cliente puede usar esa identificación de sesión como un secreto compartido para firmar solicitudes posteriores.
La identificación de sesión solo se pasa una vez y DEBE ser a través de SSL.
Ver ejemplo aquí
Utilice un nonce y una marca de tiempo al firmar la solicitud para evitar el secuestro de la sesión.
fuente
Intentaré responder la pregunta en su contexto original. Entonces, la pregunta es "¿Es seguro colocar la clave secreta (API) en JavaScript?
En mi opinión, es muy inseguro ya que frustra el propósito de autenticación entre los sistemas. Dado que la clave estará expuesta al usuario, el usuario puede recuperar información para la que no está autorizado. Porque en una comunicación de reposo típica, la autenticación solo se basa en la clave API.
En mi opinión, una solución es que la llamada de JavaScript esencialmente pasa la solicitud a un componente del servidor interno que es responsable de hacer una llamada de descanso. El componente del servidor interno, digamos que un Servlet leerá la clave API de una fuente segura, como un sistema de archivos basado en permisos, la insertará en el encabezado HTTP y realizará la llamada de descanso externa.
Espero que esto ayude.
fuente