我一直在阅读REST,关于它以及很多其他网站和博客都有很多问题。虽然我从来没有见过这个具体的问题...出于某种原因,我无法围绕这个概念进行思考......
如果我正在构建RESTful API,并且我想保护它,我看到的方法之一就是使用安全令牌。当我使用其他API时,有一个令牌和共享秘密......这是有道理的。我不明白的是,对休息服务操作的请求是通过javascript(XHR / Ajax)进行的,这是为了防止有人用FireBug(或浏览器中的“查看源代码”)这样简单的东西来嗅出它复制API密钥,然后使用密钥和秘密冒充该人?
答案 0 :(得分:59)
我们正在公开合作伙伴只能在他们向我们注册的域名上使用的API。它的内容部分是公开的(但最好只在我们知道的域上显示),但对我们的用户来说大多是私有的。所以:
要确定显示的是什么,我们的用户必须与我们一起登录,但这是单独处理的。
要确定显示数据的 ,公共API密钥用于限制对我们所知道的域的访问,最重要的是确保私有用户数据不易受{{ 3}}。
此API密钥确实对任何人都可见,我们不会以任何其他方式验证我们的合作伙伴,我们CSRF。它仍然是安全的:
请求我们get-csrf-token.js?apiKey=abc123
时:
在数据库中查找密钥abc123
并获取该密钥的有效域列表。
查找CSRF验证cookie。如果它不存在,请生成一个安全的随机值并将其放在don't need REFERER会话cookie中。如果cookie确实存在,请获取现有的随机值。
从API密钥和Cookie中的随机值创建CSRF令牌,a HTTP-only。 (而不是在服务器上保留令牌列表,我们会对这些值进行签名。这两个值都可以在签名令牌中读取,这很好。)
将响应设置为不缓存,添加cookie,并返回如下脚本:
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.');
}
注意:
上述内容不会阻止服务器端脚本伪造请求,但只能确保域与浏览器请求的匹配
sign it确保浏览器无法使用XHR(Ajax)加载然后检查JavaScript源。相反,常规浏览器只能使用<script src="https://our-api.com/get-csrf-token.js?apiKey=abc123">
(或动态等效项)加载它,然后运行代码。当然,您的服务器应该不支持same origin policy for JavaScript,也不应该为生成的JavaScript支持JSONP。
在加载上述脚本之前,浏览器脚本可以更改document.domain
的值。但是,相同的原始政策只允许通过删除前缀缩短域名,例如将subdomain.example.com
重写为example.com
或myblog.wordpress.com
改为wordpress.com
,或者在某些浏览器中甚至是bbc.co.uk
到Cross-Origin Resource Sharing。
如果使用某个服务器端脚本获取JavaScript文件,则服务器也将获取cookie。但是,第三方服务器无法使用户的浏览器将该cookie与我们的域相关联。因此,使用服务器端脚本获取的CSRF令牌和验证cookie只能由后续服务器端调用使用,而不能在浏览器中使用。但是,此类服务器端调用永远不会包含用户cookie,因此只能获取公共数据。这与服务器端脚本可以直接从合作伙伴的网站上获取的数据相同。
当用户登录时,以您喜欢的任何方式设置一些用户cookie。 (在请求JavaScript之前,用户可能已经登录。)
对服务器的所有后续API请求(包括GET和JSONP请求)必须包含CSRF令牌,CSRF验证cookie以及(如果已登录)用户cookie。服务器现在可以确定是否要信任该请求:
有效的CSRF令牌可确保JavaScript从预期的域加载, if 由浏览器加载。
没有验证Cookie的CSRF令牌的存在表明伪造。
CSRF令牌和CSRF验证cookie的存在并不能确保任何事情:这可能是伪造的服务器端请求,也可能是来自浏览器的有效请求。 (它不是来自不受支持的域的浏览器的请求。)
用户cookie的存在确保用户登录,但不确保用户是给定合作伙伴的成员,也不确保用户正在查看正确的网站。
没有 CSRF验证Cookie的用户Cookie 的存在表明伪造。
用户cookie的存在确保当前请求通过浏览器进行。 (假设用户不会在未知网站上输入他们的凭据,并假设我们不关心用户使用他们自己的凭据来提出服务器端请求。)如果我们还拥有CSRF验证cookie ,然后使用浏览器收到CSRF验证cookie。接下来,如果我们还有一个带有有效签名的CSRF令牌,和 CSRF验证cookie中的随机数与该CSRF令牌中的随机数相匹配,那么就是那个在同一个早期请求期间也收到了令牌,在此期间设置了CSRF cookie,因此也使用了浏览器。这也意味着上述JavaScript代码在设置令牌之前执行,并且那时域对于给定的API密钥有效。
所以:服务器现在可以安全地使用签名令牌中的API密钥。
如果服务器在任何时候不信任该请求,则返回403 Forbidden。窗口小部件可以通过向用户显示警告来响应。
不需要签署CSRF验证cookie,因为我们将其与签名的CSRF令牌进行比较。不签署cookie会使每个HTTP请求更短,服务器验证速度更快。
生成的CSRF令牌无限期有效,但仅与验证cookie结合使用,因此在浏览器关闭之前一直有效。
我们可以限制令牌签名的生命周期。我们可以在用户退出时删除CSRF验证cookie,以满足co.uk
。并且为了不在多个合作伙伴之间共享每用户随机数,可以将API密钥添加到cookie名称。但即使这样,当请求新令牌时,也无法轻松刷新CSRF验证cookie,因为用户可能在多个窗口中浏览同一站点,共享一个cookie(在刷新时,将在所有窗口中更新,之后其他窗口中的JavaScript令牌将不再与该单个cookie匹配。)
对于那些使用OAuth的人,请参阅the OWASP recommendation,我从中获得了JavaScript的想法。对于服务器端使用API,我们不能依赖JavaScript代码来限制域名,我们使用密钥而不是公共API密钥。
答案 1 :(得分:22)
api secret未明确传递,secret用于生成当前请求的符号,在服务器端,服务器生成符号后面的相同进程,如果两个符号匹配,然后成功验证请求 - 因此只有符号通过请求传递,而不是秘密。
答案 2 :(得分:9)
这个问题有一个公认的答案,但为了澄清,共享秘密身份验证的工作原理如下:
答案 3 :(得分:1)
我认为你的意思是会话密钥而不是API密钥。该问题继承自http协议,称为Session hijacking。与任何网站一样,正常的“解决方法”是更改为https。
要运行REST服务安全,您必须启用https,并可能启用客户端身份验证。但毕竟,这超出了REST的想法。 REST从不谈论安全性。
答案 4 :(得分:1)
您要在服务器端执行的操作是生成一个过期的会话ID,该ID会在登录或注册时发送回客户端。 然后,客户端可以使用该会话ID作为共享密钥来签署后续请求。
会话ID只传递一次,必须通过SSL。
参见示例here
在签署请求时使用nonce和timestamp以防止会话劫持。
答案 5 :(得分:1)
我将尝试回答原始背景下的问题。所以问题是“秘密(API)密钥是否可以安全地放在JavaScript中。
在我看来,这是非常不安全的,因为它破坏了系统之间的身份验证的目的。由于密钥将暴露给用户,因此用户可以检索他/她未被授权的信息。因为在典型的休息通信中,身份验证仅基于API密钥。
我认为解决方案是JavaScript调用本质上将请求传递给负责进行休息调用的内部服务器组件。内部服务器组件假设Servlet将从安全源(例如基于权限的文件系统)读取API密钥,插入HTTP标头并进行外部休息调用。
我希望这会有所帮助。