REST Security with JAX-RS JavaOne 2013
About Frank Kim SANS Institute Curriculum Lead, Application Security Author, Secure Coding in Java
Outline Authentication Encryption Validation Wrap Up
Authentication Process of establishing and verifying an identity Can be based on three factors Something you know Something you have Something you are
Java EE Authentication Configuration in web.xml 1 <security-constraint> 2 <web-resource-collection> 3 <web-resource-name>Example</web-resource-name> 4 <url-pattern>/*</url-pattern> 5 </web-resource-collection> 6 7 <auth-constraint> 8 <role-name>user</role-name> 9 <role-name>admin</role-name> 10 </auth-constraint> 11 </security-constraint> 12 13 <login-config> 14 <auth-method>FORM</auth-method> 15 <form-login-config> 16 <form-login-page>/login.jsp</form-login-page> 17 <form-error-page>/loginerror.jsp</form-error-page> 18 </form-login-config> 19 </login-config>
JAX-RS SecurityContext getAuthenticationScheme() Returns String authentication scheme used to protect the resource BASIC, FORM, CLIENT_CERT getUserPrincipal() Returns Principal object containing the username isUserInRole(String role) Returns a boolean indicating if the user has the specified logical role
Photo Sharing Site Demo
Photo Sharing Site API http://www.sparklr.com:8080/sparklr2/photos?&format=json { "photos" : [ { "id":"1" , "name":"photo1.jpg" } , { "id":"3" , "name":"photo3.jpg" } , { "id":"5" , "name":"photo5.jpg" }] }
Issues Userid/password authentication is fine If the API is used only by your site But what if your API needs to be used by Other web apps Mobile apps Native apps Do you want these apps to Have your password? Have full access to your account?
OAuth Way to authenticate a service Valet key metaphor coined by Eran Hammer-Lahav Authorization token with limited rights You agree which rights are granted You can revoke rights at any time Can gracefully upgrade rights if needed
OAuth Roles Client User Server - Photo printing service called Tonr - Photo sharing service called Sparklr - Also known as the "resource server" - Person using the app - Also known as the "resource owner"
Simplified OAuth Flow Client User Server - Photo printing service called Tonr 2) Tonr needs pictures to print and redirects you to Sparklr's log in page User Server 1) You log in to Tonr - Photo sharing service called Sparklr 3) You log in to Sparklr directly
Simplified OAuth Flow Client User Server - Photo printing service called Tonr 5) Tonr stores the "access token" with your account User Server 6) You are happy printing and viewing your pictures - Photo sharing service called Sparklr 4) Sparklr returns an OAuth "access token"
Photo Printing Site Demo
Detailed OAuth Flow Via browser: Tonr starts OAuth process Once you click the "Authorize" button http://www.sparklr.com:8080/sparklr2/oauth/authorize? client_id=tonr&redirect_uri=http://www.tonr.com:8080/ tonr2/sparklr/photos& response_type=code& scope=read write&state=92G53T
Detailed OAuth Flow Via browser: Tonr starts OAuth process Once you click the "Authorize" button http://www.sparklr.com:8080/sparklr2/oauth/authorize? client_id=tonr&redirect_uri=http://www.tonr.com:8080/ tonr2/sparklr/photos& response_type=code& scope=read write&state=92G53T The "response_type" parameter can be: "code" for requesting the authorization code grant type "token" for the implicit grant or a registered extension type
Detailed OAuth Flow 2) Via browser: Sparklr redirects back to Tonr http://www.tonr.com:8080/tonr2/sparklr/photos? code=cOuBX6&state=92G53T "code" is the authorization code received from the Server (i.e. Sparklr)
Detailed OAuth Flow 3) Via "Client": Tonr sends OAuth request to Sparklr using client id/password Request: POST /sparklr2/oauth/token HTTP/1.1 Authorization: Basic dG9ucjpzZWNyZXQ= grant_type=authorization_code&code=cOuBX6& redirect_uri=http://www.tonr.com:8080/tonr2/sparklr/photos Response: {"access_token":"5881ce86-3ed0-4427-8a6b-42aef1068dfb", "token_type":"bearer","expires_in":"42528", "scope":"read write"}
Detailed OAuth Flow 3) Via "Client": Tonr sends OAuth request to Sparklr using client id/password Request: POST /sparklr2/oauth/token HTTP/1.1 Authorization: Basic dG9ucjpzZWNyZXQ= grant_type=authorization_code&code=cOuBX6& redirect_uri=http://www.tonr.com:8080/tonr2/sparklr/photos Response: {"access_token":"5881ce86-3ed0-4427-8a6b-42aef1068dfb", "token_type":"bearer","expires_in":"42528", "scope":"read write"} dG9ucjpzZWNyZXQ= is the string tonr:secret
Detailed OAuth Flow 3) Via "Client": Tonr sends OAuth request to Sparklr using client id/password Request: POST /sparklr2/oauth/token HTTP/1.1 Authorization: Basic dG9ucjpzZWNyZXQ= grant_type=authorization_code&code=cOuBX6& redirect_uri=http://www.tonr.com:8080/tonr2/sparklr/photos Response: {"access_token":"5881ce86-3ed0-4427-8a6b-42aef1068dfb", "token_type":"bearer","expires_in":"42528", "scope":"read write"}
Detailed OAuth Flow 3) Via "Client": Tonr sends OAuth request to Sparklr using client id/password Request: POST /sparklr2/oauth/token HTTP/1.1 Authorization: Basic dG9ucjpzZWNyZXQ= grant_type=authorization_code&code=cOuBX6& redirect_uri=http://www.tonr.com:8080/tonr2/sparklr/photos Response: {"access_token":"5881ce86-3ed0-4427-8a6b-42aef1068dfb", "token_type":"bearer","expires_in":"42528", "scope":"read write"} expires_in value in seconds
Detailed OAuth Flow 4) Via "Client": Tonr gets pictures from Sparklr All Requests include: Authorization: Bearer 5881ce86-3ed0-4427-8a6b-42aef1068dfb
When to Use OAuth Use OAuth for consuming APIs from Third-party web apps Mobile apps Native apps Don't need to use OAuth If API is only consumed by the user within the same web app If APIs are only consumed server to server
Benefits No passwords shared between web apps No passwords stored on mobile devices Limits impact of security incidents If Tonr gets hacked Sparklr revokes OAuth access If Sparklr gets hacked you change your Sparklr password but don't have to do anything on Tonr If you lose your mobile device you revoke the access Sparklr gave to the Tonr mobile app
OAuth Versions 1.0 1.0a 2.0 Version Comments - Has a security flaw related to session fixation - Don’t use it 1.0a - Stable and well understood - Uses a signature to exchange credentials and signs every request - Signatures are more of a pain than it seems 2.0 - Spec is final with good support
OAuth 2.0 Authorization Grant Types Description Authorization Code - Optimized for confidential clients - Uses a authorization code from the Server - User doesn't see the access token Implicit Grant - Optimized for script heavy web apps - Does not use an authorization code from the Server - User can see the access token Resource Owner Password Credentials - Use in cases where the User trusts the Client - Exposes User credentials to the Client Client Credentials - Client gets an access token based on Client credentials only
OAuth 2.0 Access Token Types Bearer Large random token Need SSL to protect it in transit Server needs to store it securely hashed like a user password Mac Uses a nonce to prevent replay Does not require SSL OAuth 1.0 only supported a mac type token
Outline Authentication Encryption Validation Wrap Up
Session Hijacking 1) Victim goes to mybank.com via HTTP Internet Public WiFi Network Victim 1) Victim goes to mybank.com via HTTP Attacker
Session Hijacking 2) Attacker sniffs the public wifi network and Internet mybank.com Public WiFi Network Victim 2) Attacker sniffs the public wifi network and steals the JSESSIONID Attacker
Session Hijacking 3) Attacker uses the stolen JSESSIONID Internet mybank.com Public WiFi Network Victim 3) Attacker uses the stolen JSESSIONID to access the victim's session Attacker
Enable SSL in web.xml 1 <security-constraint> 2 <web-resource-collection> 3 <web-resource-name>Example</web-resource-name> 4 <url-pattern>/*</url-pattern> 5 </web-resource-collection> 6 7 ... 8 9 <user-data-constraint> 10 <transport-guarantee> 11 CONFIDENTIAL 12 </transport-guarantee> 13 </user-data-constraint> 14 </security-constraint>
JAX-RS SecurityContext iSecure() Returns a boolean indicating whether the request was made via HTTPS
Secure Flag Ensures that the Cookie is only sent via SSL Configure in web.xml as of Servlet 3.0 <session-config> <cookie-config> <secure>true</secure> </cookie-config> </session-config> Programmatically Cookie cookie = new Cookie("mycookie", "test"); cookie.setSecure(true);
Strict-Transport-Security Tells browser to only talk to the server via HTTPS First time your site accessed via HTTPS and the header is used the browser stores the certificate info Subsequent requests to HTTP automatically use HTTPS Supported browsers Implemented in Firefox and Chrome Defined in RFC 6797 Strict-Transport-Security: max-age=seconds [; includeSubdomains]
Outline Authentication Encryption Validation Wrap Up
Restrict Input Restrict to POST Restrict the Content-Type Use @POST annotation Restrict the Content-Type Use @Consumes({MediaType.APPLICATION_JSON}) Invalid Content-Type results in HTTP 415 Unsupported Media Type Restrict to Ajax if applicable Check X-Requested-With:XMLHttpRequest header Restrict response types Check Accept header for valid response types
Cross-Site Request Forgery (CSRF) Victim browser 1) Victim signs on to mybank 2) Victim visits attacker.com mybank.com attacker.com 3) Page contains CSRF code 4) Browser sends <form action=https://mybank.com/transfer.jsp method=POST> <input name=recipient value=attacker> <input name=amount value=1000> </form> <script>document.forms[0].submit()</script> the request to mybank POST /transfer.jsp HTTP/1.1 Cookie: <mybank authentication cookie> recipient=attacker&amount=1000
CSRF and OAuth 2.0 How can an attacker use CSRF to take over your account? Many sites allow logins from third-party identity providers like Facebook Many identity providers use OAuth Attacker can automatically associate your account with an attacker controlled Facebook account
OAuth CSRF Research Accounts at many sites could be taken over using OAuth CSRF Stack Exchange, woot.com, IMDB, Goodreads, SoundCloud, Pinterest, Groupon, Foursquare, SlideShare, Kickstarter, and others Research by Rich Lundeen http://webstersprodigy.net/2013/05/09/common-oauth-issue-you-can-use-to-take-over-accounts Prior research by Stephen Sclafani http://stephensclafani.com/2011/04/06/oauth-2-0-csrf-vulnerability
OAuth CSRF Attack Flow Create attacker controlled Facebook account Victim is signed on to provider account (i.e. Stack Exchange) Lure victim into visiting an evil site with OAuth CSRF code CSRF code sends OAuth authorization request 4) Attacker's Facebook account now controls victim provider account
Linking Stack Exchange with an Evil Facebook Account Image from http://webstersprodigy.net/2013/05/09/common-oauth-issue-you-can-use-to-take-over-accounts
CSRF Protection Spec defines a "state" parameter that must be included in the redirect to the Client Value must be non-guessable and tied to session Client sends "state" to Server: http://www.sparklr.com:8080/sparklr2/oauth/authorize? client_id=tonr&redirect_uri=http://www.eviltonr.com:8080/ tonr2/sparklr/photos& response_type=code& scope=read write&state=92G53T Server sends "state" back to Client after authorization: http://www.tonr.com:8080/tonr2/sparklr/photos? code=cOuBX6&state=92G53T
OAuth CSRF Protection Demo
OWASP 1-Liner Deliberately vulnerable application More information at Intended for demos and training Created by John Wilander @johnwilander More information at https://www.owasp.org/index.php/OWASP_1-Liner
JSON CSRF Demo
Normal JSON Message {"id":0,"nickName":"John", "oneLiner":"I LOVE Java!", "timestamp":"2013-05-27T17:04:23"}
Forged JSON Message {"id": 0, "nickName": "John", "oneLiner": "I hate Java!", "timestamp": "20111006"}//=dummy
CSRF Attack Form <form id="target" method="POST" action="https://local.1-liner.org:8444/ws/vulnerable/oneliners" enctype="text/plain" style="visibility:hidden"> <input type="text" name='{"id": 0, "nickName": "John", "oneLiner": "I hate Java!", "timestamp": "20111006"}//' value="dummy" /> <input type="submit" value="Go" /> </form>
CSRF Attack Form <form id="target" method="POST" action="https://local.1-liner.org:8444/ws/vulnerable/oneliners" enctype="text/plain" style="visibility:hidden"> <input type="text" name='{"id": 0, "nickName": "John", "oneLiner": "I hate Java!", "timestamp": "20111006"}//' value="dummy" /> <input type="submit" value="Go" /> </form>
Forged JSON Message {"id": 0, "nickName": "John", "oneLiner": "I hate Java!", "timestamp": "20111006"}//=dummy
CSRF Defense Must include something random in the request Use an anti-CSRF token OWASP CSRFGuard Written by Eric Sheridan @eric_sheridan Can inject anti-CSRF token using JSP Tag library - for manual, fine grained protection JavaScript DOM manipulation - for automated protection requiring minimal effort Filter that intercepts requests and validates tokens
CSRFGuard JSP Tags Tags for token name and value <form name="test1" action="protect.html"> <input type="text" name="text" value="text"/> <input type="submit" name="submit" value="submit"/> <input type="hidden" name="<csrf:token-name/>" value="<csrf:token-value/>"/> </form> Tag for name/value pair (delimited with "=") <a href="protect.html?<csrf:token/>">protect.html</a> Convenience tags for forms and links as well <csrf:form> and <csrf:a> Examples from https://www.owasp.org/index.php/CSRFGuard_3_Token_Injection
CSRFGuard DOM Manipulation Include JavaScript in every page that needs CSRF protection <script src="/securish/JavaScriptServlet"></script> JavaScript used to hook the open and send methods XMLHttpRequest.prototype._open = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url, async, user, pass) { // store a copy of the target URL this.url = url; this._open.apply(this, arguments); } XMLHttpRequest.prototype._send = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function(data) { if(this.onsend != null) { // call custom onsend method to modify the request this.onsend.apply(this, arguments); this._send.apply(this, arguments);
Protecting XHR Requests CSRFGuard sends two HTTP headers XMLHttpRequest.prototype.onsend = function(data) { if(isValidUrl(this.url)) { this.setRequestHeader("X-Requested-With", "OWASP CSRFGuard Project") this.setRequestHeader("OWASP_CSRFTOKEN", "EDTF-U8O6-J91L-RZOW-4X09-KEXB-K9B3-4OIV"); } }; If X-Requested-With is passed then CSRFGuard will verify the OWASP_CSRFTOKEN HTTP header. If X-Requested-With does not exist then CSRFGuard will look for the token in HTTP parameters.
JSON CSRF Protection Demo
Outline Authentication Encryption Validation Wrap Up
Summary Authentication Encryption Validation Can use userid/password for services consumed by your app Use OAuth for third-party web apps and mobile apps Encryption Use SSL Use Secure flag Use Strict-Transport-Security header Validation Restrict input Protect your apps against CSRF
Thanks! Frank Kim fkim@sans.org @sansappsec
References JAX-RS 2.0 OAuth 2.0 Specification Spring Security OAuth http://jcp.org/en/jsr/detail?id=339 https://jax-rs-spec.java.net/nonav/2.0/apidocs OAuth 2.0 Specification http://tools.ietf.org/html/rfc6749 http://oauth.net Spring Security OAuth http://www.springsource.org/spring-security-oauth OAuth: The Big Picture http://pages.apigee.com/oauth-big-picture-ebook.html OAuth CSRF issues http://webstersprodigy.net/2013/05/09/common-oauth-issue-you-can-use-to-take-over-accounts http://stephensclafani.com/2011/04/06/oauth-2-0-csrf-vulnerability OWASP 1-Liner https://www.owasp.org/index.php/OWASP_1-Liner CSRFGuard https://www.owasp.org/index.php/Category:OWASP_CSRFGuard_Project http://ericsheridan.blogspot.com/2010/12/how-csrfguard-protects-ajax.html