{
"type": "entry",
"published": "2020-05-31T13:49:00-07:00",
"url": "https://aaronparecki.com/2020/05/31/30/the-real-cause-of-the-sign-in-with-apple-zero-day",
"category": [
"oauth",
"oidc",
"apple",
"siwa",
"security",
"zeroday"
],
"name": "The Real Cause of the Sign In with Apple Zero-Day",
"content": {
"text": "Last week, a security researcher discovered and disclosed a zero-day bug in Sign In with Apple, and collected a $100,000 bounty.\n\nSign In with Apple is similar to OAuth and OpenID Connect, with Apple\u2019s own spin on it. While there were some critical bugs due to Apple\u2019s initial poor implementation of OpenID Connect, as documented by the OpenID Foundation, those bugs were fixed relatively quickly.\n\nThe zero-day bug that was recently discovered actually had nothing to do with the OAuth or OpenID Connect part of the Sign In with Apple exchange, and very little to do even with JWTs. Let\u2019s take a closer look to see what actually happened.\n\nThe original writeup heavily mentions JWTs and emphasizes the OAuth exchange, and I\u2019ve seen many reactions suggesting that the problem was in the JWT creation or validation, or some poor implementation of OpenID Connect. But instead, the problem was much actually much simpler than that.\n\nBreaking down the OAuth Flow\n\nIn an OAuth or OpenID Connect flow, the specs talk about the communication from the client requesting the token to the server generating the token. The steps where the user authenticates with the server are intentionally left out of the specs, since those are considered implementation details of the server.\n\nThe application initiates an OAuth request by sending the user to the authorization server. This part is the \u201cauthorization request\u201d step in the specs.\nThe user authenticates, and may see a consent screen where they approve the request from the application. This part is \u201cout of scope\u201d of the specs.\nThe authorization server generates an authorization code or ID token in the response and sends the user back to the application. This is the \u201cauthorization response\u201d step in the specs.\n\n\nLet\u2019s take a closer look at step 2, the part that you won\u2019t find any direction from the specs when building.\n\nA simple implementation for first-party applications may only ask the user to authenticate with a password.\n\n\n\nMore secure scenarios may involve the user being asked for a second authentication factor.\n\n\n\nFor third-party applications, the authorization server will often ask the user to confirm the request the application makes with a consent screen.\n\n\n\nAll of these steps happen outside of what the OAuth and OpenID Connect specs define. These are decisions that the authorization server makes in terms of which and how many of these steps to take.\n\nThis is where the flaw with Apple happened.\n\nApple\u2019s Unique OAuth Consent Screen\n\nApple has a unique feature of their consent screen which lets the user choose whether to share their real email with the application or a randomized proxy email instead. The user can also choose to edit their name that is shared with the application. Both of these features are a great step in the direction of preserving privacy and empowering the user.\n\n\n\nLet\u2019s imagine a simplified example of how this could be built. The server shows this page to the user, and provides an HTML form that will be submitted back with the options the user chooses.\n\nThe options are the user\u2019s name and an email address. Whichever the user selects will be sent back to the server, and the server will continue on with issuing the OAuth authorization response. The name is intended to be user-editable, so it makes sense that whatever the user enters here will be sent to the server. The email address however, should only be toggled between the user\u2019s real address or the generated proxy address. The problem with Apple\u2019s implementation was essentially that in this step, the email is sent to the server and the server accepted whatever email was given. The attack is to intercept this part of the flow when logging in yourself, and put in the victim\u2019s email address in the request to the server. That would cause Apple to eventually generate a JWT with the victim\u2019s email address in it, which the application would then use.\n\nHere\u2019s an example of an HTML form that has this problem. This is obviously simplified compared to Apple\u2019s actual implementation, but I\u2019m just trying to get the point across.\n\n<form action=\"/confirm\" method=\"post\">\n <input type=\"text\" name=\"name\" value=\"Aaron Parecki\">\n <select name=\"email\">\n <option value=\"aaron@parecki.com\">Share my Email</option>\n <option value=\"xxxx012345@privaterelay.apple.com\">Hide my Email</option>\n </select>\n <input type=\"submit\" value=\"Confirm\">\n</form>\n\n\nThis allows the user to edit their name and choose whether to share their real email or proxy email with the application.\n\nThe problem is in the form validation of this method on the Apple servers. Clearly someone who wanted to could change the values before submitting the form. For example, replacing the value of the \u201cShare my Email\u201d option with the email of their victim. Apple was not validating this email, so it was accepting whatever the client sent.\n\nThe result of this form action is eventually the client application would get a JWT which would contain the email address of the victim instead of that of the Apple account owner.\n\nHere\u2019s a way to build this form that would have avoided this problem in the first place.\n\n<form action=\"/confirm\" method=\"post\">\n <input type=\"text\" name=\"name\" value=\"Aaron Parecki\">\n <select name=\"email\">\n <option value=\"real_email\">Share my Email</option>\n <option value=\"proxy_email\">Hide my Email</option>\n </select>\n <input type=\"submit\" value=\"Confirm\">\n</form>\n\n\nIn this version of the form, the name field is still editable, since it\u2019s intentional that the user can type whatever they want in that field. But now, the value of the email field sent to the server will be either real_email or proxy_email, and the server will assign the appropriate value server-side. This prevents the user from being able to inject a victim\u2019s email address into this exchange.\n\nThe other way to solve this would be to validate on the server side that the submitted email matches one that is already on the user's account, which is likely what they did since Apple's actual form does still include the email address as the value.\n\nWhile this is obviously a simplified version of how Sign in with Apple works, it is meant to illustrate the bug and show how this is a common problem in any system that accepts user input.\n\nAt the end of the day, this was a problem with Apple not verifying external input from a form submission.\n\nJust remember, this bug was a lack of form validation.\n\nBut what about the JWT?\n\nThere have been a lot of comments about how this is also a problem with JWTs, either lack of JWT validation or suggesting that somehow it was a misuse of some OpenID Connect claims. There is another half of the story here in order to actually pull off this attack on a real application.\n\nGiven the bug above, an attacker was able to start signing in to an application, and swap the email claim in the JWT that was issued with a value of their choosing. The application would end up with a JWT such as the example below:\n\nRaw JWT:\n\neyJraWQiOiJlWGF1bm1MIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoibG9sLmF2b2NhZG8uY2xpZW50IiwiZXhwIjoxNTkwOTU0ODk1LCJpYXQiOjE1OTA5NTQyOTUsInN1YiI6IjAwMTQ3My5mZTZmODNiZjRiOGU0NTkwYWFjYmFiZGNiODU5OGJkMC4yMDM5Iiwibm9uY2UiOiJlODU5ZjE0MWI1IiwiYXRfaGFzaCI6Ii1wVlROTDUxSWx3X1dxZjNOOGdlQmciLCJlbWFpbCI6ImFhcm9uQHBhcmVja2kuY29tIiwiZW1haWxfdmVyaWZpZWQiOiJ0cnVlIiwiYXV0aF90aW1lIjoxNTkwOTU0Mjk0LCJub25jZV9zdXBwb3J0ZWQiOnRydWV9.rjHR_fuHKxyDt4vd9nLS5DnsEhDC4X1krR-qsiWr68uWkP8gCM2D-x3sa5_N6RFoITrqkwl6CBBPv07-LBcbndeNDP9bf4ztAtjs77CFkixoFFxQSqcc7oT28cDIzH6btyKveqRKSe1C1Fy-1ltxQRjDvAj4xDZTfAXzFrBMbeutx32zmXR8l4KbQaacjpASr8yfUKRCS8lAOk30zlALww2Zx1T0XB-swpsNhpum4nVo6iK7KOpgcY0teFxtf4xwXYGMTm-WQQJZZ65uHNdd99LGnyE_iflQvsm8iq3B7-eKpoC90uImoeN8W3W5AbFNhOY1z8yVsOtDSth9SQCvvA\n\n\nDecoded:\n\n{\n \"kid\": \"eXaunmL\",\n \"alg\": \"RS256\"\n}\n.\n{\n \"iss\": \"https://appleid.apple.com\",\n \"aud\": \"lol.avocado.client\",\n \"exp\": 1590954895,\n \"iat\": 1590954295,\n \"sub\": \"001473.fe6f83bf4b8e4590aacbabdcb8598bd0.2039\",\n \"nonce\": \"e859f141b5\",\n \"at_hash\": \"-pVTNL51Ilw_Wqf3N8geBg\",\n \"email\": \"attacker@example.com\",\n \"email_verified\": \"true\",\n \"auth_time\": 1590954294,\n \"nonce_supported\": true\n}\n.\n(signature)\n\n\nThe application would then validate the JWT by checking the signature, and consider the user \u201clogged in\u201d. Since Apple did in fact generate this JWT, the signature would be valid, so the application wouldn\u2019t know any better that the email address was not legitimate.\n\nThis points to the next problem, which only some applications would be vulnerable to.\n\nIn order to actually use this attack against the application, the application would have to be looking at only the email claim to identify the user who signed in. This is not the intent of OpenID Connect or Sign in with Apple.\n\nApplications should be using the sub value to identify the user, as the sub value is meant to be a stable identifier for users, whereas the email address on their account may change for various reasons. If an application uses only the email to identify the user, then it would be vulnerable to this kind of account takeover given the bug with Apple I described above.\n\nBut here's the real problem, and why Apple making up their own variations on the standard leads to things like this. Notice that the user's name doesn't appear anywhere in the ID token? That's because Apple decided that the user's name should actually only be delivered to the application once, the first time they log in to the app, and it is delivered in the form_post response body rather than in the ID token.\n\nYes, that means the payload your redirect URI gets will be a POST request with the following request body:\n\nstate=8c9b5acdc8\n&code=cb337ce2c688f45f6b537498acb6599fe.0.nruxt.QrMBT0tzG2Ssqs-T1L5hqA\n&user={\"name\":{\"firstName\":\"Aaron\",\"lastName\":\"Parecki\"},\"email\":\"attacker@example.com\"}\n\n\nThe application is expected to then exchange that code for the ID token, but notice that the name and email are already sent here? An app developer might look at that and say \"well that was easy, I'll just use these values and stop there\". This is where Apple would have returned the wrong email address using the technique above, so if app developers use the email address in this response to identify users, they would be vulnerable.\n\nIt would be safer for Apple to not have sent user data back in this redirect at all, and instead always return it from the token endpoint in the ID token. That way app developers could be sure that the data did in fact originate from Apple and wasn't modified.\n\nSo was this really a zero-day?\n\nApple paid $100,000 for the bounty, which is the maximum amount they list for \"Unauthorized access to iCloud account data on Apple Servers\". This suggests that some of Apple\u2019s own applications were vulnerable to this somehow, because otherwise this would have only affected access to data of third party apps.\n\nI've never seen Apple apps using Sign in with Apple themselves, typically they use their own built in Apple ID authentication directly. However it\u2019s certainly possible that Apple is moving towards Sign in with Apple even for their own apps, as that would be a huge step up in security compared to what they have now, for all the same reasons that entering passwords directly in applications is dangerous.\n\nHow to prevent these kinds of bugs in your own services?\n\nRule number 1 of authentication: Never roll your own authentication.\n\nWe have standards like OAuth which represent years of experience across a wide range of companies documenting best practices building this stuff. And while it\u2019s certainly possible to make mistakes when building an OAuth implementation, at least you have the benefit of leaning on the rest of the security community to find and document problems and patterns for building it securely.\n\nIf you\u2019re going to run or build your own OAuth server instead of using an existing service or open source product, take a very careful look at all of the OAuth RFCs and make sure you know what you\u2019re getting in to before you start.\n\nEven Apple made this mistake when they first rolled out Sign in with Apple. They had loosely based it off of OpenID Connect, but left out a few critical pieces that make it a secure implementation. These were quickly documented by the OpenID Connect community and Apple later fixed them, mostly.\n\nDon\u2019t be like Apple, use existing open standards.",
"html": "<p>Last week, a security researcher discovered and disclosed a <a href=\"https://bhavukjain.com/blog/2020/05/30/zeroday-signin-with-apple/\">zero-day bug in Sign In with Apple</a>, and collected a $100,000 bounty.</p>\n\n<p>Sign In with Apple is similar to OAuth and OpenID Connect, with Apple\u2019s own spin on it. While there were some critical bugs due to Apple\u2019s initial poor implementation of OpenID Connect, <a href=\"https://openid.net/2019/06/27/open-letter-from-the-openid-foundation-to-apple-regarding-sign-in-with-apple/\">as documented by the OpenID Foundation</a>, those bugs were fixed relatively quickly.</p>\n\n<p>The zero-day bug that was recently discovered actually had nothing to do with the OAuth or OpenID Connect part of the Sign In with Apple exchange, and very little to do even with JWTs. Let\u2019s take a closer look to see what actually happened.</p>\n\n<p>The <a href=\"https://bhavukjain.com/blog/2020/05/30/zeroday-signin-with-apple/\">original writeup</a> heavily mentions JWTs and emphasizes the OAuth exchange, and I\u2019ve seen many reactions suggesting that the problem was in the JWT creation or validation, or some poor implementation of OpenID Connect. But instead, the problem was much actually much simpler than that.</p>\n\n<h2>Breaking down the OAuth Flow</h2>\n\n<p>In an OAuth or OpenID Connect flow, the specs talk about the communication from the client requesting the token to the server generating the token. The steps where the user authenticates with the server are intentionally left out of the specs, since those are considered implementation details of the server.</p>\n\n<ol><li>The application initiates an OAuth request by sending the user to the authorization server. This part is the \u201cauthorization request\u201d step in the specs.</li>\n<li>The user authenticates, and may see a consent screen where they approve the request from the application. This part is \u201cout of scope\u201d of the specs.</li>\n<li>The authorization server generates an authorization code or ID token in the response and sends the user back to the application. This is the \u201cauthorization response\u201d step in the specs.</li>\n</ol><p><br />\nLet\u2019s take a closer look at step 2, the part that you won\u2019t find any direction from the specs when building.</p>\n\n<p>A simple implementation for first-party applications may only ask the user to authenticate with a password.</p>\n\n<p><img src=\"https://aperture-media.p3k.io/media.aaronpk.com/0a8b669b19bc72f760e72d5d49adf807fd97d069fb62086a71ea79b7f816b2f6.png\" alt=\"\" /></p>\n\n<p>More secure scenarios may involve the user being asked for a second authentication factor.</p>\n\n<p><img src=\"https://aperture-media.p3k.io/media.aaronpk.com/966c50472cf2bdd43f5703df0f0cff0612c1c2a9654b2154f156ab47cd135542.png\" alt=\"\" /></p>\n\n<p>For third-party applications, the authorization server will often ask the user to confirm the request the application makes with a consent screen.</p>\n\n<p><img src=\"https://aperture-media.p3k.io/media.aaronpk.com/f86580a2d9d865147fd16365e295aad845b2c1ab3da9f749bcee42db75aa883c.png\" alt=\"\" /></p>\n\n<p>All of these steps happen <em>outside</em> of what the OAuth and OpenID Connect specs define. These are decisions that the authorization server makes in terms of which and how many of these steps to take.</p>\n\n<p>This is where the flaw with Apple happened.</p>\n\n<h2>Apple\u2019s Unique OAuth Consent Screen</h2>\n\n<p>Apple has a unique feature of their consent screen which lets the user choose whether to share their real email with the application or a randomized proxy email instead. The user can also choose to edit their name that is shared with the application. Both of these features are a great step in the direction of preserving privacy and empowering the user.</p>\n\n<p><img src=\"https://aperture-media.p3k.io/media.aaronpk.com/714489b16635f5cdf87ad726e91325ccaaa378b565dd7183324a295aa361a90f.png\" alt=\"\" /></p>\n\n<p>Let\u2019s imagine a simplified example of how this could be built. The server shows this page to the user, and provides an HTML form that will be submitted back with the options the user chooses.</p>\n\n<p>The options are the user\u2019s name and an email address. Whichever the user selects will be sent back to the server, and the server will continue on with issuing the OAuth authorization response. The name is intended to be user-editable, so it makes sense that whatever the user enters here will be sent to the server. The email address however, should only be toggled between the user\u2019s real address or the generated proxy address. The problem with Apple\u2019s implementation was essentially that in this step, the email is sent to the server and the server accepted whatever email was given. The attack is to intercept this part of the flow when logging in yourself, and put in the victim\u2019s email address in the request to the server. That would cause Apple to eventually generate a JWT with the victim\u2019s email address in it, which the application would then use.</p>\n\n<p>Here\u2019s an example of an HTML form that has this problem. This is obviously simplified compared to Apple\u2019s actual implementation, but I\u2019m just trying to get the point across.</p>\n\n<pre><code><form action=\"/confirm\" method=\"post\">\n <input type=\"text\" name=\"name\" value=\"Aaron Parecki\">\n <select name=\"email\">\n <option value=\"aaron@parecki.com\">Share my Email</option>\n <option value=\"xxxx012345@privaterelay.apple.com\">Hide my Email</option>\n </select>\n <input type=\"submit\" value=\"Confirm\">\n</form>\n</code></pre>\n\n<p>This allows the user to edit their name and choose whether to share their real email or proxy email with the application.</p>\n\n<p>The problem is in the form validation of this method on the Apple servers. Clearly someone who wanted to could change the values before submitting the form. For example, replacing the value of the \u201cShare my Email\u201d option with the email of their victim. Apple was not validating this email, so it was accepting whatever the client sent.</p>\n\n<p>The result of this form action is eventually the client application would get a JWT which would contain the email address of the victim instead of that of the Apple account owner.</p>\n\n<p>Here\u2019s a way to build this form that would have avoided this problem in the first place.</p>\n\n<pre><code><form action=\"/confirm\" method=\"post\">\n <input type=\"text\" name=\"name\" value=\"Aaron Parecki\">\n <select name=\"email\">\n <option value=\"real_email\">Share my Email</option>\n <option value=\"proxy_email\">Hide my Email</option>\n </select>\n <input type=\"submit\" value=\"Confirm\">\n</form>\n</code></pre>\n\n<p>In this version of the form, the <code>name</code> field is still editable, since it\u2019s intentional that the user can type whatever they want in that field. But now, the value of the <code>email</code> field sent to the server will be either <code>real_email</code> or <code>proxy_email</code>, and the server will assign the appropriate value server-side. This prevents the user from being able to inject a victim\u2019s email address into this exchange.</p>\n\n<p>The other way to solve this would be to validate on the server side that the submitted email matches one that is already on the user's account, which is likely what they did since Apple's actual form does still include the email address as the value.</p>\n\n<p>While this is obviously a simplified version of how Sign in with Apple works, it is meant to illustrate the bug and show how this is a common problem in any system that accepts user input.</p>\n\n<p>At the end of the day, this was a problem with Apple not verifying external input from a form submission.</p>\n\n<p><strong>Just remember, this bug was a lack of form validation.</strong></p>\n\n<h2>But what about the JWT?</h2>\n\n<p>There have been a lot of comments about how this is also a problem with JWTs, either lack of JWT validation or suggesting that somehow it was a misuse of some OpenID Connect claims. There is another half of the story here in order to actually pull off this attack on a real application.</p>\n\n<p>Given the bug above, an attacker was able to start signing in to an application, and swap the <code>email</code> claim in the JWT that was issued with a value of their choosing. The application would end up with a JWT such as the example below:</p>\n\n<p>Raw JWT:</p>\n\n<pre><code>eyJraWQiOiJlWGF1bm1MIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoibG9sLmF2b2NhZG8uY2xpZW50IiwiZXhwIjoxNTkwOTU0ODk1LCJpYXQiOjE1OTA5NTQyOTUsInN1YiI6IjAwMTQ3My5mZTZmODNiZjRiOGU0NTkwYWFjYmFiZGNiODU5OGJkMC4yMDM5Iiwibm9uY2UiOiJlODU5ZjE0MWI1IiwiYXRfaGFzaCI6Ii1wVlROTDUxSWx3X1dxZjNOOGdlQmciLCJlbWFpbCI6ImFhcm9uQHBhcmVja2kuY29tIiwiZW1haWxfdmVyaWZpZWQiOiJ0cnVlIiwiYXV0aF90aW1lIjoxNTkwOTU0Mjk0LCJub25jZV9zdXBwb3J0ZWQiOnRydWV9.rjHR_fuHKxyDt4vd9nLS5DnsEhDC4X1krR-qsiWr68uWkP8gCM2D-x3sa5_N6RFoITrqkwl6CBBPv07-LBcbndeNDP9bf4ztAtjs77CFkixoFFxQSqcc7oT28cDIzH6btyKveqRKSe1C1Fy-1ltxQRjDvAj4xDZTfAXzFrBMbeutx32zmXR8l4KbQaacjpASr8yfUKRCS8lAOk30zlALww2Zx1T0XB-swpsNhpum4nVo6iK7KOpgcY0teFxtf4xwXYGMTm-WQQJZZ65uHNdd99LGnyE_iflQvsm8iq3B7-eKpoC90uImoeN8W3W5AbFNhOY1z8yVsOtDSth9SQCvvA\n</code></pre>\n\n<p>Decoded:</p>\n\n<pre><code>{\n \"kid\": \"eXaunmL\",\n \"alg\": \"RS256\"\n}\n.\n{\n \"iss\": \"https://appleid.apple.com\",\n \"aud\": \"lol.avocado.client\",\n \"exp\": 1590954895,\n \"iat\": 1590954295,\n \"sub\": \"001473.fe6f83bf4b8e4590aacbabdcb8598bd0.2039\",\n \"nonce\": \"e859f141b5\",\n \"at_hash\": \"-pVTNL51Ilw_Wqf3N8geBg\",\n \"email\": \"attacker@example.com\",\n \"email_verified\": \"true\",\n \"auth_time\": 1590954294,\n \"nonce_supported\": true\n}\n.\n(signature)\n</code></pre>\n\n<p>The application would then validate the JWT by checking the signature, and consider the user \u201clogged in\u201d. Since Apple did in fact generate this JWT, the signature would be valid, so the application wouldn\u2019t know any better that the email address was not legitimate.</p>\n\n<p>This points to the next problem, which only some applications would be vulnerable to.</p>\n\n<p>In order to actually use this attack against the application, the application would have to be looking at <em>only</em> the <code>email</code> claim to identify the user who signed in. This is not the intent of OpenID Connect or Sign in with Apple.</p>\n\n<p>Applications should be using the <code>sub</code> value to identify the user, as the <code>sub</code> value is meant to be a stable identifier for users, whereas the email address on their account may change for various reasons. If an application uses only the <code>email</code> to identify the user, then it would be vulnerable to this kind of account takeover given the bug with Apple I described above.</p>\n\n<p>But here's the real problem, and why Apple making up their own variations on the standard leads to things like this. Notice that the user's name doesn't appear anywhere in the ID token? That's because Apple decided that the user's name should actually only be delivered to the application once, the first time they log in to the app, and it is delivered in the <code>form_post</code> response body rather than in the ID token.</p>\n\n<p>Yes, that means the payload your redirect URI gets will be a POST request with the following request body:</p>\n\n<pre><code>state=8c9b5acdc8\n&code=cb337ce2c688f45f6b537498acb6599fe.0.nruxt.QrMBT0tzG2Ssqs-T1L5hqA\n&user={\"name\":{\"firstName\":\"Aaron\",\"lastName\":\"Parecki\"},\"email\":\"attacker@example.com\"}\n</code></pre>\n\n<p>The application is expected to then exchange that code for the ID token, but notice that the name and email are already sent here? An app developer might look at that and say \"well that was easy, I'll just use these values and stop there\". This is where Apple would have returned the wrong email address using the technique above, so if app developers use the email address in this response to identify users, they would be vulnerable.</p>\n\n<p>It would be safer for Apple to not have sent user data back in this redirect at all, and instead always return it from the token endpoint in the ID token. That way app developers could be sure that the data did in fact originate from Apple and wasn't modified.</p>\n\n<h2>So was this really a zero-day?</h2>\n\n<p>Apple paid $100,000 for the bounty, which is the maximum amount they list for <a href=\"https://aaronparecki.com/%5Bdeveloper.apple.com/security-bounty/%5D(https://developer.apple.com/security-bounty/)(https://developer.apple.com/security-bounty/)\">\"Unauthorized access to iCloud account data on Apple Servers\"</a>. This suggests that some of Apple\u2019s own applications were vulnerable to this somehow, because otherwise this would have only affected access to data of third party apps.</p>\n\n<p>I've never seen Apple apps using Sign in with Apple themselves, typically they use their own built in Apple ID authentication directly. However it\u2019s certainly possible that Apple is moving towards Sign in with Apple even for their own apps, as that would be a huge step up in security compared to what they have now, for all the same reasons that <a href=\"https://aaronparecki.com/%5Boauth.net/2/grant-types/password/%5D(https://oauth.net/2/grant-types/password/)(https://oauth.net/2/grant-types/password/)\">entering passwords directly in applications is dangerous</a>.</p>\n\n<h2>How to prevent these kinds of bugs in your own services?</h2>\n\n<p>Rule number 1 of authentication: Never roll your own authentication.</p>\n\n<p>We have standards like OAuth which represent years of experience across a wide range of companies documenting best practices building this stuff. And while it\u2019s certainly possible to make mistakes when building an OAuth implementation, at least you have the benefit of leaning on the rest of the security community to find and document problems and patterns for building it securely.</p>\n\n<p>If you\u2019re going to run or build your own OAuth server instead of using an <a href=\"https://developer.okta.com\">existing service</a> or <a href=\"https://aaronparecki.com/%5Boauth.net/code/%5D(https://oauth.net/code/)(https://oauth.net/code/)\">open source product</a>, take a very careful look at all of the OAuth RFCs and make sure you know what you\u2019re getting in to before you start.</p>\n\n<p>Even Apple made this mistake when they first rolled out Sign in with Apple. They had loosely based it off of OpenID Connect, but left out a few critical pieces that make it a secure implementation. These were quickly <a href=\"https://aaronparecki.com/%5Bbitbucket.org/openid/connect/src/master/How-Sign-in-with-Apple-differs-from-OpenID-Connect.md%5D(https://bitbucket.org/openid/connect/src/master/How-Sign-in-with-Apple-differs-from-OpenID-Connect.md)(https://bitbucket.org/openid/connect/src/master/How-Sign-in-with-Apple-differs-from-OpenID-Connect.md)\">documented by the OpenID Connect community</a> and Apple later fixed them, mostly.</p>\n\n<p><strong>Don\u2019t be like Apple, use existing open standards.</strong></p>"
},
"author": {
"type": "card",
"name": "Aaron Parecki",
"url": "https://aaronparecki.com/",
"photo": "https://aperture-media.p3k.io/aaronparecki.com/41061f9de825966faa22e9c42830e1d4a614a321213b4575b9488aa93f89817a.jpg"
},
"post-type": "article",
"_id": "12040408",
"_source": "16"
}
White people: find your black friends. Check in with them.
DON'T ask them to explain systemic racism to you, get yourself educated on your own - just be there to listen if they need it and stand arm and arm with them (while wearing masks) in any way you can.
{
"type": "entry",
"published": "2020-05-31T18:49:57+00:00",
"url": "https://twitter.com/anomalily/status/1267166442773856256",
"content": {
"text": "White people: find your black friends. Check in with them. \n\nDON'T ask them to explain systemic racism to you, get yourself educated on your own - just be there to listen if they need it and stand arm and arm with them (while wearing masks) in any way you can."
},
"author": {
"type": "card",
"name": "Lillian Karabaic \ud83e\udd44\ud83c\udff3\ufe0f\u200d\ud83c\udf08 BLM",
"url": "https://twitter.com/anomalily",
"photo": "https://pbs.twimg.com/profile_images/1123802400731664385/dsHQG1nZ.jpg"
},
"post-type": "note",
"_id": "12037949",
"_source": "2773"
}
watching a stream of the union square protests to see this police officer throw up what looks to be a white power sign. stay safe out there, these cops are monsters.
{
"type": "entry",
"published": "2020-05-31T18:48:06+00:00",
"url": "https://twitter.com/afitnerd/status/1267165976845586434",
"quotation-of": "https://twitter.com/kikimurphy_/status/1266936387124826113",
"content": {
"text": "Present day Nazis in uniform in NYC. Is this who you're supporting @NYCMayor?",
"html": "Present day Nazis in uniform in NYC. Is this who you're supporting <a href=\"https://twitter.com/NYCMayor\">@NYCMayor</a>?"
},
"author": {
"type": "card",
"name": "Micah Silverman",
"url": "https://twitter.com/afitnerd",
"photo": "https://pbs.twimg.com/profile_images/1169746170740137984/R7RpX8Q7.jpg"
},
"post-type": "note",
"refs": {
"https://twitter.com/kikimurphy_/status/1266936387124826113": {
"type": "entry",
"published": "2020-05-31T03:35:47+00:00",
"url": "https://twitter.com/kikimurphy_/status/1266936387124826113",
"video": [
"https://video.twimg.com/ext_tw_video/1266936354899976193/pu/vid/720x804/zHH0cJ9RsM_DlUNS.mp4?tag=10"
],
"content": {
"text": "watching a stream of the union square protests to see this police officer throw up what looks to be a white power sign. stay safe out there, these cops are monsters."
},
"author": {
"type": "card",
"name": "kiki",
"url": "https://twitter.com/kikimurphy_",
"photo": "https://pbs.twimg.com/profile_images/1255609328133955601/i3Fwgv-W.jpg"
},
"post-type": "video"
}
},
"_id": "12037687",
"_source": "2773"
}
In a crowd of mixed race people peacefully protesting, a black man is singled out and sprayed in the face.
It's like they've been emboldened by a constant stream of white supremacy spewing from the highest position of government for the last 3+ years...
youtu.be/GEP0NIB97Iw
{
"type": "entry",
"published": "2020-05-31T18:39:22+00:00",
"url": "https://twitter.com/afitnerd/status/1267163778837295106",
"content": {
"text": "In a crowd of mixed race people peacefully protesting, a black man is singled out and sprayed in the face.\n\nIt's like they've been emboldened by a constant stream of white supremacy spewing from the highest position of government for the last 3+ years...\n\nyoutu.be/GEP0NIB97Iw",
"html": "In a crowd of mixed race people peacefully protesting, a black man is singled out and sprayed in the face.\n\nIt's like they've been emboldened by a constant stream of white supremacy spewing from the highest position of government for the last 3+ years...\n\n<a href=\"https://youtu.be/GEP0NIB97Iw\">youtu.be/GEP0NIB97Iw</a>"
},
"author": {
"type": "card",
"name": "Micah Silverman",
"url": "https://twitter.com/afitnerd",
"photo": "https://pbs.twimg.com/profile_images/1169746170740137984/R7RpX8Q7.jpg"
},
"post-type": "note",
"_id": "12037690",
"_source": "2773"
}
We, living in White America, have a lot to answer for. I'm at a loss even to know where to start. The way I was raised, it's hard even to admit my people are part of the problem.
This is difficult for me to comprehend. I would say I'm in tears, but white tears don't do shit.
twitter.com/antoniofrench/…
{
"type": "entry",
"published": "2020-05-31T18:35:40+00:00",
"url": "https://twitter.com/jaredcwhite/status/1267162850075656192",
"quotation-of": "https://twitter.com/AntonioFrench/status/1266960975124717568",
"content": {
"text": "We, living in White America, have a lot to answer for. I'm at a loss even to know where to start. The way I was raised, it's hard even to admit my people are part of the problem.\n\nThis is difficult for me to comprehend. I would say I'm in tears, but white tears don't do shit.\ntwitter.com/antoniofrench/\u2026",
"html": "We, living in White America, have a lot to answer for. I'm at a loss even to know where to start. The way I was raised, it's hard even to admit my people are part of the problem.\n\nThis is difficult for me to comprehend. I would say I'm in tears, but white tears don't do shit.\n<a href=\"https://twitter.com/antoniofrench/status/1266960975124717568\">twitter.com/antoniofrench/\u2026</a>"
},
"author": {
"type": "card",
"name": "Jared White",
"url": "https://twitter.com/jaredcwhite",
"photo": "https://pbs.twimg.com/profile_images/1232704698890670080/goiKk3uW.jpg"
},
"post-type": "note",
"refs": {
"https://twitter.com/AntonioFrench/status/1266960975124717568": {
"type": "entry",
"published": "2020-05-31T05:13:30+00:00",
"url": "https://twitter.com/AntonioFrench/status/1266960975124717568",
"video": [
"https://video.twimg.com/ext_tw_video/1266925587505037313/pu/vid/592x1280/J5GeoBmyhE-3-Ur2.mp4?tag=10"
],
"content": {
"text": "Generations of pain."
},
"author": {
"type": "card",
"name": "Antonio French",
"url": "https://twitter.com/AntonioFrench",
"photo": "https://pbs.twimg.com/profile_images/1224460424525029376/KPUSJ1dX.jpg"
},
"post-type": "video"
}
},
"_id": "12037434",
"_source": "2773"
}
If you see bots out there attempting to affect the discourse around this latest wave of protests, please send them to me, either by DM or the form on my website. (Please don't tag the account.) I'm an independent researcher trying to figure out the deal with bots & discourse.
I don’t like to blame bots for much because I know there’s definitely a lot of actual people worth dog shit on here, but there are absolutely a lot of bots this eveni...
{
"type": "entry",
"published": "2020-05-31T18:19:27+00:00",
"url": "https://twitter.com/tinysubversions/status/1267158767256236032",
"quotation-of": "https://twitter.com/chrissyteigen/status/1266962502874656768",
"content": {
"text": "If you see bots out there attempting to affect the discourse around this latest wave of protests, please send them to me, either by DM or the form on my website. (Please don't tag the account.) I'm an independent researcher trying to figure out the deal with bots & discourse.",
"html": "If you see bots out there attempting to affect the discourse around this latest wave of protests, please send them to me, either by DM or the form on my website. (Please don't tag the account.) I'm an independent researcher trying to figure out the deal with bots & discourse."
},
"author": {
"type": "card",
"name": "Darius Kazemi",
"url": "https://twitter.com/tinysubversions",
"photo": "https://pbs.twimg.com/profile_images/1204800150939435008/Qk7oX7db.jpg"
},
"post-type": "note",
"refs": {
"https://twitter.com/chrissyteigen/status/1266962502874656768": {
"type": "entry",
"published": "2020-05-31T05:19:34+00:00",
"url": "https://twitter.com/chrissyteigen/status/1266962502874656768",
"content": {
"text": "I don\u2019t like to blame bots for much because I know there\u2019s definitely a lot of actual people worth dog shit on here, but there are absolutely a lot of bots this evening."
},
"author": {
"type": "card",
"name": "chrissy teigen",
"url": "https://twitter.com/chrissyteigen",
"photo": "https://pbs.twimg.com/profile_images/1243351734757425152/e8JZwf03.jpg"
},
"post-type": "note"
}
},
"_id": "12037038",
"_source": "2773"
}
{
"type": "entry",
"published": "2020-05-31T18:07:06+00:00",
"url": "https://twitter.com/anomalily/status/1267155658991824896",
"content": {
"text": "Is anyone in Portland able to point me to any places where I can be helpful in cleaning up damage to black-owned businesses? #blmpdx",
"html": "Is anyone in Portland able to point me to any places where I can be helpful in cleaning up damage to black-owned businesses? <a href=\"https://twitter.com/search?q=%23blmpdx\">#blmpdx</a>"
},
"author": {
"type": "card",
"name": "Lillian Karabaic \ud83e\udd44\ud83c\udff3\ufe0f\u200d\ud83c\udf08 BLM",
"url": "https://twitter.com/anomalily",
"photo": "https://pbs.twimg.com/profile_images/1123802400731664385/dsHQG1nZ.jpg"
},
"post-type": "note",
"_id": "12036777",
"_source": "2773"
}
Share widely: National guard and MPD sweeping our residential street. Shooting paint canisters at us on our own front porch. Yelling “light em up” #JusticeForGeorgeFl...
TIL that @cjane87 and I are the same age and from the same hometown - we both have clear memories of the protests and curfew and rubber bullets shot into crowds after Timothy Thomas Jr was killed - 19 years ago ago. What has CHANGED? Black men are still dying at the hands of cops
Stop whatever you're doing and watch @cjane87:
"I have to be honest with you Brian, I don’t really care about the dueling narratives.
A man is dead in Minnesota and ...
{
"type": "entry",
"published": "2020-05-31T17:36:13+00:00",
"url": "https://twitter.com/anomalily/status/1267147885549780993",
"quotation-of": "https://twitter.com/intlcarly/status/1267120677930143745",
"content": {
"text": "TIL that @cjane87 and I are the same age and from the same hometown - we both have clear memories of the protests and curfew and rubber bullets shot into crowds after Timothy Thomas Jr was killed - 19 years ago ago. What has CHANGED? Black men are still dying at the hands of cops",
"html": "TIL that <a href=\"https://twitter.com/cjane87\">@cjane87</a> and I are the same age and from the same hometown - we both have clear memories of the protests and curfew and rubber bullets shot into crowds after Timothy Thomas Jr was killed - 19 years ago ago. What has CHANGED? Black men are still dying at the hands of cops"
},
"author": {
"type": "card",
"name": "Lillian Karabaic \ud83e\udd44\ud83c\udff3\ufe0f\u200d\ud83c\udf08 BLM",
"url": "https://twitter.com/anomalily",
"photo": "https://pbs.twimg.com/profile_images/1123802400731664385/dsHQG1nZ.jpg"
},
"post-type": "note",
"refs": {
"https://twitter.com/intlcarly/status/1267120677930143745": {
"type": "entry",
"published": "2020-05-31T15:48:06+00:00",
"url": "https://twitter.com/intlcarly/status/1267120677930143745",
"video": [
"https://video.twimg.com/ext_tw_video/1267120414263668737/pu/vid/720x400/TVltjYRkFkD7lz0O.mp4?tag=10"
],
"content": {
"text": "Stop whatever you're doing and watch @cjane87:\n\n\"I have to be honest with you Brian, I don\u2019t really care about the dueling narratives. \n\nA man is dead in Minnesota and he died as a result of the weaponization of the state that\u2019s been taking place since I was in 8th grade.\"",
"html": "Stop whatever you're doing and watch <a href=\"https://twitter.com/cjane87\">@cjane87</a>:\n\n\"I have to be honest with you Brian, I don\u2019t really care about the dueling narratives. \n\nA man is dead in Minnesota and he died as a result of the weaponization of the state that\u2019s been taking place since I was in 8th grade.\""
},
"author": {
"type": "card",
"name": "Carly Ortiz\ud83c\udf43",
"url": "https://twitter.com/intlcarly",
"photo": "https://pbs.twimg.com/profile_images/1230544618703511553/eW5AKbwA.jpg"
},
"post-type": "video"
}
},
"_id": "12036168",
"_source": "2773"
}
Good morning programmers!
I've been writing object-oriented code for nearly 20 years, and I do not religiously follow ANY of the OOP patterns that get trotted out in educational materials.
(I'm looking at you, SOLID.)
{
"type": "entry",
"published": "2020-05-31T15:33:19+00:00",
"url": "https://twitter.com/jaredcwhite/status/1267116960107524096",
"content": {
"text": "Good morning programmers!\n\nI've been writing object-oriented code for nearly 20 years, and I do not religiously follow ANY of the OOP patterns that get trotted out in educational materials.\n\n(I'm looking at you, SOLID.)"
},
"author": {
"type": "card",
"name": "Jared White",
"url": "https://twitter.com/jaredcwhite",
"photo": "https://pbs.twimg.com/profile_images/1232704698890670080/goiKk3uW.jpg"
},
"post-type": "note",
"_id": "12033206",
"_source": "2773"
}
{
"type": "entry",
"published": "2020-05-31T10:42:31-0400",
"url": "https://martymcgui.re/2020/05/31/104231/",
"category": [
"caturday"
],
"video": [
"https://media.martymcgui.re/20/68/ca/1a/a26b3a68f2b4211a126b7ed1e62897637f9bd4b2750ac6b5a491a98e.mov"
],
"content": {
"text": "Wheew all that preparation was exhausting I am gonna take a break now. Good eternal Caturdaying everyone.",
"html": "<p>Wheew all that preparation was exhausting I am gonna take a break now. Good eternal Caturdaying everyone.</p>"
},
"author": {
"type": "card",
"name": "Marty McGuire",
"url": "https://martymcgui.re/",
"photo": "https://martymcgui.re/images/logo.jpg"
},
"post-type": "video",
"_id": "12032349",
"_source": "175"
}
{
"type": "entry",
"published": "2020-05-31T14:22:40+0000",
"url": "https://quickthoughts.jgregorymcverry.com/2020/05/31/published-todays-poembox-poetry-starved-trees-mypoetrytoday",
"category": [
"poembox",
"poetry",
"writingcommunity",
"clmooc",
"nctevillage"
],
"syndication": [
"https://twitter.com/jgmac1106/status/1267099198824079368"
],
"content": {
"text": "Published today's #poembox #poetry \"Starved Trees\" https://jgregorymcverry.com/mypoetry#today dedicated to students of color who must come to terms with fact that being black or brown can and does kill you #writingcommunity #clmooc #nctevillage",
"html": "Published today's <a href=\"https://quickthoughts.jgregorymcverry.com/tag/poembox\" class=\"p-category\">#poembox</a> <a href=\"https://quickthoughts.jgregorymcverry.com/tag/poetry\" class=\"p-category\">#poetry</a> \"Starved Trees\" <a href=\"https://jgregorymcverry.com/mypoetry#today\">https://jgregorymcverry.com/mypoetry#today</a> dedicated to students of color who must come to terms with fact that being black or brown can and does kill you <a href=\"https://quickthoughts.jgregorymcverry.com/tag/writingcommunity\" class=\"p-category\">#writingcommunity</a> <a href=\"https://quickthoughts.jgregorymcverry.com/tag/clmooc\" class=\"p-category\">#clmooc</a> <a href=\"https://quickthoughts.jgregorymcverry.com/tag/nctevillage\" class=\"p-category\">#nctevillage</a>"
},
"author": {
"type": "card",
"name": "Greg McVerry",
"url": "https://quickthoughts.jgregorymcverry.com/profile/jgmac1106",
"photo": "https://quickthoughts.jgregorymcverry.com/file/2d6c9cfed7ac8e849f492b5bc7e6a630/thumb.jpg"
},
"post-type": "note",
"_id": "12031691",
"_source": "1300"
}
{
"type": "entry",
"published": "2020-05-31T14:22:45+00:00",
"url": "https://twitter.com/jgmac1106/status/1267099198824079368",
"content": {
"text": "Published today's #poembox #poetry \"Starved Trees\" jgregorymcverry.com/mypoetry#today dedicated to students of color who must come to terms with fact that being black or brown can and does kill you #writingcommunity #clmooc #nctevillage (quickthoughts.jgregorymcverry.com/s/1cjkfq)",
"html": "Published today's <a href=\"https://twitter.com/search?q=%23poembox\">#poembox</a> <a href=\"https://twitter.com/search?q=%23poetry\">#poetry</a> \"Starved Trees\" <a href=\"https://jgregorymcverry.com/mypoetry#today\">jgregorymcverry.com/mypoetry#today</a> dedicated to students of color who must come to terms with fact that being black or brown can and does kill you <a href=\"https://twitter.com/search?q=%23writingcommunity\">#writingcommunity</a> <a href=\"https://twitter.com/search?q=%23clmooc\">#clmooc</a> <a href=\"https://twitter.com/search?q=%23nctevillage\">#nctevillage</a> (<a href=\"https://quickthoughts.jgregorymcverry.com/s/1cjkfq\">quickthoughts.jgregorymcverry.com/s/1cjkfq</a>)"
},
"author": {
"type": "card",
"name": "https://jgregorymcverry.com",
"url": "https://twitter.com/jgmac1106",
"photo": "https://pbs.twimg.com/profile_images/565227710104883200/g4MDcTnx.jpeg"
},
"post-type": "note",
"_id": "12031444",
"_source": "2773"
}
We need a truth and reconciliation commission. We need to demilitarize our police. We need to hear the rage and heal our trauma. This is not a WHO or China problem to fix or blame. We hurt ourselves and need triage. Now.
{
"type": "entry",
"published": "2020-05-31T13:01:16+00:00",
"url": "https://twitter.com/scott_gruber/status/1267078692552892419",
"content": {
"text": "We need a truth and reconciliation commission. We need to demilitarize our police. We need to hear the rage and heal our trauma. This is not a WHO or China problem to fix or blame. We hurt ourselves and need triage. Now."
},
"author": {
"type": "card",
"name": "Scott Gruber",
"url": "https://twitter.com/scott_gruber",
"photo": "https://pbs.twimg.com/profile_images/1262447647140360192/9wzdpsFz.jpg"
},
"post-type": "note",
"_id": "12029944",
"_source": "2773"
}