Añadiendo SSO después de que muchos usuarios ya se registraron -- ¿cómo migrarlos?

Hola a todos.

¿Cuál es la forma correcta de importar todos los usuarios existentes de Discourse a intercoin.app desde Discourse? ¿Existe algún tipo de punto final REST que devuelva todos los usuarios con sus contraseñas hasheadas y sales, entre otras cosas? ¿Cuál es el enlace al algoritmo de hash en github? Tendré que codificar de nuestro lado para usar el mismo algoritmo de hash con la contraseña y la sal ingresadas, si el nuestro no funciona, para permitir que esos usuarios inicien sesión. Creo que el #2 es relevante cada vez que un usuario de Discourse activa el SSO más tarde (como nosotros), por lo que resolverlo ayudaría a otros usuarios de Discourse también.

2 Me gusta

Enfoque interesante.

En user.rb

  def confirm_password?(password)
    return false unless password_hash && salt
    self.password_hash == hash_password(password, salt)
  end

  def hash_password(password, salt)
    raise StandardError.new("la contraseña es demasiado larga") if password.size > User.max_password_length
    Pbkdf2.hash_password(password, salt, Rails.configuration.pbkdf2_iterations, Rails.configuration.pbkdf2_algorithm)
  end

y luego el código Pbkdf2 está aquí: discourse/lib/pbkdf2.rb at 201228162c277b9833bb2988388553fdbfb39521 · discourse/discourse · GitHub

2 Me gusta

¡Excelente! Ahora, ¿cuál es el endpoint HTTP al que debo llamar para obtener toda la información del usuario, incluido el hash de la contraseña y la sal?

Imagino que no hay uno para servir al público en general (¿por qué facilitar el hackeo de personas?) Entonces, ¿qué puedo hacer? ¿Conectarme a la base de datos MySQL? ¿Escribir un plugin de Discourse?

1 me gusta

Esas son básicamente tus opciones, sí.

1 me gusta

¿Está documentado el esquema de la base de datos en alguna parte?

¿Cómo me conecto a la base de datos postgres en el docker? Disculpen si es una pregunta tonta.

1 me gusta

Acabo de hablar con nuestro equipo y están de acuerdo, estoy dispuesto a pagarle a alguien para que cree un pequeño plugin de Discourse que exponga a través de un endpoint información JSON sobre un usuario, dada su dirección de correo electrónico.

Encontré que https://github.com/discourse/discourse/blob/main/app/models/user.rb#L1855 tiene la sal “password_hash” y el nombre, nombre de usuario. Pero no tiene el correo electrónico. Para eso, veo user_email discourse/app/models/user_email.rb at main · discourse/discourse · GitHub

Entonces, dada una dirección de correo electrónico, el plugin simplemente buscará en la tabla user_email por el correo, luego encontrará el user_id y obtendrá la fila del usuario, y enviará todos los campos “seguros”, incluida la sal.

Para mayor seguridad, las solicitudes se pueden firmar mediante HMAC utilizando un secreto compartido que se puede proporcionar al plugin.

¿Alguien quiere hacer esto? Envíame un mensaje o responde aquí y házmelo saber cómo contactarte. Esperemos que sea sencillo (un par de SELECTs y una verificación de HMAC si se estableció el secreto). Leeremos el JSON.

1 me gusta

Simplemente usaría el admin/users/list/active.json existente y extendería la respuesta con las contraseñas hasheadas.

Además, cíñete al mecanismo de autenticación de API existente, no reinventes otra rueda.

1 me gusta

¿Entonces dices que hagamos algo único que importe a todos los usuarios unidos con sus sales y contraseñas?

De acuerdo, pero eso aún necesita ser un plugin, ¿no? Así que sería genial si alguien de Discourse pudiera crearlo.

1 me gusta

Probablemente usaría el plugin data explorer para exportar la información que desea. Será mucho más fácil que escribir un nuevo plugin.

1 me gusta

¿Cómo puedo averiguar este valor de configuración? ¿Cuál es el valor predeterminado? ¿Está en el código en alguna parte? @RGJ

root@server:~# cd /var/discourse/
root@server:/var/discourse# ./launcher enter app
Se detectó la arquitectura x86_64.
root@server:/var/www/discourse# rails c
[1] pry(main)> Rails.configuration.pbkdf2_iterations
=> 64000
[2] pry(main)>

¡Gracias! OK @RGJ, un par de preguntas rápidas:

La biblioteca xorcist es solo un xor de cadenas más rápido, ¿verdad? ¿Qué pasa si uno de los caracteres termina siendo 0 porque ‘a’ se hizo xor con ‘a’? ¿Qué pasa con esa cadena? ¿No están las cadenas terminadas en nulo?

Mi objetivo es portar esto a PHP, así que cualquier cosa que puedas hacer para ayudar (como darme información sobre cómo replicarlo en PHP) será muy útil.

Además, ¿qué hace esta línea? ret.bytes.map { |b| (\"0\" + b.to_s(16))[-2..-1] }.join(\"\")

$u = hash_hmac('sha256', $password, $salt . pack('N', 1));
$ret = $u = hash_hmac('sha256', $password, $u);
for ($i=2; $i<$iterations; ++$i) {
  $u = hash_hmac('sha256', $password, $u);
  $ret = ($ret ^ $u);
}
// todo: averiguar qué está haciendo RUBY en esta última línea

¿Es esto cercano? ¿Puedes por favor arreglar este código PHP?

Esta es una función integrada.

$hash = hash_pbkdf2('sha256', 'YourPassword', 'YourSalt', 64000, 64, false);

Normalmente, los algoritmos hash operan sobre datos binarios y el resultado se codifica en hexadecimal o Base64 al salir. Así que eso no es un problema.

1 me gusta

¡Muchas gracias Richard! ¡Me ahorraste MUCHO tiempo al tener que implementarlo en PHP de usuario!

¡Sí! Pude crear un script que recorre todos los usuarios de Discourse y los importa con el hash de su frase de contraseña a nuestra plataforma.

Pronto, podremos permitir que cualquiera que tenga un foro de Discourse agregue también Eventos, Videoconferencias, Medios y más, con Discourse residiendo en la pestaña “Discutir”. Puedes ver el resultado en https://intercoin.app

Básicamente, convertimos cualquier instalación de Discourse en una red social moderna al estilo de Facebook. Hemos trabajado durante años en estas funciones y ahora queremos integrarlas estrechamente con Discourse y WordPress también. Así, las personas pueden combinar WordPress, Discourse y Qbix y autoalojar toda su comunidad.

Pero tengo dos problemas pendientes.

  1. En Qbix, ciframos la contraseña en el cliente al menos con sha1(contraseña + userId) antes de enviarla al servidor. Incluso cuando es https. Lo hacemos para que el servidor o cualquier MITM NUNCA tenga la contraseña, para reutilizarla en varios sitios. Pero, Discourse simplemente envía la contraseña al servidor. Por lo tanto, tuvimos que desactivar este cifrado en el lado del cliente. ¿Es posible realizar algunas iteraciones de hash_pbkdf2 en el lado del cliente y el resto en el lado del servidor? Lo intenté y no parece coincidir:
php > $password = 'abc';
php > $salt = 'def';
php > $a = hash_pbkdf2('sha256', $password, $salt, 64000, 64, false);
php > $b = hash_pbkdf2('sha256', $password, $salt, 1, 64, false);
php > $c = hash_pbkdf2('sha256', $password, $b, 63999, 64, false);
php > echo $a;
9d7a21ae4113bea06d81e0c486f45ab778bb739f19f7a6a305d8401918a9d8a1
php > echo $c;
f42af6861ebcf8560b027276e0d02ad46502636045486057d81be7c4c4aa630e
  1. ¿Sería posible simplemente usar Discourse como un Proveedor de SSO, en lugar de usar nuestro sitio como proveedor de SSO? Entonces, los hosts de los foros de Discourse tendrían aún más probabilidades de expandirlo con las funciones de Qbix, ya que el inicio de sesión seguiría siendo exactamente el mismo, y del lado de Discourse. Facebook, Google y cualquier otra cosa. ¿Hay alguna documentación sobre qué tipo de información devuelve Discourse Connect como Proveedor de SSO a nuestro sitio consumidor? ¿Incluye cosas como la foto que podemos descargar, el nombre, el apellido y al menos el nombre de usuario?

Sinceramente, no creo que enviar una contraseña a través de HTTPS sea tu mayor desafío de seguridad en este momento.

Claro. Creo que obtienes la mayoría de las cosas en el serializador de usuario estándar.
Pero si eso no es suficiente, siempre puedes usar la API para obtener más información de Discourse.

2 Me gusta

Sinceramente, no creo que enviar una contraseña a través de HTTPS sea tu mayor desafío de seguridad en este momento.

Lindo. Veo tu sha1 y te bajo un md5 :slight_smile:

Veo por qué ese pbkdf2 no funciona realmente para dividirlo… el problema es la primera línea:

U1 = PRF(Password, Salt + INT_32_BE(i))
U2 = PRF(Password, U1)
⋮
Uc = PRF(Password, Uc−1)

¿Alguna idea para dividirlo, sin embargo? Supongo que puedo usar la biblioteca php pura del espacio de usuario: pbkdf2/src/PBKDF2.php at master · Spomky-Labs/pbkdf2 · GitHub

Recomendaría a Discourse que hashee las cosas con una sal (userId funciona) antes de enviar la contraseña por la red. ¿Por qué no? No tiene por qué ser incompatible con lo que almacenas en la base de datos ahora. Simplemente haz las primeras 100 iteraciones en Javascript, luego resta 10 de 64000. Tienes una implementación personalizada de todos modos (copiada de rails), así que simplemente enviarías una variable isHashed, y si es verdadera, entonces haz solo los “últimos” 64K-10 pasos.

El ID de usuario no se conoce antes del inicio de sesión, así que eso no funcionará…

10 iteraciones no son seguras, y 63990 iteraciones son menos seguras que 64000 iteraciones. Así que, aunque sea marginal, parece que estás reemplazando un método seguro con dos métodos menos seguros y mucha complejidad adicional.

¿Y cuál es la ganancia real?

1 me gusta