当前位置: 首页 > 科技观察

细说API–Authentication,Authorization,Credentials_0

时间:2023-03-22 17:35:02 科技观察

在一些互联网公司的面试中,面试官经常会问这样一个问题:“如果浏览器cookie被禁用,如何实现用户跟踪和认证?”不幸的是,还有很多考生回答得不相关,搞不清楚cookie和session的区别。工作中还有令人惊讶的真实案例:将用户ID作为令牌存储在本地存储中,因为他们声称要放弃像cookie这样过时的东西;对于一个移动端的项目,服务端给的API需要客户端模拟一个cookie在浏览器中像ajax一样消费API。互联网是建立在HTTP协议之上的,HTTP协议因为简单而流行,但是HTTP协议是无状态的(虚电路在通信层面要比数据报贵很多),所以人们想出了各种方法跟踪用户,包括cookie/session机制、token、flash跨浏览器cookie甚至浏览器指纹。已经有很多关于使用springsecurity等特定技术在各个地方隐藏用户身份的资料(浏览器指纹识别技术甚至不需要存储介质)。本文不打算写框架和代码的具体实现。我们将讨论身份验证和授权之间的区别,然后介绍一些业界广泛使用的技术。下面我们就来谈谈如何为API的构建选择合适的认证方式。身份验证、授权、凭证首先,身份验证和授权是两个不同的概念。为了让我们的API更安全,设计更清晰,有必要了解认证和授权的区别。它们在英语中也是不同的词。.Authentication就是认证,是指当前用户的身份。用户登录后,系统可以跟踪其身份并进行符合相应业务逻辑的操作。即使用户没有登录,大多数系统也会跟踪他的身份并将其视为访客或匿名用户。身份验证技术解决了“我是谁?”问题。授权不同。授权是指允许什么样的身份访问某些资源。获取用户身份后,继续检查用户的权限。单系统授权往往通过认证完成,但在开放API的多系统结构下,授权可以由不同的系统完成,比如OAuth。赋能技术就是要解决“我能做什么?”问题。认证和授权的基础是需要一种介质(凭证)来标记访问者的身份或权利。在现实生活中,每个人都需要一个证书来证明自己的身份,才能访问银行账户、结婚和申请养老保险等,这就是身份验证证书;在古代军事活动中,皇帝会给打仗的将领颁发兵符,下属将领不管持兵符的人是谁,只需要执行相应的兵符命令即可。在互联网世界中,服务器为每个访问者颁发一个sessionID,并存储在cookie中,这是一种凭证技术。数字凭证也存在于SSH登录密钥、JWT令牌、一次性密码等一切事物中。用户帐户不一定是存储在数据库中的表。在一些企业IT系统中,对账号管理和权限的要求比较高。所以账户技术(accounting)可以帮助我们以不同的方式管理用户账户,同时具备在不同系统之间共享账户的能力。比如微软的活动目录(AD),还有简单目录访问协议(LDAP),甚至区块链技术。另一个重要的概念是访问控制策略(AC)。如果我们需要将资源的权限划分到一个非常细的粒度,我们就不得不考虑用户使用什么身份访问受限资源,选择基于访问控制列表(ACL)还是基于用户角色访问控制(RBAC)或者其他访问控制策略。在流行的技术和框架中,这些概念不能孤立地实现,所以在现实中使用这些技术时,人们经常争论一个OAuth2到底是认证还是授权的概念。为了便于理解,我在文末附上了常用技术和概念的词汇表。下面我将介绍几种API开发中经常用到的认证授权技术:HTTPBasicAUthentication、HAMC、OAuth2,以及凭证技术JWTtoken。HTTPBasicAuthentication这个方法你一定用过,但你可能不知道它是什么。不久前,当你访问家用路由器的管理界面时,经常会看到一个浏览器弹窗,要求你输入用户密码。这背后,当用户输入完用户名和密码后,浏览器会为你做一个很简单的操作:将用户名和密码结合起来,对编码后的字符串进行base64编码,加上一个Basic前缀,然后设置名为Authorization的header。内部API也可以很简单的提供HTTPBasicAuthentication认证,那么客户端可以简单的通过Base64传输用户名和密码:用冒号连接用户名和密码,例如username:abc123456为了防止用户名或密码超过ForASCII码范围内的字符,建议使用UTF-8编码。对上述字符串使用Base64编码,例如,dXNlcm5hbWU6YWJjMTIzNDU2在HTTP请求头中加入“Basic+encodedstring”,即:Authorization:BasicQWxhZGRpbjpPcGVuU2VzYW1l这种方法的实现非常简单,大量场景使用.当然缺点也很明显,Base64只能叫编码,不能叫加密(其实客户端没有配置key是没有什么靠谱的加密方式的,我们都依赖TSL协议)。这种方式的致命弱点是如果加密后的密码以明文方式传输,在网络传输过程中很容易被泄露。如果密码没有过期,一旦密码泄露,唯一的办法就是修改密码。HMAC(AK/SK)认证会要求我们在连接一些PASS平台和支付平台的时候,预先生成一个accesskey(AK)和securitykey(SK),然后通过签名完成认证请求,这样可以避免安全密钥被传输,并且在大多数情况下签名只允许使用一次。这种基于AK/SK的认证方式主要是使用基于散列的消息认证码(Hash-basedMessageAuthenticationCode)来实现的,所以有很多地方叫HMAC认证,其实不是很准确。HMAC只是使用一个key值的hash算法来生成消息摘要,在设计API的时候有不同的实现。HMAC作为网络通信认证设计中的凭证生成算法,避免了密码等敏感信息在网络中的传输。基本流程如下:客户端需要在认证服务器中预先设置访问密钥(AK或appID)和SK。调用API时,客户端需要对参数和accesskey进行自然排序,并使用securitykey进行签名生成一个附加的参数digestserver根据预先设置的securitykey进行相同的摘要计算,并要求结果完全一致。注意,SK不能在网络中传输并存储在不受信任的位置(浏览器等),以便使每个请求的签名发生变化。要成为唯一,我们需要在签名中加入一些噪音。行业标准中有两种典型的方法,挑战/响应算法(OCRA:OATHChallenge-ResponseAlgorithm)和基于时间的一次性密码算法(TOTP:Time-basedOne-timePasswordAlgorithm)。挑战/响应算法挑战/响应算法要求客户端向服务器请求一次,得到一个401unauthenticated返回,并得到一个随机字符串(nonce)。按照上述方法将nonce附加到HMAC签名上,服务器端也使用预先分配的nonce进行签名校验。这个随机数只会在服务器上使用一次,所以只能提供这个摘要。Time-basedone-timepasswordauthentication为了避免额外请求获取nonce,还有一种算法是使用时间戳,通过时间同步协商共识,在一定的时间窗口(约1分钟)内有效。这里时间戳只是作为验证的时间窗口,严格来说不能算作基于时间的一次性密码算法。标准的基于时间的一次性密码算法在2FA中被大量使用,例如GoogleAuthenticator不需要网络通信就可以这样做(但依赖于准确的计时服务)。原理是客户端和服务端共享秘钥,然后根据时间窗口通过HMAC算法计算出相同的验证码。TOTP基本原理和常见供应商OAuth2和OpenIDOAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另一个服务提供商上的信息,而无需向第三方网站提供用户名和密码或任何共享他们的数据。OAuth是授权标准,不是认证标准。提供资源的服务器不需要知道确切的用户身份(session),只需要验证授权服务器授予的权限(token)即可。上图只是OAuth的一个简化流程。OAuth的基本思想是通过授权服务器获取访问令牌和刷新令牌(刷新令牌用于刷新访问令牌),然后通过访问令牌从资源服务器获取数据。在具体场景下,有以下几种模式:授权码模式(authorizationcode)简化模式(implicit)密码模式(resourceownerpasswordcredentials)客户端模式(clientcredentials)如果需要获取用户认证信息,OAuth本身并没有定义这部分内容。如果需要识别用户信息,就需要使用另外的认证层,比如OpenIDConnect。accesstoken的验证在一些介绍OAuth的博客中,很少提到资源服务器是如何验证accesstoken的。OAuth核心标准并没有定义这部分内容,但是在其他OAuth标准文档中有两种验证访问令牌的方法。完成授权过程后,资源服务器可以使用OAuth服务器提供的Introspection接口来验证accesstoken,OAuth服务器会返回accesstoken的状态和过期时间。OAuth标准中用于验证令牌的术语是内省。同时需要注意的是accesstoken是用户和资源服务器之间的证书,不是资源服务器和授权服务器之间的证书。资源服务器和授权服务器之间应该使用额外的身份验证(例如基本身份验证)。使用JWT身份验证。授权服务器使用私钥以JWT形式下发访问令牌,资源服务器需要使用预先配置的公钥对JWT令牌进行验证,获取令牌状态和访问令牌中包含的一些信息.因此在JWT方案下,资源服务器和授权服务器不再需要通信,这在某些场景下带来了巨大的优势。同时JWT也有一些弱点,我会在JWT部分进行说明。Refreshtoken和accesstoken几乎每个人在刚开始了解OAuth的时候都会有一个疑问,既然已经有了accesstoken,为什么还需要refreshtoken呢?授权服务器在初始授权请求时会一起返回accesstoken和refreshtoken,后面刷新accesstoken时只需要refreshtoken。访问令牌和刷新令牌的设计意图是不同的。访问令牌设计用于客户端和资源服务器之间的交互,而刷新令牌设计用于客户端和授权服务器之间的交互。在某些授权模式下,访问令牌需要作为资源服务器与浏览器之间的临时会话暴露给浏览器。浏览器与资源服务器之间没有签名机制,accesstoken成为唯一凭证,因此accesstoken的过期时间(TTL)应该尽可能短,以免对用户的accesstoken造成威胁。由于需要访问令牌的时间很短,因此刷新令牌可以帮助用户保持长期状态,避免频繁重新授权。大家会觉得accesstoken保有很长的过期时间就够了吗?其实refreshtoken和accesstoken的区别在于即使refreshtoken被拦截,系统还是安全的,客户端拿refreshtoken获取accesstoken也需要预先配置的安全密钥,客户端和授权服务器之间始终存在安全身份验证。OAuth、OpenID、OpenIDConnect认证术语太多。当我自己搭建认证服务器或者连接第三方认证平台的时候,有时候直到开发工作结束我才明白这些术语。OAuth负责解决分布式系统之间的授权问题,即使有时客户端和资源服务器或认证服务器存在于同一台机器上。OAuth并没有解决认证的问题,而是提供了一个很好的设计,方便与现有的认证系统对接。OpenID解决了分布式系统之间的身份认证问题。OpenIDtoken可用于多系统间用户验证,返回用户信息。可以独立使用,与OAuth无关。OpenIDConnect解决了OAuth体系下的用户认证问题。实现的基本原理是将用户的认证信息(IDtoken)作为一种资源来对待。在OAuth框架下完成授权后,通过accesstoken获取用户身份。这三个概念之间的关系有点难以理解。在现实场景中,如果系统中需要独立的认证体系,OpenID可以在多个系统之间直接使用,无需授权。如果使用OAuth作为授权标准,则可以通过OpenIDConnect完成用户认证。在OAuth等分布式认证授权系统下,JWT对凭证技术有更多的要求,比如包含用户ID、有效期等信息,不需要关联外部存储。因此,业界进一步优化了代币,设计了自成体系的代币。令牌发出后,不需要从服务器存储中检查是否合法。通过解析token可以得到token的有效期和有效性。这是JWT(JSON网络令牌)。JWT是一个自包含的令牌,或价值令牌。我们用来与会话关联的哈希值称为参考令牌。简而言之,一个基本的JWT令牌是一个点状的3段结构。eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ生成JWT令牌的流程为headerjson的base64编码为令牌前面那一部分payloadjson的base64编码为令牌第二部分拼装两部分编码后的json以及secret进行签名的令牌的第因此,这三个部分只需要签名的密钥来验证JWT令牌。如果在消息体中加入用户ID和过期信息,则无需从数据库/缓存中读取信息即可验证token是否有效或过期。因为使用了加密算法,即使修改了两部分(包括过期信息),也无法通过验证。JWT的好处是它不仅可以作为token使用,还可以携带一些必要的信息,免去多次查询的麻烦。注意:JWTtoken的两部分只是base64编码,肉眼不可读,不应该存储敏感信息。JWTtoken的自包含性使得JWT的签名算法无法撤销。你可以自己拟定。为了方便调试,可以在本地环境使用对称加密算法,生产环境推荐使用非对称加密算法。JWTtoken在微服务系统中的优势尤为突出。JWTtoken可以直接在多层调用API中传递,自包含的能力可以减少用户信息查询的次数;更重要的是,使用非对称加密可以通过在系统中分发密钥来验证JWT令牌。当然,OAuth不限制用于访问令牌和其他凭据的技术。OAuth不强制使用JWT。在利用JWT自包含特性的优势时,必须考虑JWT退出的难度。一些对提币要求高的项目不适合使用JWT。即使采用了一些方案(白名单和黑名单),也违背了设计JWT的初衷。Cookie、Cookie中的Token、SessionToken在构建API时仍然会用到。开发者会发现我们的认证方式和web应用有些不同。除了ajax等典型的Web技术之外,如果我们希望API是无状态的,则不推荐使用cookie。使用Cookie的本质是,服务器会在用户第一次访问时分配一个SessionID,客户端会在后续请求中带上这个ID作为当前用户的标志,因为HTTP本身是无状态的,而Cookie是一个内置的-inbrowser中状态的实现方式。如果我们的API用于客户端使用,强制API的调用者管理cookies也可以完成任务。在一些遗留的或非标准的认证实施项目中,我们仍然可以看到这些快速获得认证的做法。使用cookie,比如web项目中的ajax,使用sessionID或者hash作为token,但是把token放在header中,把生成的token(可能是JWT)传入cookie,使用HTTPonly和security标签保护token,选择适当的一种认证方式随着微服务的发展,API的设计不仅仅面向WEB或MobileAPP,还包括BFF(BackendforFrontend)和DomainAPI认证,以及第三方服务的集成。客户端到服务器的身份验证不同于服务器到服务器的身份验证。我们把涉及终端用户(Human)的通信称为人机(H2M),服务器之间的通信称为机器对机器(M2M)。H2M通信对安全性要求更高,而M2M通信自然比H2M更安全,因此更加强调性能,在不同的场合选择合适的认证技术就显得尤为重要。例如,HTTPBasicAuthentication用作H2M认证,看起来有些过时,但在M2M中应用广泛。另外值得一提的是,在H2M的通信方式中,客户端是不受控制的。由于密钥不能独立分发,认证通信的安全性高度依赖于HTTPS。从宏观的角度看他们的关系,对我们的技术选择很有帮助。词汇表浏览器指纹识别通过查询浏览器的代理字符串、屏幕颜色深度、语言等,然后将这些值通过哈希函数传递生成指纹,无需cookies即可识别浏览器MAC(Messageauthenticationcode)。在密码学中,消息认证码是经过特定算法检查某条信息的完整性后生成的一小段信息,利用两种不同的元素相结合来确认用户身份的一种认证方法。是多因素认证OTP(Onetimepassword)一次性密码的特例,比如注册邮箱和短信里的认证码:Thinkworker,转载请联系原作者】点此查看该作者更多好文