/ web

HTTP验证大法(Basic Auth,Session, JWT, Oauth, Openid)

成为一个"认证”老司机

本文翻译自Auth-Boss。 如果有翻译的不恰当或不对的地方, 欢迎指出。

成为一个认证老司机, 了解网络上不同的身份认证方法。

本文档的目的是记录和编目Web上的身份验证方法。
认证指的是创建一个系统的过程,用户可以通过该系统“登录”在线服务,并授予对受保护资源的访问权限。
以下引用可能更好地总结我想要解释的内容:

客户端认证涉及向Web上的服务器证明客户端(或用户)的身份。[1]

Who

我是一个自学成才的开发人员,热爱开源技术,学习,指导和知识共享。

Why

我写这篇指南,因为关于验证这方面的信息很难直接找到。我决定戴上我的“研究帽“,做一些跑腿的工作。

How

我写作风格简洁,会用到一些技术词。

**免责声明:**本文档不作为包含所有认证方法的网络的目录;
本文档也不旨在提供“最佳”认证方法。
没人给钱贿赂我。
如果你想赞助我那更棒, 可以用一些其它方式, 比如领养一只小狗,或者帮助正在辛劳的人们。

**引文:**这些引文表示我引用的来源。
如果你想让我收下链接/更好地引用我的来源(不仅仅是一个链接到原始发布),我可以这样做。
如果你想让我更好的引用引文, 我当然愿意,不过请先让我知道<3。

遗漏,错误:
犯错并不罕见,我并不是安全方面的专家
如果你看到一些可以改进的东西请PR,告诉我哪里弄错了,我可以改进。
有关PR更多的信息,请查看CONTRIBUTIONS.md。

假想案例

我将使用本文档中的一个常见示例来说明在“客户端”(用户在其计算机前面)和“服务器”(后台)上发生的情况的登录流程。

我们的例子将会有一个想象的朋友:Beorn。
Beorn喜欢针织,经常去http://knittingworld.com购买用品。
Beorn在knittingworld有一个帐号,我们将看到他登陆的例子。

一般最佳做法

在讨论用于管理身份验证的技术之前,以下是你不应该做的。

以下某些项目可能不直接与登录/身份验证/用户注册有关,但通常有用。

  • 切勿将密码存储为数据库中的纯文本。
  • 不要写自己的哈希算法(除非你真的很聪明)
  • 不要写自己的认证技术(再次,除非你真的很聪明)。
  • 使用HTTPS。

一些忠告

我们还发现许多网站设计自己的身份验证机制,以提供更好的用户体验。
不幸的是,设计师和实现者通常没有安全背景,因此不能很好地理解他们可以使用的工具[2]

术语

web验证开发领域中有相当多的术语。
下面是一个术语列表,您将在下面看到。

HTTP 超文本传输协议。

这是一个大的概念。我只能简单的解释一下它的含义。
Web是围绕HTTP构建的 - 它是用于在Web服务器和用户之间通信的协议。

您的浏览器被视为HTTP客户端,因为它向HTTP服务器发送请求。
你的客户可以做很多不同类型的请求, - 你可能听说过一些最流行的请求 - POST POST PUT和DELETE。

HTTP服务器向您的浏览器 - 客户端发送响应。
这些响应就是资源。
资源可以是(但不限于):HTML文件,图像,文本,JSON等。
你可以认为资源是从服务器返回的“文件”。

关于此主题的其他链接:

HTTPS

HTTPS是安全的HTTP。
它与SSL / TLS密切相关。
最初在互联网上的支付交易很受欢迎,最近变得越来越普遍。
您可能会认为https是“在浏览器中显示在我的网址左侧的绿色文本”;
经常伴随着锁的图标或类似的东西。

HTTPS是用TLS(或在过去的日子里,SSL)封装的HTTP,以保护浏览器和服务器之间的流量。

HTTPS会对与您的HTTP请求一起发送的信息和发回的响应进行加密。
这在我们开始谈论身份验证时尤其重要!

来自维基百科:

HTTPS在不安全的网络创建安全通道。
这确保了合理的保护免受窃听者和中间人攻击,只要使用足够的密码套件并且服务器证书被验证和信任。

TLS / SSL

TLS和SSL是加密协议。
TLS和SSL加密您通过网络发送的数据 - 它旨在防止人们“窃听”或篡改您要发送的数据。
SSLv2和v3今天被视为不安全(请参阅POODLE),因此大多数HTTPS是使用TLS 1.2完成的。
YouTube上有一些有用的视频,有助于解释这些复杂的问题,
这个视频MIT opencourseware挺不错的!

State

statestatefulstatelesspiece of state
这些是术语,它们的定义不同。
在本文里,“piece of state”或“stateful”描述了一块存储在内存中的数据。

HTTP请求通常被描述为“stateless”。
当您访问网站和登录时,您正在传递一些信息以及您的HTTP请求, 用来标识您的身份。
无论你需要使用什么身份验证方法来识别自己,都必须“附加”到某个或另一个HTTP请求,因为你不能简单地将该状态放在HTTP协议本身中 - 必须采取另一种形式,可以凌驾于HTTP协议之上(正如你会看到在本文档的其余部分。)。
可能有点夸张... 我认为这篇来自苏格兰的文章解释的很不错The Ins and Outs of Token Based Authentication

由于HTTP协议是无状态的,这意味着如果我们使用用户名和密码验证用户,那么在下一个请求,我们的应用程序将不知道我们是谁。
我们必须再次验证。

Cookies

Cookie是存储在用户浏览器上的小数据。
Cookie与HTTP相反,是有状态的 - 这意味着尽管HTTP不能存储用户信息,但是cookie可以。

网络Cookie的常见示例:

Beorn访问http://knittingworld.com为他的下一个针织项目购买一些不错的纱线和材料。
他登录后向他的购物车添加了三件商品。突然他听到一声砰!
并意识到他的微波炉里还有一罐金枪鱼。不好!
Beorn关闭了浏览器,立即忘记了他购物车里的东西,跑去检查微波炉。
罐头金枪鱼的酱汁已经洒在他家的地板上,Beorn回到他的电脑前,并重新访问https://knittingworld.com ...想看看他之前加入购物车的商品还在不在。
Cookies。

有不同种类的Cookies。
有些Cookie会在您的浏览器中停留多天,而其他Cookie会在您关闭浏览器后立即消失。

Cookie在过去(仍然是)认证中起到了很大的作用。
Web服务器通常使用认证cookie来确定用户是否登录以及他们有权访问哪些资源。

持久性cookie有时会带来麻烦,因为它们可以被广告商用来记录关于用户的web习惯的信息。
另一方面,它们通常用于保存用户每次访问站点时不必重新输入其登录凭证。

您可以通过导航到您的(使用Chrome)开发者工具并打开网络标签查看与请求一起发送的Cookie。
刷新页面将显示传入资源的列表,您可以选择其中的一个。
滚动列表, 看看找得到cookie不!
您也可以在开发人员工具的[Application]标签中查看Cookie 的相关信息。

Sessions / Session Management

我并不会一开始就去尝试简单的描绘出sessions,我会引用OWASP:

网络会话是与同一用户相关联的网络HTTP请求和响应事务的序列。
现代和复杂的web应用程序需要在多个请求期间保留关于每个用户的信息或状态。
因此,会话提供了建立变量的能力,例如访问权限和本地化设置,这将适用于在会话期间用户与webApp的每一次交互。

您可以在下面的“Methodologies”部分找到基于会话身份验证的示例。
关于Sessions的更多链接:

Methodologies

以下是用于建立认证的技术方案列表。这不是一个完整的列表!

HTTP基本认证

HTTP基本身份验证(或“基本身份验证”)已经存在了很长时间。看起来人们倾向于使用它,因为它的简单性,它支持跨浏览器。这是一个空白页面,要求基本的验证。这里将演示如何确保webApp正常运行当Beorn关闭游览器重新再打开后:

  • Beorn去http://knittingworld.com 买纱线。
  • 在挑选出纱线后,他点击“购买”按钮购买。
  • 他的浏览器发出一个GET请求,服务器响应401告诉他需要验证。
  • Beorn在他的用户名和密码中输入登录表单。
  • 在他点击登陆后,他的浏览器会发起GET(POST)请求, 并在请求头里带着Authorization。Authorization请求头类似于这样Authorization:QWxhZGRpbjpvcGVuIHNlc2FtZQ==
  • 服务器继续验证身份验证头并确定Beorn是否可以可以提交购买操作。浏览器会记住Authorization,之后的每一次游览器提交的请求,都会在请求头里加上Authorization:QWxhZGRpbjpvcGVuIHNlc2FtZQ==, 直到游览器关闭。

关于HTTP基本身份验证的一些重要注意事项:

  • 上面的示例授权头部看起来不像用户名和密码,但是这是因为它是base64编码。它没有加密。
  • 如果使用HTTP基本认证,请使用HTTPS。如果使用HTTP,身份验证凭据将作为明文发送到服务器。这不好。用户的用户名和密码通过线路仅作为base64编码文本发送 - 这对于解码来说很简单。通过使用HTTPS / TLS,您确保从客户端发送到服务器的数据被加密。
  • HTTP基本验证由游览器实现,今天很少使用。
  • 基本验证使用API​​的基本认证,当与令牌组合时,(稍后讨论)只是一个授权报头,是完全合理的。它有额外的好处,不需要API客户端维护一个额外的会话cookie,并且,因为大多数系统日志查询参数而不是标题,将不会被默认记录。
  • 基本验证与Token组合的时候, 好处很多,比如不需要客户端单独维护一个cookie, 并且也不会被客户端记录。

链接

基于Session的认证

Session认证已经存在了一段时间,并且平常用的比较多。基于session的身份验证的关键是,用户的登录与服务器上的内存的一段状态或key-value存储(如Redis中)相关联。

让我们看看我们的朋友Beorn使用基于session的身份验证的示例。

  • Beorn去http://knittingworld.com 买一些东西。
  • 当Beorn登录时,他将他的凭据发送到服务器。
  • 当凭据到达服务器时,服务器以这种方式或另一种方式需要检查Beorn是否是其数据库中的用户。在这一点上,Beorn还没有登录。
  • Beorn的凭据匹配成功,所以他可以登录。
  • Beorn需要一些东西来识别他对服务器的未来请求 - 特别是如果他想要购买东西(必须登陆才能买)。这就是认证session的思想。
  • 现在服务器知道Beorn是谁,并且已经将他识别为数据库中的用户,服务器将向他(或“返回”)发送一个cookie,这可以将Beorn列为在以后的请求中是已经登陆的用户。
  • 现在,Beorn已经验证,并在他的浏览器上有一个session cookie(cookie的一种)。
  • 当Beorn转到页面http://knittingworld.com/great_deals.html 他正在做另一个HTTP请求 - 但这次,他的session cookie将放在HTTP请求头里发送到服务器。
  • 服务器将根据与内存中Session信息匹配的cookie进行身份验证(可以用redis,memcache等数据库来保存)
  • 当Beorn从http://knittingworld.com 退出时,他在服务器(或Redis等)上的会话实例将过期,他的会话cookie也会过期。

基于Token的认证

基于令牌的认证已经变得更加普遍最近随着RESTful API的应用,单页应用程序和微服务的兴起。

什么是token?

token是一小块数据。

利用基于Token的认证的认证系统意味着用户向服务器发出的请求携带token以执行认证逻辑。当发出HTTP请求时, token是验证用户是否有资格访问资源的凭证。

token认证是无状态的,而基于session的认证意味着在您的服务器(或在Redis等)中的某个地方保存着状态用以识别用户。

Auth0的博客文章Cookies vs Tokens:The Definitive 描述了cookie和令牌之间的身份验证流程的差别的:

基于session的认证流程:

1. 用户输入其登录信息
2. 服务器验证信息是否正确,并创建一个session,然后将其存储在数据库中
3. 具有sessionID的Cookie将放置在用户浏览器中
4. 在后续请求中,会根据数据库验证sessionID,如果有效,则接受请求
5. 一旦用户注销应用程序,会话将在客户端和服务器端都被销毁

基于令牌的认证流程:

1. 用户输入其登录信息
2. 服务器验证信息是否正确,并返回已签名的token
3. token储在客户端,最常见的是存储在`local storage`中,但也可以存储在session存储或cookie中
4. 之后的HTTP请求都将token添加到请求头里
5. 服务器解码JWT,并且如果令牌有效,则接受请求
6. 一旦用户注销,令牌将在客户端被销毁,不需要与服务器进行交互一个关键是,令牌是无状态的。后端服务器不需要保存令牌或当前session的记录。

哇标记听起来很酷。他们比基于session的身份验证更好吗?

问错人了,伙计。我只是告诉你存在这个验证方式。我不会比较没有意义的比较,我尽最大努力做到这一点。有关更多有趣的免责声明,请访问上面的免责声明部分。令牌的类型一些常见的令牌包括JWT(下面讨论),SWT(简单网络令牌)和SAML(安全断言标记语言)

链接

JWT

JWT代表“JSON Web Token”。 JWT是一种基于Token的认证。 JWT基于Web标准。现在JWT用的越来越多;JWT是Token认证的一种,所以说JWT基于Token的认证。再次,基于Token的认证的不同方法具有不同的优点和缺点。因此,上面的基于令牌的认证部分中的很多信息适用于此。

来自JWT RFC 7519标准化的摘要说明:JSON Web Token(JWT)是一种紧凑的,URL安全的方式,表示要在双方之间传输的声明。

JSON Web令牌是一个字符串。它可能看起来像这样:

eyJhbGciOIJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

上面的字符串是你在使用JWT执行身份验证时可能看到的;它是在认证时从服务器返回的凭证。JWT经过验证并且安全,因为它们使用私钥进行“数字签名”,并使用密钥进行身份验证。

JWT的结构?

JWT是一个自包含的数据块。每个JWT由payloadsignature组成。当您的服务器创建token时,您还可以为token分配唯一的数据,可以在前端使用。这可以用于保存稍后进行其他数据库调用的需要。你仍然应该警惕在发送给客户的令牌中发布机密信息(比如说用户密码等等)。

在Python中创建JWT令牌的示例函数:

def create_token(user):
    """Create a JWT token, set expiry, iat, etc"""
    payload = {
        'sub': user.id,
        'name': user.first_name,
        'role_id': user.role_id,
        'iat': datetime.utcnow(),
        'exp': datetime.utcnow() + timedelta(days=1)
    }
    token = jwt.encode(payload, MY_SECRET_KEY_SHHH_DONT_TELL_ANYONE, algorithm='HS256')
    return token.decode('unicode_escape')

上面的键sub,iat和exp跟随保留的JWT键,但我也添加了用户的名称和role_id。你将需要一个库来编码/解码JWT令牌。 JWT.io列出了许多语言的库。

链接

OAuth

OAuth是一种认证协议,允许用户对没有密码的服务器执行认证。 OAuth存在很多版本 - OAuth 1.0,OAuth 1.0a和OAuth 2.0。

如果您曾使用Twitter,Google或Facebook帐户登录了某项服务,那么您已使用OAuth。

OAuth提供商(Facebook,Google等)通过提供您的服务(“OAuth客户端”)身份验证方式的私有,唯一的访问令牌,允许登录。

如果您要使用OAuth让用户登录您的服务,则需要将您的服务器注册为OAuth客户端。这通常会设置一个客户端ID和客户端密钥。登录到您的服务的用户将重定位到OAuth提供程序,用户可以在其中确认他们确实想要“登录”(即允许他们登录的服务器)访问OAuth提供程序的任何必需的信息。 )在我们的朋友Beorn的情况下...

在我们的朋友Beorn的情况下...

  • Beorn去http://knittingworld.com 买东西。
  • Beorn决定使用他的Google帐户登录。
  • 提示Beorn输入他的google帐户信息(如果他还没有登录
  • 输入信息后,Google(或者其它的OAuth提供商)将提示他是否要使用他的Google帐户登录http://knittinggworld.com
  • 接受后,Beorn被重定向到http://knittingworld.com
  • 如果knittingworld需要访问关于Beorn信息的资源,它可以向资源服务器(通过OAuth提供者)请求访问它们,只要它的token是有效的。

OWASP说:

建议使用OAuth 1.0a或OAuth 2.0,因为已发现第一个版本(OAuth1.0)容易受到session固定的影响。OAuth 2.0依靠HTTPS进行安全保障,目前OAuth的API(如Facebook,Google,Twitter和Microsoft)已经实现了。 OAuth1.0a很难使用,因为它需要使用用于数字签名的加密库。然而,由于OAuth1.0a不依赖HTTPS来实现安全性,因此它更适合于更高风险的事务。

链接

OpenId

OpenId是另一种不需要密码的身份验证协议(类似于OAuth)。 OpenId网站有一个非常简洁明了的描述,在我看来:

OpenID允许您使用现有帐户登录多个网站,而无需创建新密码。您可以选择将信息与您的OpenID相关联,以便与您访问的网站(例如姓名或电子邮件地址)共享。使用OpenID,您可以控制与您访问的网站共享的信息量。使用OpenID,您的密码仅提供给您的身份提供商,然后该提供商会确认您访问的网站的身份。除了您的提供商,没有网站曾经看到您的密码,因此您不需要担心一个不道德或不安全的网站危害您的身份。

虽然从2005年开始,最近(2014年-h),OpenId发布了OpenId Connect,这是一种“基于OAuth 2.0系列规范的可互操作身份验证协议”(源)

OpenId和OAuth有什么区别?

OpenId类似于OAuth,但有一些差异。类似地,OpenId依赖于与第三方(依赖方(您登录的站点))交互以提供认证凭证的身份提供商。

不同的是,您可以使用OAuth允许您登录的网站能够访问来自提供程序的数据。这听起来可怕和混乱,但这里有一个简单的例子:

  • Beorn注册为twitter。他要推销他编织的帽子
  • Beorn不知道follow谁,没有人follow他。 Beorn悲伤的感觉不重要。
  • Twitter提示Beorn使用OAuth连接他的Google帐户,以便他可以导入他的联系人到Twiiter。
  • Beorn follow了一群人,包括来自他多年没有见过的高中的老朋友
  • Beorn这样做了,现在他正在不停地tweeting。

链接

这里是Stack Overflow的登录页面的图片,它提供了许多不同的身份验证方法:

资源和脚注