`
fosa0989
  • 浏览: 110202 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

设计一个不用OAuth的安全的REST接口

 
阅读更多

You want to develop a RESTful web API for developers that is secure to use, but doesn’t require the complexity of OAuth and takes a simple “pass the credentials in the query” approach… or something equally-as-easy for people to use, but it needs to be secure.

You are a smart guy, so you start to think…

Problem

You realize that literally passing the credentials over HTTP leaves that data open to being sniffed in plain-text; After the Gawker incident, you realize that plain-text orweakly-hashed anything is usually a bad idea.

You realize that hashing the password and sending the hash over the wire in lieu of the plain-text password still gives people sniffing at least the username for the account and a hash of the password that could (in a disturbing number of cases) be looked up in a Rainbow Table.

That’s not good, so you scratch your head some more…

Then you realize that a lot of popular public APIs seem to use a combination of two values passed along with each command request: one public value and one (hopefully) private value that only the account owner is suppose to know.

Still not quite right!” you exclaim, because in this case (which is really a username/password scenario all over again) you still suffer from the same problems (sniffed traffic) that sending the username and password in plain text had.

At this point you are about to give up and concede to using OAuth, but you insist that there has to be a secure but relatively easy way to design a public web API that can keep credentials private.

Solution

After doing Peyote for 2 days straight (you should find better ways to relax) it finally dawns on you: Amazon Web Serviceshas one of the largest and most used web APIs online right now, and they don’t support OAuth at all!

After a long afternoon of fever-dreams, you finally come down enough to see how Amazon keeps it’s API requests secure.

You aren’t sure why, but after reading the entire page on how to assemble a request for an AWS service, it still doesn’t make total sense to you. What’s with this “signature” thing? What is the data argument in the code examples?

So you keep searching for articles on “secure API design“…

You come across other people, asking the exact same question and see them getting excellent replies that point at this “HMAC” thing… or something, you aren’t sure yet.

You find other articles that encourage you to use “HMAC” and you are H-FINE using it, if someone would H-EXPLAIN it in plain H-ENGLISH!

You do run across a distillation of the basic concept that makes sense (yay!) and it goes something like this in plain English:

A server and a client know a public and private key; only the server and client know the private key, but everyone can know the public key… who cares what they know.

A client creates a unique HMAC (hash) representing it’s request to the server. It does this by combining the request data (arguments and values or XML/JSON or whatever it was planning on sending) and hashing the blob of request data along with the private key.

The client then sends that HASH to the server, along with all the arguments and values it was going to send anyway.

The server gets the request and re-generates it’s own unique HMAC (hash) based on the submitted values using the same methods the client used.

The server then compares the two HMACs, if they are equal, then the server trusts the client, and runs the request.

That seems pretty straight forward. What was confusing you originally is that you thought the original request was being encrypted and sent, but really all the HMAC method does is create some unique checksum (hash) out of the arguments using a private key that only the client and server know.

Then it sends the checksum along with the original parameters and values to the server, and then the server double-checks the checksum (hash) to make sure it agrees with what the client sent.

Since, hypothetically, only the client and server know the private key, we assume that if their hashes match, then they can both trust each, so the server then processes the request normally.

You realize that in real-life, this is basically like someone coming up to you and saying: “Jimmy told me to tell you to give the money to Johnny Two-toes“, but you have no idea who this guy is, so you hold out your hand and test him to see if he knows the secret handshake.

If he does, then he must be part of your gang and you do what he says… if he doesn’t know the secret handshake, you decide to shoot him in the face (you have anger issues).

You sort of get it, but then you wonder: “What is the best way to combine all the parameters and values together when creating the giant blob?” and luckily the guy behind tarsnap has your back and explains to you how Amazon screwed this up with Signature Version 1.

Now you re-read how Amazon Web Services does authentication and it makes sense, it goes something like:

  1. [CLIENT] Before making the REST API call, combine a bunch of unique data together (this is typically all the parameters and values you intend on sending, it is the “data” argument in the code snippets on AWS’s site)
  2. [CLIENT] Hash (HMAC-SHA1 or SHA256 preferably) the blob of data data (from Step #1) with your private key assigned to you by the system.
  3. [CLIENT] Send the server the following data:
    1. Some user-identifiable information like an “API Key”, client ID, user ID or something else it can use to identify who you are. This is the public API key, never the private API key. This is a public value that anyone (even evil masterminds can know and you don’t mind). It is just a way for the system to know WHO is sending the request, not if it should trust the sender or not (it will figure that out based on the HMAC).
    2. Send the HMAC (hash) you generated.
    3. Send all the data (parameters and values) you were planning on sending anyway. Probably unencrypted if they are harmless values, like “mode=start&number=4&order=desc” or other operating nonsense. If the values are private, you’ll need to encrypt them.
  4. (OPTIONALThe only way to protect against “replay attacks” on your API is to include a timestamp of time kind along with the request so the server can decide if this is an “old” request, and deny it. The timestamp must be included into the HMAC generation (effectively stamping a created-on time on the hash) in addition to being checked “within acceptable bounds” on the server.
  5. [SERVER] Receive all the data from the client.
  6. [SERVER] (see OPTIONAL) Compare the current server’s timestamp to the timestamp the client sent. Make sure the difference between the two timestamps it within an acceptable time limit (5-15mins maybe) to hinder replay attacks.
    1. NOTEBe sure to compare the same timezones and watch out for issues that popup with daylight savings time change-overs.
    2. UPDATEAs correctly pointed out by a few folks, just use UTC time and forget about the DST issues.
  7. [SERVER] Using the user-identifying data sent along with the request (e.g. API Key) look the user up in the DB and load their private key.
  8. [SERVER] Re-combine the same data together that the client did in the same way the client did it. Then hash (generate HMAC) that data blob using the private key you looked up from the DB.
    1. (see OPTIONAL) If you are protecting against replay attacks, include the timestamp from the client in the HMAC re-calculation on the server. Since you already determined this timestamp was within acceptable bounds to be accepted, you have to re-apply it to the hash calculation to make sure it was the same timestamp sent from the client originally, and not a made-up timestamp from a man-in-the-middle attack.
  9. [SERVER] Run that mess of data through the HMAC hash, exactly like you did on the client.
  10. [SERVER] Compare the hash you just got on the server, with the hash the client sent you; if they match, then the client is considered legit, so process the command. Otherwise reject the command!

REMINDERBe consistent and careful with how you combine all parameters and values together. Don’t do what Amazon did with Auth Signature version 1 and open yourself up to hash-collisions! (Suggestion: just hash the whole URL-encoded query string!)

SUPER-REMINDERYour private key should never be transferred over the wire, it is just used to generate the HMAC, the server looks the private key back up itself and recalculates it’s own HMAC. The public key is the only key that goes across the wire to identify the user making the call; it is OK if a nefarious evil-doer gets that value, because it doesn’t imply his messages will be trusted. They still have to be hashed with the private key and hashed in the same manner both the client and server are using (e.g. prefix, postfix, multiple times, etc.)

Update 10/13/11: Chris correctly pointed out that if you don’t include the URI or HTTP method in your HMAC calculation, it leaves you open to more hard-to-track man-in-the-middle attacks where an attacker could modify the endpoint you are operating on as well as the HTTP method… for example change an HTTP POST to /issue/create to /user/delete. Great catch Chris!

Denoument

It’s been a long few days, but you finally figured out a secure API design and you are proud of yourself. You are super-extra proud of yourself because the security method outlined above actually protects against another commonly popular way of hacking API access: side-jacking.

Session sidejacking is where a man-in-the-middle sniffs network traffic and doesn’t steal your credentials, but rather steals the temporary Session ID the API has given you to authenticate your actions with the API for a temporary period of time (e.g. 1hr). With the method above, because the individual methods themselves are checksumed, there is no Session ID to steal and re-use by a nefarious middle man.

You rock.

You also slowly realize and accept that at some point you will have to implement OAuth, but it will probably be OAuth 2.0 support and that isn’t quite ready yet.

I am relatively new to the RESTful API game, focusing primarily on client-side libraries. If I missed something please point it out and I’ll fix it right up. If you have questions, suggestions or ideas that you think should go into the story above, please leave a comment below.

Alternatively you can email me and we can talk about friendship, life and canoeing.

Gotchas (Problems to Watch For)

<This section was removed, because by using UTC time you avoid the daylight-savings-time issue all together and my solution proposed here was stupid anyway.>

Additional Thoughts for APIs

What about the scenario where you are writing a public-facing API like Twitter, where you might have a mobile app deployed on thousands of phones and you have your public and private keys embedded in the app?

On a rooted device, those users could likely decompile your app and pull your private key out, doesn’t that leave the private key open to being compromised?

Yes, yes it does.

So what’s the solution?

Taking a hint from Twitter, it looks like to some degree you cannot avoid this. Your app needs to have it’s private key (they call it a secret key) and that means you are open to getting your private key compromised.

What you can do though is to issue private keys on a per-application-basis, instead of on a per-user-account basis. That way if the private key is compromised, that version of the application can be banned from your API until new private keys are generated, put into an updated version of the app and re-released.

What if the new set of keys get compromised again?

Well yes, that is very possible. You would have to combat this in some way on your own, like encrypting the keys with another private key… or praying to god people will stop hacking your software.

Regardless, you would have to come up with some 2nd layer of security to protect that new private key, but at least there is a way to get the apps deployed in the wild working again (new version) instead of the root account being locked and NONE of the apps being able to access the service again.

Update #1: There are some fantastic feedback and ideas on securing a web-API down in the comments, I would highly recommend reading them.

Some highlights are:

Update #2: I have since looked at “2-legged OAuth” and it is, as a few readers pointed out, almost exactly the process described above. The advantage being that if you write your API to this spec, there are plenty of OAuth client libraries available for implementors to use.

The only OAuth-specific things of note being:

  • OAuth spec is super-specific with how you need to encode your pararms, order them and then combine them all together when forming the HMAC (called the “method signature” in OAuth)
  • OAuth, when using HMAC-SHA1 encoding, requires that you send along a nonce. The server or “provider” must keep the nonce value along with the timestamp associated with the request that used that nonce on-record to verify that no other requests come in with the SAME nonce and timestamp (indicating a “replay” attempt). Naturally you can expire these values from your data store eventually, but it would probably be a good idea to keep them on-file for a while.
    • The nonce doesn’t need to be a secret. It is just a way to associate some unique token to a particular timestamp; the combination of the two are like a thumbprint saying “at 12:22pm a request with a nonce token of HdjS872djas83 was received”. And since the nonce and timestamp are included in the HMAC hash calculation, no nefarious middle-man can ever try and “replay” that previous message AND successfully hash his request to match yours without the server seeing the same timestamp + nonce combination come back in; at which point it would say “Hey! A request with this thumbprint showed up two hours ago, what are you trying to do?!”
  • Instead of passing all this as GET params, all these values get jammed into one giant “Authorization” HTTP header and coma-separated.

That is pretty much the high points of 2-legged OAuth. The HMAC generation using the entire request and all the params is still there, sending along the timestamp and a nonce is still there and sending along the original request args are all still there.

When I finally get around to implementing 2-legged OAuth from a server perspective, I’ll write up another article on it.

分享到:
评论

相关推荐

    基于tp5的restfulapi风格接口oauth20接口版本管理

    综上所述,这个项目涉及了使用ThinkPHP5框架实现RESTful API接口,结合OAuth2.0进行安全授权,以及接口版本管理的最佳实践,对于想要学习和实践PHP Web服务开发的人员来说,是一个有价值的参考案例。通过深入理解...

    C# 实现Rest服务接口,含实现文档

    - 使用ASP.NET MVC或ASP.NET Web API框架,定义控制器类和操作方法,对应REST接口。 - 创建路由配置,将URL映射到相应的处理方法。 - 操作方法返回类型可以是JSON(JavaScript Object Notation),用于传输轻量级...

    接口概要设计说.doc

    通过以上描述,我们可以看出"接口概要设计说"文档将涵盖这些核心概念,为整个项目提供一个清晰的接口设计蓝图,确保系统间有效且高效地协同工作。在实际开发中,设计师需要根据具体项目需求和环境来调整和优化这些...

    简易rest接口调试工具

    Rest接口调试工具是一种用于测试和验证RESTful Web服务的实用程序。REST(Representational State Transfer)是一种网络应用程序的设计风格和开发方式,基于HTTP协议,强调简洁、无状态和可缓存性,广泛应用于Web...

    H3C CAS REST API接口

    H3C CAS的REST API设计简洁,易于理解和使用,使得开发者可以使用常见的编程语言如Python、Java、JavaScript等来调用这些接口,实现自动化管理任务。 在使用H3C CAS REST API时,首先需要了解API的基本结构和请求...

    rest api 接口测试工具

    Advanced REST Client是Google Chrome浏览器的一个扩展,它是一个强大的REST API测试工具。版本v3.1.9提供了以下功能: 1. 支持所有HTTP方法:包括基本的GET、POST、PUT、DELETE以及HEAD、OPTIONS、PATCH等。 2. ...

    PyPI 官网下载 | django_rest_framework_oauth-0.0.1.tar.gz

    总的来说,`django_rest_framework_oauth`是一个帮助Python开发者利用Django构建支持OAuth 2.0的RESTful API的工具,它的使用可以提升Web服务的安全性和可扩展性。为了使用这个库,开发者需要熟悉Django框架、...

    spring-security-oauth-1.0.2.RELEASE.zip

    【描述】中的"crowd4s.zip"是一个Scala库,专门设计用来包装Atlassian Crowd的REST API。Crowd是一款身份管理和访问控制服务器,它可以集中管理用户、组和权限。Crowd4s使Scala开发者能够更方便地与Crowd集成,进行...

    rest的所有jar

    8. **安全相关库**:如OAuth2、JWT(JSON Web Tokens)等,用于认证和授权,保护REST接口的安全。 9. **日志库**:如Log4j、SLF4J等,帮助记录和分析接口的运行情况。 10. **测试库**:JUnit、Mockito等,用于编写...

    api接口设计.rar

    接口的安全性也是一个重要考虑因素。常见的安全措施包括OAuth2.0授权框架、JWT(JSON Web Tokens)以及HTTPS协议来确保数据传输的安全性。 错误处理和文档化也是API设计的关键部分。良好的错误响应应该提供明确的...

    rest-interface.rar_quantum computing_rest_rest interface

    5. **认证和授权(Authentication and Authorization)**: 为了确保安全,REST接口通常会包含身份验证和权限控制机制,比如OAuth2,以限制对量子计算资源的访问。 **REST接口的优势** - **简单易用**: REST接口...

    REST API教程.zip_zip压缩包

    REST(Representational State Transfer,表述性状态转移)是一种软件架构风格,主要用于Web服务的设计,以实现简洁、高效、可扩展的接口。REST API是遵循REST原则的API设计规范,广泛应用于现代Web应用和云服务中,...

    OAuth协议 介绍

     OAuth不强求一个特定的用户接口或操作模式,也不限定服务提供方如何验证用户,特别适合认证证书对消费方不可用的情况,例如OpenID。 OAuth致力于为托管web服务认证提供统一的体验和实现,形成一个社区驱动的协议...

    odoo rest api

    Odoo的REST API是其强大的开放性的一个体现,使得开发者能够轻松集成其他系统并与Odoo进行数据交换。通过理解并熟练运用这些API,可以极大地扩展Odoo的功能,并构建出符合企业需求的定制化解决方案。对于那些想要...

    Python-Susanoo一个RESTAPI的安全测试框架

    Python-Susanoo是一个专门针对RESTful API进行安全测试的框架,它为开发者和安全测试人员提供了方便、高效的方式来检测API接口可能存在的安全漏洞。在现代Web应用中,REST API已经成为数据交互的重要方式,因此,...

    api_rest_oauth2:使用OAuth2和NodeJS开发REST API

    在IT行业中,API(应用程序接口)是软件系统之间交互的核心,而REST(Representational State Transfer,表述性状态转移)是一种广泛采用的API设计风格。OAuth2则是一种授权框架,允许第三方应用安全地访问用户的...

    rest转换

    在"fmsserver"这个上下文中,我们可以推测这可能是一个服务器应用或者服务,可能涉及到将传统的文件管理或服务器接口转换为REST API,以便客户端能够更方便、高效地进行操作。下面我们将详细探讨REST转换的相关知识...

    C# WEB API 安全认证源码 REST

    9. **REST原则**:REST(Representational State Transfer)是网络应用程序设计的一个架构风格,强调简洁和标准化的接口。Web API设计应遵循REST原则,如使用HTTP方法(GET、POST、PUT、DELETE)来表示操作,以及...

    geoserver通过rest自动发布postigs图层

    REST(Representational State Transfer)是一种网络应用程序的设计风格和开发方式,常用于API接口的构建。本话题将探讨如何利用GeoServer的REST API自动化地发布存储在PostGIS数据库中的地理数据图层。 【知识点...

    springmvc+rest+json交互+接口

    6. **测试**:使用Postman或者JUnit测试你的REST接口。发送不同类型的HTTP请求,并检查返回的JSON数据是否符合预期。 总的来说,Spring MVC结合REST和JSON,使得开发健壮、可扩展的Web服务变得简单而直观。理解这些...

Global site tag (gtag.js) - Google Analytics