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:
var apiConfig = apiConfig || {};
if(document.domain === 'expected-domain.com'
|| document.domain === 'www.expected-domain.com') {
apiConfig.csrfToken = 'API key, random value, signature';
// Invoke a callback if the partner wants us to
if(typeof apiConfig.fnInit !== 'undefined') {
apiConfig.fnInit();
}
} else {
alert('This site is not authorised for this API key.');
}
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 reescribir subdomain.example.com
a solo example.com
, o myblog.wordpress.com
a wordpress.com
, o en algunos navegadores incluso bbc.co.uk
a co.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.