如何验证域上的用户(使用 ssl)并在不同的域中重定向和创建会话?
How to authenticate a user on a domain (with ssl) and redirect and create the session in a different domain?
我有一个多租户 rails 应用程序,可以让帐户使用自己的自定义域。该应用程序托管在 Heroku 上,父域具有 ssl 证书。我希望我的自定义域用户能够使用父域 (www.foo.com) 登录并被重定向到他们的自定义域 (www.bar.com) 当用户登录?
此功能与 Shopify 的工作方式非常相似。
You may find this answer helpful. 在对自定义域的重定向 URL 或 HTTP 请求中,您可以传入一个令牌,该令牌会被自定义域的 rails 应用捕获以记录用户英寸
我创建了一个具有类似功能的应用程序。我处理这个问题的方法是进行简单的重定向。您可以在他们创建帐户时将子域字符串保存到用户帐户,然后当用户成功登录时,您可以像这样重定向他们
if current_user.subdomain?
redirect_to root_url(subdomain: current_user.subdomain)
else
redirect_to root_url, notice: "Logged in!"
end
我认为有两种方法可以做到这一点:
1.当从 parent 域返回时,通过请求 headers 将用户 session 信息发送到自定义域。
示例代码应该如何将 session 存储到 redis 我们可以通过 header 将其发送到其他服务器:
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。让用户通过 session 服务通过代码强制登录,以在与同一用户的 parent 域相同的自定义域上启动 session。
让我们来看看 shopify 以及他们是如何做的。
Shopify 区分两种情况:
- 租户的域有 SSL
- 租户的域没有 SSL
案例 #1 - 租户的域具有 SSL
租户的虚拟域:https://www.secure.shop
注册 表单指向 https://www.secure.shop/signup
并且在成功注册后我得到一个 302 Found
将我重定向到 https://www.secure.shop
并设置会话 cookie。
-> 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
登录 表单指向 https://www.secure.shop/login
,成功登录后我得到 302 Found
,将我重定向到 https://www.secure.shop
并设置会话 cookie。
-> 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
签出发生在https://www.secure.shop/checkout
。
注销 指向 https://www.secure.shop/logout
并且发生的情况是:
-> 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
结论:一切都在租户的域下运行。一个会话 cookie,不涉及魔法。
案例 #2:租户的域没有 SSL
租户的虚拟域:http://www.insecure.shop
注册 表单指向 https://insecureshop.myshopify.com/account
,当我创建一个新帐户时,会发生以下情况:
-> 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
登录 表单指向 https://insecureshop.myshopify.com/account/login
,结果是:
-> 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
退房发生在https://checkout.shopify.com
注销 指向 http://www.insecure.shop/account/logout
并且发生的情况是:
-> 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
结论:在不安全的商店的情况下,每件事都做了两次,两个独立的(和不同的!)会话一次在租户的不安全域上创建,一次在租户的 myshopify 子域上创建。两个会话都指向后端的同一条用户记录。
凭据被加密发送,一次性令牌在重定向到不安全域时传递,然后在不安全域上创建经过身份验证的会话。此令牌以纯文本形式传输。
你首先想到的可能是:
如果中间人拦截该令牌并劫持会话怎么办?
好吧,攻击者将在 http://www.insecure.shop
上以您的身份进行身份验证,但不会在 https://insecureshop.myshopify.com
上进行身份验证。
如果我们尝试变得聪明并使用带有 CORS 的 AJAX 请求并使用 JavaScript 手动设置会话 cookie,那么不会发生带有魔法令牌的重定向
这也无济于事,因为会话 cookie 本身始终以纯文本形式传输,因此无论如何会话都可能被劫持。更糟糕的是,我们只有一节课。
为什么他们对同一用户使用两个不同的会话/会话 ID?
神奇之处在于,您在 http://www.insecure.shop
上的会话很容易被破坏,但是您在 https://insecureshop.myshopify.com
上的会话不会受到破坏,因为攻击者不知道会话 ID,因为 cookie 已传输通过 SSL 并且会话 ID 与 "insecure" 不同。
但攻击者仍然可以在 http://www.insecure.shop
上滥用我的帐户
没错,但他能做什么?他可以将产品添加到您的购物车、阅读您的个人资料等等,但他不能结账和从您的信用卡中扣款。为什么?因为签出通过 https://insecureshop.myshopify.com
处的安全部分,他没有会话 cookie,因此未通过身份验证。
但是如果攻击者很聪明,他可以在不安全和安全部分更改密码并重新登录
如果您添加适当的措施,即要求用户输入密码以进行任何配置文件更改,则不会。攻击者不知道凭据,因为它们是通过 SSL 传输的。
还是有更好的解决办法吗
有 - 在任何地方都使用 HTTPS,例如 CloudFlare 使得将 SSL 放在客户域前面变得非常容易。这既可以减少您的实施开销,又可以为您的客户和客户的客户增加价值。双赢局面。
您不必为此使用像 CloudFlare 这样的第三方解决方案,因为您是负责人 - 所有流量都通过您的服务器/前端代理(例如 nginx)。您可以为您的客户管理 SSL 证书并向他们收费,但是这对于记帐和配置来说都变得非常麻烦,因为每个域都需要自己的证书。
更新(重要)
请注意不安全情况下的细微差别,两个 cookie 的名称分别为 _session_id
和 _secure_session_id
。这是有充分理由的,因为两个会话都存在于同一个 rails 实例上,并且它们可以互换使用,这是一件坏事。我认为他们正在做的是在会话上设置一个标志,以确定会话是否是通过安全通道创建的,并添加适当的操作前操作,例如
before_action :require_secure_session, only: :checkout
def require_secure_session
head :unauthorized unless session[:is_secure_flag]
end
来源:
商店示例取自 http://wemakewebsites.com/blog/80-best-shopify-stores-for-ecommerce-inspiration
,https 一个是#79,http 一个是#23。使用的工具:Chrome 开发人员工具(网络选项卡)、cURL。
我有一个多租户 rails 应用程序,可以让帐户使用自己的自定义域。该应用程序托管在 Heroku 上,父域具有 ssl 证书。我希望我的自定义域用户能够使用父域 (www.foo.com) 登录并被重定向到他们的自定义域 (www.bar.com) 当用户登录?
此功能与 Shopify 的工作方式非常相似。
You may find this answer helpful. 在对自定义域的重定向 URL 或 HTTP 请求中,您可以传入一个令牌,该令牌会被自定义域的 rails 应用捕获以记录用户英寸
我创建了一个具有类似功能的应用程序。我处理这个问题的方法是进行简单的重定向。您可以在他们创建帐户时将子域字符串保存到用户帐户,然后当用户成功登录时,您可以像这样重定向他们
if current_user.subdomain?
redirect_to root_url(subdomain: current_user.subdomain)
else
redirect_to root_url, notice: "Logged in!"
end
我认为有两种方法可以做到这一点:
1.当从 parent 域返回时,通过请求 headers 将用户 session 信息发送到自定义域。
示例代码应该如何将 session 存储到 redis 我们可以通过 header 将其发送到其他服务器:
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。让用户通过 session 服务通过代码强制登录,以在与同一用户的 parent 域相同的自定义域上启动 session。
让我们来看看 shopify 以及他们是如何做的。
Shopify 区分两种情况:
- 租户的域有 SSL
- 租户的域没有 SSL
案例 #1 - 租户的域具有 SSL
租户的虚拟域:https://www.secure.shop
注册 表单指向 https://www.secure.shop/signup
并且在成功注册后我得到一个 302 Found
将我重定向到 https://www.secure.shop
并设置会话 cookie。
-> 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
登录 表单指向 https://www.secure.shop/login
,成功登录后我得到 302 Found
,将我重定向到 https://www.secure.shop
并设置会话 cookie。
-> 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
签出发生在https://www.secure.shop/checkout
。
注销 指向 https://www.secure.shop/logout
并且发生的情况是:
-> 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
结论:一切都在租户的域下运行。一个会话 cookie,不涉及魔法。
案例 #2:租户的域没有 SSL
租户的虚拟域:http://www.insecure.shop
注册 表单指向 https://insecureshop.myshopify.com/account
,当我创建一个新帐户时,会发生以下情况:
-> 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
登录 表单指向 https://insecureshop.myshopify.com/account/login
,结果是:
-> 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
退房发生在https://checkout.shopify.com
注销 指向 http://www.insecure.shop/account/logout
并且发生的情况是:
-> 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
结论:在不安全的商店的情况下,每件事都做了两次,两个独立的(和不同的!)会话一次在租户的不安全域上创建,一次在租户的 myshopify 子域上创建。两个会话都指向后端的同一条用户记录。
凭据被加密发送,一次性令牌在重定向到不安全域时传递,然后在不安全域上创建经过身份验证的会话。此令牌以纯文本形式传输。
你首先想到的可能是:
如果中间人拦截该令牌并劫持会话怎么办?
好吧,攻击者将在 http://www.insecure.shop
上以您的身份进行身份验证,但不会在 https://insecureshop.myshopify.com
上进行身份验证。
如果我们尝试变得聪明并使用带有 CORS 的 AJAX 请求并使用 JavaScript 手动设置会话 cookie,那么不会发生带有魔法令牌的重定向
这也无济于事,因为会话 cookie 本身始终以纯文本形式传输,因此无论如何会话都可能被劫持。更糟糕的是,我们只有一节课。
为什么他们对同一用户使用两个不同的会话/会话 ID?
神奇之处在于,您在 http://www.insecure.shop
上的会话很容易被破坏,但是您在 https://insecureshop.myshopify.com
上的会话不会受到破坏,因为攻击者不知道会话 ID,因为 cookie 已传输通过 SSL 并且会话 ID 与 "insecure" 不同。
但攻击者仍然可以在 http://www.insecure.shop
没错,但他能做什么?他可以将产品添加到您的购物车、阅读您的个人资料等等,但他不能结账和从您的信用卡中扣款。为什么?因为签出通过 https://insecureshop.myshopify.com
处的安全部分,他没有会话 cookie,因此未通过身份验证。
但是如果攻击者很聪明,他可以在不安全和安全部分更改密码并重新登录
如果您添加适当的措施,即要求用户输入密码以进行任何配置文件更改,则不会。攻击者不知道凭据,因为它们是通过 SSL 传输的。
还是有更好的解决办法吗
有 - 在任何地方都使用 HTTPS,例如 CloudFlare 使得将 SSL 放在客户域前面变得非常容易。这既可以减少您的实施开销,又可以为您的客户和客户的客户增加价值。双赢局面。
您不必为此使用像 CloudFlare 这样的第三方解决方案,因为您是负责人 - 所有流量都通过您的服务器/前端代理(例如 nginx)。您可以为您的客户管理 SSL 证书并向他们收费,但是这对于记帐和配置来说都变得非常麻烦,因为每个域都需要自己的证书。
更新(重要)
请注意不安全情况下的细微差别,两个 cookie 的名称分别为 _session_id
和 _secure_session_id
。这是有充分理由的,因为两个会话都存在于同一个 rails 实例上,并且它们可以互换使用,这是一件坏事。我认为他们正在做的是在会话上设置一个标志,以确定会话是否是通过安全通道创建的,并添加适当的操作前操作,例如
before_action :require_secure_session, only: :checkout
def require_secure_session
head :unauthorized unless session[:is_secure_flag]
end
来源:
商店示例取自 http://wemakewebsites.com/blog/80-best-shopify-stores-for-ecommerce-inspiration
,https 一个是#79,http 一个是#23。使用的工具:Chrome 开发人员工具(网络选项卡)、cURL。