**The EventSource API** _The EventSource interface is used to receive server-sent events. It connects to a server over HTTP and receives events in text/event-stream format without closing the connection._ https://developer.mozilla.org/en-US/docs/Web/API/EventSource **Last-Event-ID** _Setting an ID lets the browser keep track of the last event fired so that if, the connection to the server is dropped, a special HTTP header (Last-Event-ID) is set with the new request._ https://www.html5rocks.com/en/tutorials/eventsource/basics/ **Sending the Last-Event-ID header across origins:** In order to send an arbitrary Last-Event-ID header across origins a website and a cooperating server is needed. The following page tells the browser to open a connection to the attacker's server: ```html
``` Given the server responds as follows: ```http HTTP/1.1 200 OK Content-Type: text/event-stream Access-Control-Allow-Origin: http://127.0.0.1 Access-Control-Allow-Credentials: true id: server-controlled data: injection ``` the browser will store the event ID. If the server sends no more data a reconnection is triggered and the browser will issue a subsequent request that already contains the Last-Event-ID header with the ID set by the server. Responding with a redirect will result in an additional request (made by the browser) to the specified location: ```http HTTP/1.1 302 Found Content-Type: text/event-stream Access-Control-Allow-Origin: http://127.0.0.1 Access-Control-Allow-Credentials: true Location: http://example.com/page.php ``` _Tip: You can easily achieve this using netcat: `cat response1 | nc -nvlp 11111 && cat response2 | nc -nvlp 11111`_ Note that there is no preflight request for the cross-origin request. I don't know why. [This issue](https://github.com/whatwg/fetch/issues/568) implies that it's by design although the accompanying commit is not merged to the master branch, yet. If you have more insight or any other additional info please let me know in the comments. Furthermore, in case the Last-Event-ID gets CORS white-listed the EventSource API, most likely, won't be needed anymore to achieve the same behavior (fetch or XHR will do). **Sample scenario** _Stored XSS via HTTP Header_ Imagine a website that stores and presents detailed logs of authenticated requests to users. We saw that a malicious website can force users' browsers into issuing authenticated cross-origin requests (GET) with an arbitrary Last-Event-ID header. Let's say this arbitrary value is an XSS payload that is reflected back to the user when viewing the aforementioned logs: ```http HTTP/1.1 200 OK Content-Type: text/event-stream Access-Control-Allow-Origin: http://127.0.0.1 Access-Control-Allow-Credentials: true id: data: injection ``` In case the website fails to properly encode the header value (e.g. under the assumption that it can't be forged by an attacker) the payload is executed under the current user's context. **Conclusion** HTTP request headers are user-controlled data. Although browsers do a decent job to prevent arbitrary websites to issue cross-origin requests with arbitrary headers, it's still important to treat request headers as untrusted data. Also, it should be noted that the Last-Event-ID is [currently listed as CORS allowed in MDN web docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Simple_requests). _Thanks to [@Abdulahhusam](https://twitter.com/Abdulahhusam) for his [latest challenge](http://sandbox.ahussam.me/challenges/2.php), which made me learn all of the above while I was trying to solve it._