The thin line between RESTful and AWful 
Jakub Kubrynski 
jk@devskiller.com / @jkubrynski 1 / 48
jk@devskiller.com / @jkubrynski 2 / 48
"The Code is more what you'd call guidelines than actual rules. Welcome 
aboard the Black Pearl, Miss Turner" 
-- Cpt. Hector Barbossa to Elizabeth Swann 
RT Ben Hale 
jk@devskiller.com / @jkubrynski 3 / 48
Formal REST constraints 
Client-Server 
Stateless 
Cache 
Interface / Uniform Contract 
Layered System 
jk@devskiller.com / @jkubrynski 4 / 48
Richardson maturity model 
http://martinfowler.com/articles/richardsonMaturityModel.html 
jk@devskiller.com / @jkubrynski 5 / 48
POST vs PUT 
jk@devskiller.com / @jkubrynski 6 / 48
POST vs PUT 
POST creates new resources 
jk@devskiller.com / @jkubrynski 7 / 48
POST vs PUT 
POST creates new resources 
PUT updates existing resources 
PUT can create resource if ID is already known 
jk@devskiller.com / @jkubrynski 8 / 48
Maybe PATCH? 
no "out of the box" support 
jk@devskiller.com / @jkubrynski 9 / 48
Maybe PATCH? 
no "out of the box" support 
partial update 
@RequestMapping(value = "/{id}", method = PATCH) 
public void updateArticle(HttpServletRequest request, @PathVariable("id") String id) { 
Article currentArticle = repository.findOne(id); 
Article updatedArticle = objectMapper.readerForUpdating(currentArticle) 
.readValue(request.getReader()); 
repository.save(updatedArticle); 
} 
jk@devskiller.com / @jkubrynski 10 / 48
Caching 
be aware - especially IE caches aggressively 
jk@devskiller.com / @jkubrynski 11 / 48
Caching 
be aware - especially IE caches aggressively 
disable caching 
@Configuration 
public class RestConfig extends WebMvcConfigurerAdapter { 
@Override 
public void addInterceptors(InterceptorRegistry registry) { 
WebContentInterceptor webContentInterceptor = new WebContentInterceptor(); 
webContentInterceptor.setCacheSeconds(0); 
registry.addInterceptor(webContentInterceptor); 
} 
} 
jk@devskiller.com / @jkubrynski 12 / 48
Cache headers 
cache-control: public, max-age=0, no-cache 
public / private 
no-cache 
no-store 
max-age 
s-maxage 
jk@devskiller.com / @jkubrynski 13 / 48
Cache headers 
cache-control: public, max-age=0, no-cache 
public / private 
no-cache 
no-store 
max-age 
s-maxage 
ETag 
If-None-Match: "0d41d8cd98f00b204e9800998ecf8427e" 
Spring brings ShallowEtagHeaderFilter 
jk@devskiller.com / @jkubrynski 14 / 48
Compression 
reduces response size dramatically 
in Tomcat extend Connector with 
compression="on" 
compressionMinSize="2048" 
noCompressionUserAgents="gozilla, traviata" 
compressableMimeType="text/html,text/xml" 
jk@devskiller.com / @jkubrynski 15 / 48
HATEOAS 
self-descriptive 
client understands hypermedia 
{ 
"name": "Alice", 
"links": [ 
{ "rel": "self", "href": "/customers/1213" }, 
{ "rel": "parent", "href": "/customers/14" }, 
{ "rel": "currentOrder", "href": "/orders/14312" } 
] 
} 
HTTP/1.1 201 Created 
Location: http://api.mydomain.com/orders/1234 
jk@devskiller.com / @jkubrynski 16 / 48
HATEOAS in Spring 
public class Customer extends ResourceSupport { ... } 
// or wrap entity into Resource object 
jk@devskiller.com / @jkubrynski 17 / 48
HATEOAS in Spring 
public class Customer extends ResourceSupport { ... } 
// or wrap entity into Resource object 
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*; 
public HttpEntity<Customer> get(@PathVariable("id") String customerId) { 
Customer customer = repository.findOne(customerId); 
String pId = customer.getBoss(); 
String oId = customer.currentOrderId(); 
customer.add(linkTo(methodOn(CustomerController.class).get(customerId)).withSelfRel()); 
customer.add(linkTo(methodOn(CustomerController.class).get(pId)).withRel("parent")); 
customer.add(linkTo(methodOn(OrderController.class).get(oId)).withRel("currentOrder")); 
return new ResponseEntity<Customer>(customer, HttpStatus.OK); 
} 
public ResponseEntity create(@RequestBody Customer customer) { 
String id = repository.save(customer); 
return ResponseEntity.created(linkTo(CustomerController.class).slash(id).toUri()) 
.build(); 
} 
jk@devskiller.com / @jkubrynski 18 / 48
@DanaDanger HTTP codes classification 
20x: cool 
30x: ask that dude over there 
40x: you fucked up 
50x: we fucked up 
jk@devskiller.com / @jkubrynski 19 / 48
Exceptions 
include detailed information 
{ 
"status": 400, 
"code": 40483, 
"message": "Incorrect body signature", 
"moreInfo": "http://www.mycompany.com/errors/40483" 
} 
jk@devskiller.com / @jkubrynski 20 / 48
Exceptions 
include detailed information 
{ 
"status": 400, 
"code": 40483, 
"message": "Incorrect body signature", 
"moreInfo": "http://www.mycompany.com/errors/40483" 
} 
hide stacktrace 
jk@devskiller.com / @jkubrynski 21 / 48
Handling Spring MVC exceptions 
@ControllerAdvice 
public class MyExceptionHandler extends ResponseEntityExceptionHandler { 
/* Handling framework exceptions */ 
@Override 
protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, 
HttpHeaders headers, HttpStatus status, WebRequest request) { 
LOG.error("Spring MVC exception occurred", ex); 
return super.handleExceptionInternal(ex, body, headers, status, request); 
} 
/* Handling application exceptions */ 
@ResponseStatus(value = HttpStatus.NOT_FOUND) 
@ExceptionHandler(ResourceNotFoundException.class) 
public void handleResourceNotFound() { 
} 
} 
jk@devskiller.com / @jkubrynski 22 / 48
API Versioning 
don't even think about 
api.domain.com/v2/orders 
URIs to the same resources should be fixed between 
versions 
jk@devskiller.com / @jkubrynski 23 / 48
API Versioning 
don't even think about 
api.domain.com/v2/orders 
URIs to the same resources should be fixed between 
versions 
use Content-Type 
1 version: application/vnd.domain+json 
2 version: application/vnd.domain.v2+json 
jk@devskiller.com / @jkubrynski 24 / 48
Filtering and sorting 
GET /reviews?rating=5 
GET /reviews?rating=5&sortAsc=author 
jk@devskiller.com / @jkubrynski 25 / 48
Filtering and sorting 
GET /reviews?rating=5 
GET /reviews?rating=5&sortAsc=author 
Dynamic queries are easier in POST body 
jk@devskiller.com / @jkubrynski 26 / 48
Filtering and sorting 
GET /reviews?rating=5 
GET /reviews?rating=5&sortAsc=author 
Dynamic queries are easier in POST body 
POST /reviews/searches 
GET /reviews/searches/23?page=2 
jk@devskiller.com / @jkubrynski 27 / 48
Documentation 
runnable with examples 
Swagger 
jk@devskiller.com / @jkubrynski 28 / 48
jk@devskiller.com / @jkubrynski 29 / 48
Stateless or not? 
password hashing cost 
session replication 
load-balancing 
jk@devskiller.com / @jkubrynski 30 / 48
Stateless or not? 
password hashing cost 
session replication 
load-balancing 
... 
stateless session? 
jk@devskiller.com / @jkubrynski 31 / 48
Avoiding session creation in Spring 
@EnableWebSecurity 
public class SpringSecurity extends WebSecurityConfigurerAdapter { 
@Override 
protected void configure(HttpSecurity http) throws Exception { 
http 
.csrf().disable() 
.authorizeRequests() 
.antMatchers("/secure/**").fullyAuthenticated() 
.and() 
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER) 
.and() 
.httpBasic(); 
} 
} 
jk@devskiller.com / @jkubrynski 32 / 48
Security 
SQL Injection 
XSS 
CSRF 
XXE 
jk@devskiller.com / @jkubrynski 33 / 48
HQL Injection 
List<Product> products = em.createQuery( 
"SELECT p FROM Product p where p.category = '" + categ + "'", Product.class) 
.getResultList(); 
jk@devskiller.com / @jkubrynski 34 / 48
HQL Injection 
List<Product> products = em.createQuery( 
"SELECT p FROM Product p where p.category = '" + categ + "'", Product.class) 
.getResultList(); 
categ = ' OR '1'='1 
jk@devskiller.com / @jkubrynski 35 / 48
HQL Injection 
List<Product> products = em.createQuery( 
"SELECT p FROM Product p where p.category = '" + categ + "'", Product.class) 
.getResultList(); 
categ = ' OR '1'='1 
SELECT __fields__ FROM products WHERE category = '' OR '1'='1' 
jk@devskiller.com / @jkubrynski 36 / 48
HQL Injection 
List<Product> products = em.createQuery( 
"SELECT p FROM Product p where p.category = '" + categ + "'", Product.class) 
.getResultList(); 
categ = ' OR '1'='1 
SELECT __fields__ FROM products WHERE category = '' OR '1'='1' 
List<Product> products = em.createQuery( 
"SELECT p FROM Product p where p.category = :categ", Product.class) 
.setParameter("categ", categ) 
.getResultList(); 
jk@devskiller.com / @jkubrynski 37 / 48
HQL Injection 
List<Product> products = em.createQuery( 
"SELECT p FROM Product p where p.category = '" + categ + "'", Product.class) 
.getResultList(); 
categ = ' OR '1'='1 
SELECT __fields__ FROM products WHERE category = '' OR '1'='1' 
List<Product> products = em.createQuery( 
"SELECT p FROM Product p where p.category = :categ", Product.class) 
.setParameter("categ", categ) 
.getResultList(); 
SELECT __fields__ FROM products WHERE category = ' OR ''1''=''1''' 
jk@devskiller.com / @jkubrynski 38 / 48
CSRF - Cross-site request forgery 
<img src="https://api.mybank.com/transfers/from/1233/to/1234/amount/5000"> 
<form action="https://api.mybank.com/transfers" method="POST"> 
<input type="hidden" name="from" value="1233"/> 
<input type="hidden" name="to" value="1234"/> 
<input type="hidden" name=amount" value="5000"/> 
<input type="submit" value="Celebrity Nude Photos!"/> 
</form> 
jk@devskiller.com / @jkubrynski 39 / 48
CSRF - Cross-site request forgery 
<img src="https://api.mybank.com/transfers/from/1233/to/1234/amount/5000"> 
<form action="https://api.mybank.com/transfers" method="POST"> 
<input type="hidden" name="from" value="1233"/> 
<input type="hidden" name="to" value="1234"/> 
<input type="hidden" name=amount" value="5000"/> 
<input type="submit" value="Celebrity Nude Photos!"/> 
</form> 
One time request tokens 
Correct CORS headers 
jk@devskiller.com / @jkubrynski 40 / 48
CORS - Cross Origin Requests Sharing 
Preflight request 
OPTIONS /cors HTTP/1.1 
Origin: http://www.domain.com 
Access-Control-Request-Method: PUT 
Access-Control-Request-Headers: X-Custom-Header 
Host: api.mydomain.org 
Accept-Language: en-US 
Connection: keep-alive 
User-Agent: Mozilla/5.0... 
Preflight response 
Access-Control-Allow-Origin: http://www.domain.com 
Access-Control-Allow-Methods: GET, POST, PUT 
Access-Control-Allow-Headers: X-Custom-Header 
Content-Type: text/html; charset=utf-8 
jk@devskiller.com / @jkubrynski 41 / 48
XML External Entity 
<?xml version="1.0" encoding="utf-8"?> 
<comment> 
<text>Yeah! I like it!</text> 
</comment> 
jk@devskiller.com / @jkubrynski 42 / 48
XML External Entity 
<?xml version="1.0" encoding="utf-8"?> 
<comment> 
<text>Yeah! I like it!</text> 
</comment> 
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE myentity [ <!ENTITY a "Yeah! I like it!"> ]> 
<comment> 
<text>&a;</text> 
</comment> 
jk@devskiller.com / @jkubrynski 43 / 48
XML External Entity 
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE myentity [ <!ENTITY a SYSTEM "/etc/passwd"> ]> 
<comment> 
<text>&a;</text> 
</comment> 
jk@devskiller.com / @jkubrynski 44 / 48
XML External Entity 
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE myentity [ <!ENTITY a SYSTEM "/etc/passwd"> ]> 
<comment> 
<text>&a;</text> 
</comment> 
<?xml version="1.0" encoding="utf-8"?> 
<comment> 
<text>root:x:0:0:root:/root:/bin/bash 
bin:x:1:1:bin:/bin:/sbin/nologin 
daemon:x:2:2:daemon:/sbin:/sbin/nologin 
adm:x:3:4:adm:/var/adm:/sbin/nologin 
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin 
sync:x:5:0:sync:/sbin:/bin/sync 
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown 
halt:x:7:0:halt:/sbin:/sbin/halt 
..... 
</text> 
</comment> 
jk@devskiller.com / @jkubrynski 45 / 48
XML External Entity 
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE myentity [ 
<!ENTITY a "abcdefghij1234567890" > 
<!ENTITY b "&a;&a;&a;&a;&a;&a;&a;&a;&a;&a" > 
<!ENTITY c "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;" > 
<!ENTITY d "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;" > 
... 
<!ENTITY h "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;" > 
]> 
<comment> 
<text>&h;</text> 
</comment> 
jk@devskiller.com / @jkubrynski 46 / 48
http://knowyourmeme.com/photos/531557 thx to @mihn 
jk@devskiller.com / @jkubrynski 47 / 48
Thanks! 
jk@devskiller.com / @jkubrynski 48 / 48

What you won't read in books about RESTful services

  • 1.
    The thin linebetween RESTful and AWful Jakub Kubrynski jk@devskiller.com / @jkubrynski 1 / 48
  • 2.
  • 3.
    "The Code ismore what you'd call guidelines than actual rules. Welcome aboard the Black Pearl, Miss Turner" -- Cpt. Hector Barbossa to Elizabeth Swann RT Ben Hale jk@devskiller.com / @jkubrynski 3 / 48
  • 4.
    Formal REST constraints Client-Server Stateless Cache Interface / Uniform Contract Layered System jk@devskiller.com / @jkubrynski 4 / 48
  • 5.
    Richardson maturity model http://martinfowler.com/articles/richardsonMaturityModel.html jk@devskiller.com / @jkubrynski 5 / 48
  • 6.
    POST vs PUT jk@devskiller.com / @jkubrynski 6 / 48
  • 7.
    POST vs PUT POST creates new resources jk@devskiller.com / @jkubrynski 7 / 48
  • 8.
    POST vs PUT POST creates new resources PUT updates existing resources PUT can create resource if ID is already known jk@devskiller.com / @jkubrynski 8 / 48
  • 9.
    Maybe PATCH? no"out of the box" support jk@devskiller.com / @jkubrynski 9 / 48
  • 10.
    Maybe PATCH? no"out of the box" support partial update @RequestMapping(value = "/{id}", method = PATCH) public void updateArticle(HttpServletRequest request, @PathVariable("id") String id) { Article currentArticle = repository.findOne(id); Article updatedArticle = objectMapper.readerForUpdating(currentArticle) .readValue(request.getReader()); repository.save(updatedArticle); } jk@devskiller.com / @jkubrynski 10 / 48
  • 11.
    Caching be aware- especially IE caches aggressively jk@devskiller.com / @jkubrynski 11 / 48
  • 12.
    Caching be aware- especially IE caches aggressively disable caching @Configuration public class RestConfig extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { WebContentInterceptor webContentInterceptor = new WebContentInterceptor(); webContentInterceptor.setCacheSeconds(0); registry.addInterceptor(webContentInterceptor); } } jk@devskiller.com / @jkubrynski 12 / 48
  • 13.
    Cache headers cache-control:public, max-age=0, no-cache public / private no-cache no-store max-age s-maxage jk@devskiller.com / @jkubrynski 13 / 48
  • 14.
    Cache headers cache-control:public, max-age=0, no-cache public / private no-cache no-store max-age s-maxage ETag If-None-Match: "0d41d8cd98f00b204e9800998ecf8427e" Spring brings ShallowEtagHeaderFilter jk@devskiller.com / @jkubrynski 14 / 48
  • 15.
    Compression reduces responsesize dramatically in Tomcat extend Connector with compression="on" compressionMinSize="2048" noCompressionUserAgents="gozilla, traviata" compressableMimeType="text/html,text/xml" jk@devskiller.com / @jkubrynski 15 / 48
  • 16.
    HATEOAS self-descriptive clientunderstands hypermedia { "name": "Alice", "links": [ { "rel": "self", "href": "/customers/1213" }, { "rel": "parent", "href": "/customers/14" }, { "rel": "currentOrder", "href": "/orders/14312" } ] } HTTP/1.1 201 Created Location: http://api.mydomain.com/orders/1234 jk@devskiller.com / @jkubrynski 16 / 48
  • 17.
    HATEOAS in Spring public class Customer extends ResourceSupport { ... } // or wrap entity into Resource object jk@devskiller.com / @jkubrynski 17 / 48
  • 18.
    HATEOAS in Spring public class Customer extends ResourceSupport { ... } // or wrap entity into Resource object import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*; public HttpEntity<Customer> get(@PathVariable("id") String customerId) { Customer customer = repository.findOne(customerId); String pId = customer.getBoss(); String oId = customer.currentOrderId(); customer.add(linkTo(methodOn(CustomerController.class).get(customerId)).withSelfRel()); customer.add(linkTo(methodOn(CustomerController.class).get(pId)).withRel("parent")); customer.add(linkTo(methodOn(OrderController.class).get(oId)).withRel("currentOrder")); return new ResponseEntity<Customer>(customer, HttpStatus.OK); } public ResponseEntity create(@RequestBody Customer customer) { String id = repository.save(customer); return ResponseEntity.created(linkTo(CustomerController.class).slash(id).toUri()) .build(); } jk@devskiller.com / @jkubrynski 18 / 48
  • 19.
    @DanaDanger HTTP codesclassification 20x: cool 30x: ask that dude over there 40x: you fucked up 50x: we fucked up jk@devskiller.com / @jkubrynski 19 / 48
  • 20.
    Exceptions include detailedinformation { "status": 400, "code": 40483, "message": "Incorrect body signature", "moreInfo": "http://www.mycompany.com/errors/40483" } jk@devskiller.com / @jkubrynski 20 / 48
  • 21.
    Exceptions include detailedinformation { "status": 400, "code": 40483, "message": "Incorrect body signature", "moreInfo": "http://www.mycompany.com/errors/40483" } hide stacktrace jk@devskiller.com / @jkubrynski 21 / 48
  • 22.
    Handling Spring MVCexceptions @ControllerAdvice public class MyExceptionHandler extends ResponseEntityExceptionHandler { /* Handling framework exceptions */ @Override protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) { LOG.error("Spring MVC exception occurred", ex); return super.handleExceptionInternal(ex, body, headers, status, request); } /* Handling application exceptions */ @ResponseStatus(value = HttpStatus.NOT_FOUND) @ExceptionHandler(ResourceNotFoundException.class) public void handleResourceNotFound() { } } jk@devskiller.com / @jkubrynski 22 / 48
  • 23.
    API Versioning don'teven think about api.domain.com/v2/orders URIs to the same resources should be fixed between versions jk@devskiller.com / @jkubrynski 23 / 48
  • 24.
    API Versioning don'teven think about api.domain.com/v2/orders URIs to the same resources should be fixed between versions use Content-Type 1 version: application/vnd.domain+json 2 version: application/vnd.domain.v2+json jk@devskiller.com / @jkubrynski 24 / 48
  • 25.
    Filtering and sorting GET /reviews?rating=5 GET /reviews?rating=5&sortAsc=author jk@devskiller.com / @jkubrynski 25 / 48
  • 26.
    Filtering and sorting GET /reviews?rating=5 GET /reviews?rating=5&sortAsc=author Dynamic queries are easier in POST body jk@devskiller.com / @jkubrynski 26 / 48
  • 27.
    Filtering and sorting GET /reviews?rating=5 GET /reviews?rating=5&sortAsc=author Dynamic queries are easier in POST body POST /reviews/searches GET /reviews/searches/23?page=2 jk@devskiller.com / @jkubrynski 27 / 48
  • 28.
    Documentation runnable withexamples Swagger jk@devskiller.com / @jkubrynski 28 / 48
  • 29.
  • 30.
    Stateless or not? password hashing cost session replication load-balancing jk@devskiller.com / @jkubrynski 30 / 48
  • 31.
    Stateless or not? password hashing cost session replication load-balancing ... stateless session? jk@devskiller.com / @jkubrynski 31 / 48
  • 32.
    Avoiding session creationin Spring @EnableWebSecurity public class SpringSecurity extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() .antMatchers("/secure/**").fullyAuthenticated() .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER) .and() .httpBasic(); } } jk@devskiller.com / @jkubrynski 32 / 48
  • 33.
    Security SQL Injection XSS CSRF XXE jk@devskiller.com / @jkubrynski 33 / 48
  • 34.
    HQL Injection List<Product>products = em.createQuery( "SELECT p FROM Product p where p.category = '" + categ + "'", Product.class) .getResultList(); jk@devskiller.com / @jkubrynski 34 / 48
  • 35.
    HQL Injection List<Product>products = em.createQuery( "SELECT p FROM Product p where p.category = '" + categ + "'", Product.class) .getResultList(); categ = ' OR '1'='1 jk@devskiller.com / @jkubrynski 35 / 48
  • 36.
    HQL Injection List<Product>products = em.createQuery( "SELECT p FROM Product p where p.category = '" + categ + "'", Product.class) .getResultList(); categ = ' OR '1'='1 SELECT __fields__ FROM products WHERE category = '' OR '1'='1' jk@devskiller.com / @jkubrynski 36 / 48
  • 37.
    HQL Injection List<Product>products = em.createQuery( "SELECT p FROM Product p where p.category = '" + categ + "'", Product.class) .getResultList(); categ = ' OR '1'='1 SELECT __fields__ FROM products WHERE category = '' OR '1'='1' List<Product> products = em.createQuery( "SELECT p FROM Product p where p.category = :categ", Product.class) .setParameter("categ", categ) .getResultList(); jk@devskiller.com / @jkubrynski 37 / 48
  • 38.
    HQL Injection List<Product>products = em.createQuery( "SELECT p FROM Product p where p.category = '" + categ + "'", Product.class) .getResultList(); categ = ' OR '1'='1 SELECT __fields__ FROM products WHERE category = '' OR '1'='1' List<Product> products = em.createQuery( "SELECT p FROM Product p where p.category = :categ", Product.class) .setParameter("categ", categ) .getResultList(); SELECT __fields__ FROM products WHERE category = ' OR ''1''=''1''' jk@devskiller.com / @jkubrynski 38 / 48
  • 39.
    CSRF - Cross-siterequest forgery <img src="https://api.mybank.com/transfers/from/1233/to/1234/amount/5000"> <form action="https://api.mybank.com/transfers" method="POST"> <input type="hidden" name="from" value="1233"/> <input type="hidden" name="to" value="1234"/> <input type="hidden" name=amount" value="5000"/> <input type="submit" value="Celebrity Nude Photos!"/> </form> jk@devskiller.com / @jkubrynski 39 / 48
  • 40.
    CSRF - Cross-siterequest forgery <img src="https://api.mybank.com/transfers/from/1233/to/1234/amount/5000"> <form action="https://api.mybank.com/transfers" method="POST"> <input type="hidden" name="from" value="1233"/> <input type="hidden" name="to" value="1234"/> <input type="hidden" name=amount" value="5000"/> <input type="submit" value="Celebrity Nude Photos!"/> </form> One time request tokens Correct CORS headers jk@devskiller.com / @jkubrynski 40 / 48
  • 41.
    CORS - CrossOrigin Requests Sharing Preflight request OPTIONS /cors HTTP/1.1 Origin: http://www.domain.com Access-Control-Request-Method: PUT Access-Control-Request-Headers: X-Custom-Header Host: api.mydomain.org Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0... Preflight response Access-Control-Allow-Origin: http://www.domain.com Access-Control-Allow-Methods: GET, POST, PUT Access-Control-Allow-Headers: X-Custom-Header Content-Type: text/html; charset=utf-8 jk@devskiller.com / @jkubrynski 41 / 48
  • 42.
    XML External Entity <?xml version="1.0" encoding="utf-8"?> <comment> <text>Yeah! I like it!</text> </comment> jk@devskiller.com / @jkubrynski 42 / 48
  • 43.
    XML External Entity <?xml version="1.0" encoding="utf-8"?> <comment> <text>Yeah! I like it!</text> </comment> <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE myentity [ <!ENTITY a "Yeah! I like it!"> ]> <comment> <text>&a;</text> </comment> jk@devskiller.com / @jkubrynski 43 / 48
  • 44.
    XML External Entity <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE myentity [ <!ENTITY a SYSTEM "/etc/passwd"> ]> <comment> <text>&a;</text> </comment> jk@devskiller.com / @jkubrynski 44 / 48
  • 45.
    XML External Entity <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE myentity [ <!ENTITY a SYSTEM "/etc/passwd"> ]> <comment> <text>&a;</text> </comment> <?xml version="1.0" encoding="utf-8"?> <comment> <text>root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt ..... </text> </comment> jk@devskiller.com / @jkubrynski 45 / 48
  • 46.
    XML External Entity <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE myentity [ <!ENTITY a "abcdefghij1234567890" > <!ENTITY b "&a;&a;&a;&a;&a;&a;&a;&a;&a;&a" > <!ENTITY c "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;" > <!ENTITY d "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;" > ... <!ENTITY h "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;" > ]> <comment> <text>&h;</text> </comment> jk@devskiller.com / @jkubrynski 46 / 48
  • 47.
    http://knowyourmeme.com/photos/531557 thx to@mihn jk@devskiller.com / @jkubrynski 47 / 48
  • 48.
    Thanks! jk@devskiller.com /@jkubrynski 48 / 48