2016-03-20 2 views
2

J'ai une application rails multi-locataires qui permet aux comptes d'utiliser leur propre domaine personnalisé. L'application est hébergée sur Heroku et le domaine parent a un certificat SSL. Je souhaite que mes utilisateurs de domaine personnalisés puissent se connecter à l'aide du domaine parent (www.foo.com) et être redirigés vers leur domaine personnalisé (www.bar.com) Comment puis-je conserver la session sur le domaine personnalisé lorsque l'utilisateur signes dans?Comment authentifier un utilisateur sur un domaine (avec ssl) et rediriger et créer la session dans un domaine différent?

Cette fonctionnalité est très similaire à celle utilisée par Shopify.

+0

s'il vous plaît élaborer un peu, est le domaine personnalisé un sous-domaine? Les locataires utilisent-ils tous la même instance d'application de rails? HTTPS? – sled

+0

Bonjour. Tous les comptes utilisent la même instance de rails. Le domaine parent a ssl, ceux personnalisés ne le sont pas. C'est pourquoi je veux signer des utilisateurs via https://www.foo.com (domaine avec ssl) et les rediriger vers http://www.bar.com. – user177468

+0

bien - ce qui nécessite SSL pour vos locataires à travers par exemple CloudFlare n'est pas une option? Le point ici est, que même si vous protégez les informations d'identification de connexion via SSL, le cookie de session peut être intercepté par un man-in-the-middle après la connexion. – sled

Répondre

2

Jetons un coup d'œil à shopify et comment ils le font.

Shopify distingue deux cas:

  • domaine du locataire a SSL
  • Le domaine du locataire n'a pas SSL

Cas n ° 1 - Le domaine du locataire SSL

Domaine fictif du locataire: https://www.secure.shop

Le formulaire inscrit forme le point à https://www.secure.shop/signup et après une inscription réussie, je reçois un 302 Found qui me redirige vers https://www.secure.shop et définit un cookie de session.

-> POST https://www.secure.shop/signup, (signup data) 
<- 302 Found 
    Location: https://www.secure.shop 
    Set-Cookie: _session_id=eba010959d42ec1b734c7bc335ca13cb; path=/;secure; HttpOnly 
-> GET https://www.secure.shop 
<- 200 OK 

Les se identifier forme de points à https://www.secure.shop/login et après une ouverture de session réussie je reçois un 302 Found qui me redirige vers https://www.secure.shop et définit un cookie de session.

-> POST https://www.secure.shop/login, (credentials) 
<- 302 Found 
    Location: https://www.secure.shop 
    Set-Cookie: _session_id=238aba8be83ceb3ba4a8ae4d94b1b026; path=/;secure; HttpOnly 
-> GET https://www.secure.shop 
<- 200 OK 

Le check-out passe par https://www.secure.shop/checkout.

Les log-out des points à https://www.secure.shop/logout et ce qui se passe est:

-> GET https://www.secure.shop/logout 
<- 302 Found 
    Location: https://www.secure.shop 
    Set-Cookie: _session_id=3b778bb251e170a9e3b1cd8794862203; path=/; secure; HttpOnly 
-> GET https://www.secure.shop 
<- 200 OK 

Conclusion: Tout fonctionne sous le domaine du locataire. Un cookie de session, pas de magie impliquée.

Cas n ° 2: Le domaine du locataire n'a pas SSL

domaine fictif du locataire: http://www.insecure.shop

Le formulaire d'inscription des points à https://insecureshop.myshopify.com/account et quand je crée un nouveau compte les éléments suivants se produit:

-> POST https://insecureshop.myshopify.com/account, (signup data) 
<- 302 Found 
    Location: http://www.insecureshop.com/account?sid=514d3e699fd55ddb7c12398405e65abf 
    Set-Cookie: _secure_session_id=c31d544b27ee8a49b5a6cf9e303e6829; path=/; secure; HttpOnly 
-> GET http://www.insecureshop.com/account?sid=514d3e699fd55ddb7c12398405e65abf 
<- 200 OK 
    Set-Cookie: _session_id=18d5f07e1e61d8707e111879860abad6; path=/; HttpOnly 
Les

connexion à former des points à https://insecureshop.myshopify.com/account/login et ce qui se passe est:

-> POST https://insecureshop.myshopify.com/account/login, (credentials) 
<- 302 Found 
    Location: http://www.insecure.shop/account?sid=a232e58b8cb9fb4936ddf889ab7e73e4 
    Set-Cookie: _secure_session_id=d54a653cf4fcb66b831968e9e669b005; path=/; secure; HttpOnly 
-> GET http://www.insecure.shop/account?sid=a232e58b8cb9fb4936ddf889ab7e73e4 
<- 200 OK 
    Set-Cookie: _session_id=44e041cdbcb64d2a2281bb64db52ada0; path=/; HttpOnly 

Le check-out passe par https://checkout.shopify.com

Les log-out des points à http://www.insecure.shop/account/logout et ce qui se passe est:

-> GET http://www.insecure.shop/account/logout 
<- 302 Found 
    Location: https://insecureshop.myshopify.com/account/logout?sid=d6c774d39307def7f772de31031c665c 
    Set-Cookie: _session_id=8dfb0a130d6f479d1af3a52c40ad3be6; path=/; HttpOnly 
-> GET https://insecureshop.myshopify.com/account/logout?sid=d6c774d39307def7f772de31031c665c 
<- 302 Found 
    Location: http://www.insecure.shop 
    Set-Cookie: _secure_session_id=18fcde259616586f89831399cc9c2425; path=/; secure; HttpOnly 
-> GET http://www.insecure.shop 
<- 200 OK 

Conclusion: le cas d'un magasin non sécurisé, tout est fait deux fois, deux sessions distinctes (et différentes!) sont créées une fois sur l'insécurité d du locataire omain et une fois sur myshopify sous-domaine du locataire. Les deux sessions pointent vers le même enregistrement d'utilisateur dans le back-end.

Les informations d'identification sont envoyées cryptées et un jeton à usage unique est transmis sur la redirection vers le domaine non sécurisé qui crée ensuite une session authentifiée sur le domaine non sécurisé. Ce jeton est transmis en texte brut.

La première chose qui apparaît dans votre esprit est probablement:

Et si un homme-in-the-middle INTERCEPTIONS ce jeton et la session détourne?

Eh bien, l'attaquant sera authentifié comme vous sur http://www.insecure.shop, mais pas sur https://insecureshop.myshopify.com.

si nous essayons d'être intelligent et utiliser une requête AJAX avec CORS et réglez le cookie de session manuellement avec JavaScript, donc pas redirect avec des jetons magiques se produisent

Cela ne permet pas non plus puisque le cookie de session lui-même est transmis en texte clair tout le temps, de sorte que la session peut être détournée de toute façon. Pire encore, nous aurions une seule session. Pourquoi utilisent-ils deux identifiants de sessions/sessions différents pour le même utilisateur?

gît la magie, votre session sur http://www.insecure.shop peut être compromise facilement, mais votre session https://insecureshop.myshopify.com ne soit pas compromise puisque l'attaquant ne connaît pas l'ID de session parce que le cookie a été transmis par SSL et l'ID de session est différent de l'insécurité.

Mais encore l'attaquant peut abuser mon compte sur http://www.insecure.shop

vrai, mais qu'est-ce qu'il peut faire? Il peut ajouter des produits à votre panier, lire votre profil et ainsi de suite, mais il ne peut pas faire un check-out et débiter votre carte de crédit. Pourquoi? Parce que la vérification passe par la partie sécurisée au https://insecureshop.myshopify.com, pour laquelle il n'a pas le cookie de session et n'est donc pas authentifié.

Mais si l'attaquant est intelligent, il pourrait tout simplement changer le mot de passe et re-connexion à la fois sur la partie non sécurisé et sécurisé

Non si vous ajoutez des mesures appropriées, à savoir demander à l'utilisateur d'entrer son mot de passe faire des changements de profil. L'attaquant ne connaît pas les informations d'identification car elles ont été transmises par SSL.

encore, n'est pas là une meilleure solution

Il y a - utiliser HTTPS partout, CloudFlare pour des exemples, il est mort facile à mettre SSL devant les domaines de vos clients. Cela vous donne à la fois moins de frais généraux à mettre en œuvre et une valeur ajoutée pour vos clients et les clients de vos clients. La situation gagnant-gagnant. Vous n'avez pas besoin d'utiliser une solution tierce comme CloudFlare pour cela, puisque vous êtes en charge - tout le trafic passe par votre serveur/proxy frontal (par exemple nginx). Vous pouvez gérer les certificats SSL pour vos clients et les facturer, mais cela devient assez lourd à la fois pour la comptabilité et la configuration puisque chaque domaine a besoin de son propre certificat.

MISE À JOUR (important)

S'il vous plaît noter la différence subtile dans le cas d'insécurité, les deux témoins ont les noms _session_id et _secure_session_id. C'est pour une bonne raison que les deux sessions existent sur la même instance de rails et qu'elles peuvent être utilisées de manière interchangeable, ce qui est une mauvaise chose. Ce que je pense qu'ils font est de mettre un drapeau sur la session si la session a été créée par un canal sécurisé et ajouter une appropriée avant d'agir comme

before_action :require_secure_session, only: :checkout 

def require_secure_session 
    head :unauthorized unless session[:is_secure_flag] 
end 

Sources:

Exemples de magasins ont été prises de http://wemakewebsites.com/blog/80-best-shopify-stores-for-ecommerce-inspiration, https un était # 79, et http un était # 23. Outils utilisés: outils de développement Chrome (onglet réseau), cURL.

+0

Cela me semble assez clair, merci beaucoup! – user177468

+0

J'utilise la gemme rack-cors pour les requêtes inter-domaines, mais je continue d'obtenir une authenticité de jeton CSRF Can not verify sur les logs. Si je fais un skip_before_action: verify_authenticity_token sur le contrôleur tout fonctionne. Est-ce que ce devrait être le moyen de le faire? Laisser la chose gérer les demandes de partout? Je peux écrire les demandes d'origine autorisées dans la configuration rack-cors. Je suppose que ça devrait le faire, mais ça devrait être dynamique. – user177468

0

You may find this answer helpful. Dans l'URL de redirection ou requête HTTP au domaine personnalisé, vous pouvez passer un jeton qui se fait attraper par l'application des rails du domaine personnalisé pour connecter l'utilisateur.

+0

L'idée de base est donc d'envoyer un jeton d'accès unique dans l'URL et de l'utiliser pour se connecter à l'utilisateur. Le problème que je vois avec cette approche est que le jeton sera envoyé non crypté et pourrait être sniffé. Ai-je raison? – user177468

+0

En théorie oui, bien que vous puissiez configurer le jeton pour qu'il expire instantanément pour rendre l'attaque difficile à exécuter. Une autre approche consiste à utiliser des cookies de session pour modifier la façon dont votre site répond aux domaines d'autres origines. Cette réponse peut être utile: http://stackoverflow.com/questions/5123325/correct-way-to-share-login-sessions-across-subdomains-in-rails-3 – Kelseydh

+0

Cet article parle principalement de sous-domaines (qui est facile). Voulez-vous que je lise quelque chose de précis? Je me demande comment Shopify le fait. Ils sont les seuls dont je suis conscient qui gère à peu près la même fonctionnalité. – user177468

0

J'ai créé une application qui avait un fonctionnalité similaire. La façon dont j'ai géré cela était d'avoir une redirection simple. Vous pouvez enregistrer la chaîne de sous-domaine au compte de l'utilisateur lors de la création du compte, lorsque l'utilisateur signe avec succès, vous pouvez les rediriger comme si

if current_user.subdomain? 
     redirect_to root_url(subdomain: current_user.subdomain) 
    else 
     redirect_to root_url, notice: "Logged in!" 
    end 
+0

Merci, mais ce n'est pas ce qui a été demandé. – user177468

0

Je pense qu'il ya 2 façons de le faire:

1. En envoyant des informations de session utilisateur via les en-têtes de requête à un domaine personnalisé lors du retour du domaine parent.

Exemple de code comment il devrait être comme nous stockons session Redis nous pouvons l'envoyer par tête à un autre serveur:

def after_sign_in_path_for(resource_or_scope) 
    #store session to redis 
    if current_user 
    # an unique MD5 key 
    cookies["_validation_token_key"] = Digest::MD5.hexdigest("#{session[:session_id]}:#{current_user.id}") 
    # store session data or any authentication data you want here, generate to JSON data 
    stored_session = JSON.generate({"user_id"=> current_user.id, "username"=>current_user.screen_name, ... ...}) 
    $redis.hset(
     "mySessionStore", 
     cookies["_validation_token_key"], 
     stored_session, 
    ) 
    end 
end 

2. utilisateur Have connexion forcée par le code grâce à un service de session pour démarrer une session sur un domaine personnalisé identique au domaine parent pour le même utilisateur.