Why is your API insecure?
0 × 00 background
Some time ago, I reported to Spree Commerce about the JSONP + CSRF vulnerability in all its API paths. Similarly, the Instagram API has the CSRF vulnerability. The APIs of Disqus, Stripe, and Shopify leak private information through JSONP. The root cause of all these problems is the lack of rational use of hybrid API authentication.
I hope all API developers can take a look at this article. I will explain the basics of API authentication and the best practices in the industry.
0 × 01 detailed process
First, your API passes api_key authentication:
def load_user @current_api_user = Spree.user_class.find_by(spree_api_key: api_key.to_s)end
At this time, someone asks you to enable CORS (Cross-Origin Resource Sharing) because they want to call your API through JS:
config.middleware.insert_before 0, "Rack::Cors" do allow do origins '*' resource '*', :headers => :any, :methods => [:get, :post, :options] endend
Apparently, your APIController contains skip_before_action: verify_authenticity_token. So why do you still need CSRF verification for API requests from Android apps?
Another developer hopes that you can add support for JSONP (JSON with Padding) because earlier Browsers Do not support CORS. Of course you think this is okay:
after_filter :set_jsonp_formatdef set_jsonp_format if params[:callback] && request.get? self.response_body = "#{params[:callback]}(#{response.body})" headers["Content-Type"] = 'application/javascript' endend
At present, everything seems to be okay. Finally, your developers decide to follow the trend of Backend-As-API and use your api.example.com on the client. There are two options:
1. manually add api_token
For example, each API request header of Soundcloud uses Authorization: OAuth 1-16343-15233329-796b6b695d2c7c1 and Foursquare uses oauth_token = YXIAC4Y254HGZBNPQW6S0UFBGGSU57RBP.
Disadvantages:
1. XSS. OAuth tokens can be accessed through JS, which allows attackers to leak victim creden. You can use HttpOnly flag to prevent such events. However, OAuth tokens does not take such preventive actions.
2. Each request has an OPTIONS request, which increases the potential risk.
Although many people use this method, I do not recommend it.
Ii. User Authentication through cookie
As a result, the solution is simple:
@current_api_user = (try_spree_current_user || Spree.user_class.find_by(spree_api_key: api_key.to_s))
Try_spree_current_user parses _ spree_session cookie and returns user_id to User. find (session [: user_id]). So what are the problems with this approach?
Like Authorization, cookies are encapsulated in the header, but even experienced developers cannot really understand cookies. I call it "sticky credentials" because they are automatically added, even requests from third-party domains (such as evil.com ).
Because most web developers do not understand this concept, CSRF becomes the most common security issue in the world. This is also why all cookie-based authentication requires the use of additional csrf_token nonce for dual authentication. This nonce allows you to determine the request comes from your domain name.
1. Because CSRF protection is missing in your API request, all your API paths are at risk of request forgery. Example of Spree admin Password Change (http://securecanvas.com/csrf.html# {"url": "https://majestic-stall-2602.spree.mx/api/users/1", "autosubmit": false, "target": "_ top", "data ": "utf8 = % E2 % 9C % 93 & _ method = put & user % 5 Bemail % 5D = spree % 40example. com1 & user % 5Bspree_role_ids % 5D % 5B % 5D = & user % 5 Bpassword % 5D = 123123123 & user % 5Bpassword_confirmation % 5D = 123123123 & button = & sbmbtn = &", "method": "POST "}).
2. JSONP cross-site leakage GET response:
<script src="http://api.example.com/orders.json?callback=leakMe"></script>
3. CORS is even less reliable, and every request will leak information.
0 × 02 Solution
So how is this done? Hybrid API authentication:
@current_api_user = unless api_key.to_s.empty? Spree.user_class.find_by(spree_api_key: api_key.to_s) # Good to go!else # Everyone stand back, we are using cookies! # 1) verify CSRF token for all non-GET requests # 2) drop JSONP support # 3) drop CORS support try_spree_current_userend
This hybrid method allows front-end (JS/HTML app) and third-party apps to use your api.example.com, so that your creden are not subject to XSS (HttpOnly) problems, there will be no unnecessary OPTIONS requests. The above is the best method in the industry.