使用令牌保护 REST Web 服务 (Java)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24411228/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 12:03:48  来源:igfitidea点击:

Securing REST Web Service using token (Java)

javaweb-servicessecurityrestencryption

提问by Teddy

This question is in some way related to the below linked question. However, I need a little more clarity on some aspects and some additional information. Refer: REST Web Service authentication token implementation

这个问题在某种程度上与以下链接的问题有关。但是,我需要对某些方面和一些附加信息进行更清楚的说明。参考: REST Web 服务身份验证令牌实现

Background:

背景:

  • I need to implement security for a REST Web Service using token
  • The webservice is intended for use with Java client. Hence, form authentication and popups for credentials are not useful.
  • I'm new to REST security and encryption
  • 我需要使用令牌为 REST Web 服务实现安全性
  • Web 服务旨在与 Java 客户端一起使用。因此,表单身份验证和凭据弹出窗口没有用。
  • 我是 REST 安全和加密的新手

This is what I have understood till now:

这是我迄今为止所理解的:

For first request:

对于第一个请求:

  1. User establishes https connection (or container ensures https using 301)
  2. User POSTs username and password to login service
  3. If credentials are valid we:
    • Generate a random temporary token
    • Store the random token on server mapping it to actual username
    • Encrypt the token using a symmetric key only known to server
    • Hash the encrypted token
    • Send the encrypted token and the hash to the client
  1. 用户建立 https 连接(或容器使用 301 确保 https)
  2. 用户 POST 用户名和密码登录服务
  3. 如果凭据有效,我们:
    • 生成随机临时令牌
    • 将随机令牌存储在服务器上,将其映射到实际用户名
    • 使用只有服务器知道的对称密钥加密令牌
    • 散列加密的令牌
    • 将加密的令牌和哈希发送给客户端

For subsequent requests:

对于后续请求:

  1. Client sends this encrypted token and hash combination (using username field of basic?)
  2. We make sure the encrypted token is not tampered using the hash and then decrypt it
  3. We check the decrypted token in the session-tracking-table for a not-expired entry and get the actual username (expiry to be managed by code?)
  4. If the username is found, based on allowed roles, allowed operations are configured
  1. 客户端发送这个加密的令牌和哈希组合(使用基本的用户名字段?)
  2. 我们确保加密的令牌不被使用哈希篡改,然后对其进行解密
  3. 我们检查会话跟踪表中解密的令牌是否有未过期的条目并获取实际用户名(过期由代码管理?)
  4. 如果找到用户名,则根据允许的角色,配置允许的操作

More details:

更多细节:

  1. Since client is a java client, the first request can be a POST containing the credentials. However, this looks like it may expose the credentials before the https gets established. Hence should there be a dummy GET to a secured resource so that https is established first?
  2. Assuming above is required, the second request is a LoginAction POST with credentials. This request is handled manually (not using container's authorisation). Is this right?
  3. The above LoginAction returns the user the combination of encrypted token + hash
  4. User sets it to the header that is used by BASIC authentication mechanism (field username)
  5. We implement a JAASRealm to decrypt and validate the token, and find the roles allowed
  6. The rest of authorisation process is taken care of by the container with the WebResourceCollection defined in the web.xml
  1. 由于客户端是 Java 客户端,因此第一个请求可以是包含凭据的 POST。但是,这看起来可能会在 https 建立之前公开凭据。因此,是否应该对安全资源进行虚拟 GET,以便首先建立 https?
  2. 假设上面是必需的,第二个请求是带有凭据的 LoginAction POST。此请求是手动处理的(不使用容器的授权)。这是正确的吗?
  3. 上述LoginAction返回给用户的是加密token+hash的组合
  4. 用户将其设置为 BASIC 身份验证机制使用的标头(字段用户名)
  5. 我们实现了一个 JAASRealm 来解密和验证令牌,并找到允许的角色
  6. 其余的授权过程由容器处理,在 web.xml 中定义了 WebResourceCollection

Is this the correct approach?

这是正确的方法吗?

采纳答案by SilverlightFox

Why not simplify it to the following?

为什么不将其简化为以下内容?

For first request:

对于第一个请求:

  1. User establishes HTTPS connection to server (service does not listen on any other ports) and POSTs credentials to login service.
  2. Server replies with HSTS headerto ensure all further communication is HTTPS.
  3. If credentials are valid we:
    • Generate a random temporary token which is securely generated using a CSPRNG. Make this long enough to be secure (128 bit).
    • Store the random token on server mapping it to actual username.
    • Send the random token to the client
  1. 用户建立到服务器的 HTTPS 连接(服务不侦听任何其他端口)并将凭据发布到登录服务。
  2. 服务器回复HSTS 标头以确保所有进一步的通信都是 HTTPS。
  3. 如果凭据有效,我们:
    • 生成使用CSPRNG安全生成的随机临时令牌。使其足够长以确保安全(128 位)。
    • 将随机令牌存储在服务器上,将其映射到实际用户名。
    • 向客户端发送随机令牌

For subsequent requests:

对于后续请求:

  1. Client sends token in a custom HTTP header over HTTPS.
  2. Token is located in the DB and mapped to the username. If found access is configured based on allowed roles and allowed operations.
  3. If not found user is considered unauthenticated and will have to authenticate with the login service again to get a new token.
  1. 客户端通过 HTTPS 在自定义 HTTP 标头中发送令牌。
  2. 令牌位于数据库中并映射到用户名。如果找到访问权限是根据允许的角色和允许的操作配置的。
  3. 如果未找到,则认为用户未经身份验证,必须再次向登录服务进行身份验证才能获取新令牌。

On the server side the token will be stored with an expiry date. On each access to the service this date will be updated to create a sliding expiration. There will be a job that will run every few minutes to delete expired tokens and the query that checks the token for a valid session will only check those that have not deemed to have expired (to prevent permanent sessions if the scheduled job fails for any reason).

在服务器端,令牌将与到期日期一起存储。每次访问服务时,都会更新此日期以创建滑动到期日。将有一个作业每隔几分钟运行一次以删除过期的令牌,并且检查令牌的有效会话的查询将只检查那些未被视为已过期的令牌(如果计划的作业因任何原因失败,则防止永久会话) )。

There is no need to hash andencrypt the tokens within the database - it adds no real value apart from a touch of security through obscurity. You could just hash though. This would prevent an attacker that managed to get at the session data table from hiHymaning existing user sessions.

不需要对数据库中的令牌进行散列加密——它除了通过隐匿性带来一点安全性之外没有任何实际价值。你可以只是散列。这将防止设法获取会话数据表的攻击者劫持现有用户会话。

回答by Gerard

The approach looks ok. Not very secure. Let me highlight some of the attacks possible with the request.

该方法看起来不错。不是很安全。让我强调一些可能与请求有关的攻击。

  1. Man-In-the-middle attack in a POST request, the user can tamper with the request and server does not have any way to ensure the data is not tampered.

  2. Replay attack: In this, the attacker does not tamper with the request. The attacker taps the request and sends it to the server multiple times in a short duration, though it is a valid request, the server processes the request multiple times, which is not needed Please read about Nonce.

  3. In the first step, the user sends his credentials i.e username and password to the login service and if you have a web based application that also uses the same password it might be dangerous. If in case password in compromised, API and web everything is exposed, please use a different PIN for API access. Also, ensure decrypted token as specified by you, expires after a certain time.

  4. Ensure the service (application server) tomcat. jboss never returns a server page in case of internal error, this gives the attacker extra information of the server where the app is deployed.

  1. POST 请求中的中间人攻击,用户可以篡改请求,服务器无法确保数据不被篡改。

  2. 重放攻击:在这种情况下,攻击者不会篡改请求。攻击者窃听请求,并在短时间内多次向服务器发送,虽然是有效请求,但服务器多次处理该请求,不需要请阅读Nonce。

  3. 在第一步中,用户将他的凭据(即用户名和密码)发送到登录服务,如果您有一个也使用相同密码的基于 Web 的应用程序,这可能很危险。如果密码泄露,API 和 Web 一切都暴露了,请使用不同的 PIN 访问 API。此外,请确保您指定的解密令牌在一定时间后过期。

  4. 确保服务(应用服务器)tomcat。jboss 永远不会在发生内部错误的情况下返回服务器页面,这为攻击者提供了部署应用程序的服务器的额外信息。

-- MODIFIED BASED ON SECOND POST --
Yes your correct if your using mutual SSL, but in case its a one way access you don't have the client certificates. It would be good if you just double ensured everything in the request, just like signed (signature) SOAP, one of the strong data transfer mechanism. But replay attack is a possibility with HTTPS, just handle that. Rest use tokens encryption is good. And why not ask the client to decrypt the token with the password and return the output of the decryption by this you can validate the output, if it is present in your database ? This approach the user does not send the password over the wire even if it is HTTPS ?

-- 基于第二篇文章的修改 --
是的,如果您使用相互 SSL,则是正确的,但如果是单向访问,则您没有客户端证书。如果您只是双重确保请求中的所有内容,就像签名(签名)SOAP 一样,这是一种强大的数据传输机制,那就太好了。但是重放攻击是 HTTPS 的一种可能性,只需处理它。休息使用令牌加密很好。为什么不要求客户端用密码解密令牌并返回解密的输出,如果它存在于您的数据库中,您可以验证输出?这种方法即使是 HTTPS,用户也不会通过网络发送密码?