REST身份验证和公开API密钥

tja*_*ans 92 restful-authentication

我一直在阅读REST,关于它,以及很多其他网站和博客都有很多问题.虽然我从来没有见过这个具体的问题...出于某种原因,我无法围绕这个概念进行思考......

如果我正在构建RESTful API,并且我想要保护它,我看到的方法之一是使用安全令牌.当我使用其他API时,有一个令牌和共享秘密......这是有道理的.我不明白的是,对休息服务操作的请求是通过javascript(XHR/Ajax)进行的,这是为了防止有人用FireBug(或浏览器中的"查看源代码")这样简单的东西来嗅出它复制API密钥,然后使用密钥和密钥冒充该人?

Arj*_*jan 60

我们公开了合作伙伴只能在他们向我们注册的域名上使用的API.它的内容部分是公开的(但最好只在我们知道的域上显示),但对我们的用户来说大多是私有的.所以:

  • 要确定什么是所示,我们的用户必须和我们一起登录,但这是分开处理.

  • 要确定其中显示的数据,公共API密钥是用来限制进入我们知道域,首先要保证用户私有数据不会受到CSRF.

任何人都可以看到此API密钥,我们不会以任何其他方式验证我们的合作伙伴,我们也不需要REFERER.它仍然是安全的:

  1. 当我们get-csrf-token.js?apiKey=abc123被要求时:

    1. abc123在数据库中查找密钥并获取该密钥的有效域列表.

    2. 查找CSRF验证cookie.如果它不存在,则生成安全随机值并将其放入仅HTTP会话cookie中.如果cookie确实存在,请获取现有的随机值.

    3. 从API密钥和cookie中的随机值创建CSRF令牌,然后对其进行签名.(而不是在服务器上保留令牌列表,我们正在签署这些值.这两个值在签名令牌中都是可读的,没关系.)

    4. 将响应设置为不缓存,添加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.');
      }
      
      Run Code Online (Sandbox Code Playgroud)

    笔记:

    • 以上并不妨碍服务器端脚本编造一个请求,但只有保证了域相匹配,如果浏览器请求.

    • JavaScript的同源策略确保浏览器无法使用XHR(阿贾克斯)加载,然后检查JavaScript源.相反,常规浏览器只能使用<script src="https://our-api.com/get-csrf-token.js?apiKey=abc123">(或动态等效)加载它,然后运行代码.当然,你的服务器应该支持跨来源资源共享,也为JSONP生成的JavaScript.

    • 浏览器脚本可以document.domain在加载上述脚本之前更改值.但是,同样的原产地政策只允许通过缩短域去除前缀,像重写subdomain.example.comexample.com,或myblog.wordpress.comwordpress.com,或者在某些浏览器甚至bbc.co.ukco.uk.

    • 如果使用某个服务器端脚本获取JavaScript文件,则服务器也将获取cookie.但是,第三方服务器无法使用户的浏览器将该cookie与我们的域相关联.因此,使用服务器端脚本获取的CSRF令牌和验证cookie只能由后续服务器端调用使用,而不能在浏览器中使用.但是,此类服务器端调用永远不会包含用户cookie,因此只能获取公共数据.这是服务器端脚本可以直接从合作伙伴的网站上获取的相同数据.

  2. 当用户登录时,以您喜欢的任何方式设置一些用户cookie.(在请求JavaScript之前,用户可能已经登录.)

  3. 对服务器的所有后续API请求(包括GET和JSONP请求)必须包括CSRF令牌,CSRF验证cookie和(如果已登录)用户cookie.服务器现在可以确定是否要信任该请求:

    1. 如果由浏览器加载,存在有效的CSRF令牌可确保从预期域加载JavaScript .

    2. 没有验证cookie 的CSRF令牌的存在表示伪造.

    3. CSRF令牌和CSRF验证cookie的存在并不能确保任何事情:这可能是伪造的服务器端请求,也可能是来自浏览器的有效请求.(它不是来自不受支持的域的浏览器的请求.)

    4. 用户cookie的存在确保用户登录,但不确保用户是给定合作伙伴的成员,也不确保用户正在查看正确的网站.

    5. 没有 CSRF验证cookie 的用户cookie的存在表示伪造.

    6. 用户cookie的存在确保当前请求通过浏览器进行.(假设用户不会在未知网站上输入他们的凭据,并假设我们不关心用户使用他们自己的凭据来提出服务器端请求.)如果我们还有 CSRF验证cookie,那么CSRF验证cookie是也收到了使用浏览器.接下来,如果我们还有一个带有有效签名的CSRF令牌,并且 CSRF验证cookie中的随机数该CSRF令牌中的随机数匹配,那么在该CSRF期间的同一早期请求期间也会收到该令牌的JavaScript cookie已设置,因此也使用浏览器.这也意味着上面的JavaScript代码是在设置令牌之前执行的,并且那时域对于给定的API密钥是有效的.

      所以:服务器现在可以安全地使用签名令牌中的API密钥.

    7. 如果服务器在任何时候不信任该请求,则返回403 Forbidden.窗口小部件可以通过向用户显示警告来响应.

我们不需要签署CSRF验证cookie,因为我们将其与签名的CSRF令牌进行比较.不签署cookie会使每个HTTP请求更短,服务器验证速度更快.

生成的CSRF令牌无限期有效,但仅与验证cookie结合使用,因此在浏览器关闭之前一直有效.

我们可以限制令牌签名的生命周期.我们可以在用户注销时删除CSRF验证cookie,以满足OWASP建议.并且为了不在多个合作伙伴之间共享每用户随机数,可以将API密钥添加到cookie名称.但即使这样,当请求新令牌时,也无法轻松刷新CSRF验证cookie,因为用户可能在多个窗口中浏览同一站点,共享一个cookie(在刷新时,将在所有窗口中更新,之后其他窗口中的JavaScript令牌将不再匹配该单个cookie).

对于使用OAuth的用户,请参阅OAuth和客户端小部件,我从中获得了JavaScript的想法.对于API的服务器端使用,我们不能依赖JavaScript代码来限制域,我们使用的是密钥而不是公共API密钥.


Jam*_*.Xu 22

api secret没有明确传递,secret用于生成当前请求的标志,在服务器端,服务器生成跟随相同进程的标志,如果两个符号匹配,则请求被成功验证 - 所以只有标志是通过请求传递的,而不是秘密.

  • 那么如果它只是通过的标志......不是仍然在javascript中暴露...所以如果我通过他们的API(通过javascript调用)在我的网页上放置一张闪烁照片,并且你访问我的页面,那么'我将API密钥暴露给访问我页面的任何人? (9认同)
  • 因此,@ James.Xu声明'秘密用于生成当前请求的标志'是错误的!因为客户端不知道这个秘密,因为将它发送给他是不安全的(他怎么知道呢?)技术上称为"私钥"的"秘密"仅由服务器使用(因为没有人知道它)生成一个与客户的标志进行比较的标志.所以问题是:什么样的数据与除了客户端和服务器之外没有其他人知道的"api密钥"结合在一起?Sign = api_key +什么? (7认同)
  • 我认为我没有正确地提出我的问题......可能部分原因是我一开始并没有找到我想要的东西.当我进行ajax调用时,比如使用jquery,我必须在apj调用中嵌入api键,以便将其传递给服务器......此时有人可以看到API密钥.如果我理解错误的话,如果API密钥没有嵌入到客户端脚本中,它如何与请求一起发送? (6认同)
  • 总结:在使用openapi/restapi之前,人们将被分配一个apikey + apisecret对,apikey + sign将被转移到服务器端以确保服务器知道谁在发出请求,apisecret永远不会被转移到服务器端以获得安全性. (4认同)
  • 你是对的,@ACs。即使两个服务器(网站和第 3 方 API)都知道相同的秘密,也无法在网站服务器上计算出一些签名,然后将*该结果* 放入 HTML/JavaScript,然后让浏览器将其传递给应用程序接口。这样做,*任何*其他服务器都可以从第一个 Web 服务器请求 HTML,从响应中获取签名,然后在他们自己网站上的 HTML 中使用它。(我真的认为上面的帖子没有回答关于 HTML* 中的 *公共 API 密钥如何安全的问题。) (2认同)

chr*_*ris 9

这个问题有一个公认的答案但只是为了澄清,共享秘密身份验证的工作原理如下:

  1. 客户端有公钥,这可以与任何人共享,无关紧要,因此您可以将其嵌入到javascript中.这用于标识服务器上的用户.
  2. 服务器有密钥,这个秘密必须受到保护.因此,共享密钥身份验证要求您可以保护您的密钥.因此,直接连接到另一个服务的公共javascript客户端是不可能的,因为您需要服务器中间人来保护秘密.
  3. 服务器使用包含秘密密钥的某种算法签署请求(秘密密钥有点像盐),并且优选地,时间戳然后将请求发送到服务.时间戳是为了防止"重播"攻击.请求的签名仅在大约n秒内有效.您可以通过获取应包含签名中包含的时间戳值的时间戳标头来检查服务器上的内容.如果该时间戳已过期,则请求失败.
  4. 该服务获取的请求不仅包含签名,还包含以纯文本签名的所有字段.
  5. 然后,服务使用共享密钥以相同方式签署请求并比较签名.