[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"work:secret-key:en":3},[4,17],{"type":5,"items":6},"toc",[7,11,14],{"id":8,"level":9,"title":10},"how-do-we-find-hidden-api-keys",2,"How Do We Find Hidden API Keys?",{"id":12,"level":9,"title":13},"add-the-check-to-the-process","Add the Check to the Process",{"id":15,"level":9,"title":16},"retrospective","Retrospective",{"type":18,"html":19},"html","\u003Cp>While working on a new requirement for a page I had taken over from my predecessor, I happened to find one line of code.\u003C\u002Fp>\n\u003Cp>I wondered if I had misread it.\u003C\u002Fp>\n\u003Cp>I checked the browser developer tools, hoping it was not what I thought. It was plainly visible.\u003C\u002Fp>\n\u003Cpre class=\"shiki github-light\" style=\"background-color:#fff;color:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E\">$.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1\">ajax\u003C\u002Fspan>\u003Cspan style=\"color:#24292E\">({\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E\">  url: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62\">'https:\u002F\u002Fsome-api.com\u002Fitems\u002Frecommendations'\u003C\u002Fspan>\u003Cspan style=\"color:#24292E\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E\">  data: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5\">JSON\u003C\u002Fspan>\u003Cspan style=\"color:#24292E\">.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1\">stringify\u003C\u002Fspan>\u003Cspan style=\"color:#24292E\">(data),\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1\">  beforeSend\u003C\u002Fspan>\u003Cspan style=\"color:#24292E\">: \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49\">function\u003C\u002Fspan>\u003Cspan style=\"color:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E36209\">xhr\u003C\u002Fspan>\u003Cspan style=\"color:#24292E\">){xhr.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1\">setRequestHeader\u003C\u002Fspan>\u003Cspan style=\"color:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62\">'x-api-key'\u003C\u002Fspan>\u003Cspan style=\"color:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"color:#032F62\">'k8nSt...'\u003C\u002Fspan>\u003Cspan style=\"color:#24292E\">)},\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49\">  ...\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E\">})\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>I immediately revoked the key and made a hotfix that moved the logic to the server.\u003C\u002Fp>\n\u003Cp>The usage history showed no signs of suspected misuse. But because the key had been exposed on an old JSP page, it was difficult to determine whether it had actually leaked.\u003C\u002Fp>\n\u003Cp>I was lucky to find it this time, but other pages could have the same problem. We decided to inspect all related repositories as quickly as possible.\u003C\u002Fp>\n\u003Ch2 id=\"how-do-we-find-hidden-api-keys\">How Do We Find Hidden API Keys?\u003C\u002Fh2>\n\u003Cp>API keys are generally long and contain irregular mixtures of many characters so that attackers cannot guess them.\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"__WORK_ASSET__api-key-created.webp\" alt=\"\" loading=\"lazy\">\u003C\u002Fp>\n\u003Cp>Shannon entropy can quantify how irregular a string is.\u003C\u002Fp>\n\u003Cp>For example, if we calculate the entropy of the following two strings, the first has a higher value.\u003C\u002Fp>\n\u003Cpre class=\"shiki github-light\" style=\"background-color:#fff;color:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">### Comparing Shannon entropy values\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1\">1.\u003C\u002Fspan>\u003Cspan style=\"color:#032F62\"> A0D28bba7b1287ff432d0b\u003C\u002Fspan>\u003Cspan style=\"color:#032F62\"> :\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5\"> 3.5161\u003C\u002Fspan>\u003Cspan style=\"color:#6A737D\"> # Irregularly mixed string\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1\">2.\u003C\u002Fspan>\u003Cspan style=\"color:#032F62\"> aaaaaaabbbbbbbbccccccc\u003C\u002Fspan>\u003Cspan style=\"color:#032F62\"> :\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5\"> 1.5820\u003C\u002Fspan>\u003Cspan style=\"color:#6A737D\"> # Three repeated lowercase letters\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>This is because the first string uses more kinds of characters than the second, and the frequency of each character is relatively more distributed.\u003C\u002Fp>\n\u003Cp>But a high Shannon entropy value does not necessarily mean that a string is an API key.\u003C\u002Fp>\n\u003Cp>A UUID, for example, also has fairly high entropy.\u003C\u002Fp>\n\u003Cpre class=\"shiki github-light\" style=\"background-color:#fff;color:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1\">c1b100db-f254-493f-8b53-f0fcab2437d0:\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5\"> 3.7391\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>Entropy alone therefore cannot determine whether a string is an API key. It can, however, help narrow down suspicious candidates.\u003C\u002Fp>\n\u003Cp>For a quick inspection, I wrote a script that calculated the entropy of strings in the source code and produced a report. It extracted about 100 suspicious strings from ten repositories. We found and addressed one more key that had been externally exposed.\u003C\u002Fp>\n\u003Cpre class=\"shiki github-light\" style=\"background-color:#fff;color:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62\">=================\u003C\u002Fspan>\u003Cspan style=\"color:#032F62\"> Example\u003C\u002Fspan>\u003Cspan style=\"color:#032F62\"> List\u003C\u002Fspan>\u003Cspan style=\"color:#032F62\"> of\u003C\u002Fspan>\u003Cspan style=\"color:#032F62\"> Detected\u003C\u002Fspan>\u003Cspan style=\"color:#032F62\"> Strings\u003C\u002Fspan>\u003Cspan style=\"color:#032F62\"> ================\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1\">EXAMPLE-KEY-8bba7b1287ff432d0b7777532f52fd59ef75cc56d3818b8ed5a8d06\u003C\u002Fspan>\u003Cspan style=\"color:#032F62\"> :\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5\"> 4.3197\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1\">5b8621ee0cf2475f66709a5d\u003C\u002Fspan>\u003Cspan style=\"color:#032F62\"> :\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5\"> 3.7721\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1\">eventEntry1Remark\u003C\u002Fspan>\u003Cspan style=\"color:#032F62\"> :\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5\"> 3.4548\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1\">1842692849310691\u003C\u002Fspan>\u003Cspan style=\"color:#032F62\"> :\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5\"> 2.9056\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1\">P2018043031858\u003C\u002Fspan>\u003Cspan style=\"color:#032F62\"> :\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5\"> 2.8424\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5\">...\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch2 id=\"add-the-check-to-the-process\">Add the Check to the Process\u003C\u002Fh2>\n\u003Cp>The script was a temporary tool for quickly inspecting all related repositories.\u003C\u002Fp>\n\u003Cp>At the time, we were already considering introducing static analysis with SonarQube, which also had a \u003Ca href=\"https:\u002F\u002Fwww.sonarsource.com\u002Fresources\u002Flibrary\u002Fdetect-secrets-in-the-ide-with-sonarlint\u002F\" target=\"_blank\" rel=\"noreferrer\">secret detection feature\u003C\u002Fa>. Instead of operating the script separately, we decided to include this check in the existing build flow.\u003C\u002Fp>\n\u003Cp>I configured the build process to perform static analysis on the source code. If it found issues above our internal threshold, both the Quality Gate and the build failed. If the same kind of problem occurs again, this should help us find and address it before it reaches production.\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"__WORK_ASSET__image.webp\" alt=\"\" loading=\"lazy\">\u003C\u002Fp>\n\u003Ch2 id=\"retrospective\">Retrospective\u003C\u002Fh2>\n\u003Cp>When I first found the API key, I thought only about removing the immediate problem. Establishing a new check that would prevent the same problem from recurring was a separate task.\u003C\u002Fp>\n\u003Cp>There was a lot to consider: whether the system could operate reliably for a long time in an enterprise environment, and how to convince team members that the new process was necessary. SonarQube was unfamiliar to everyone, so I explained the changed deployment flow and the Quality Gate thresholds in detail. I also documented the restart procedure needed during a failure, focusing on making the information part of the team’s shared knowledge.\u003C\u002Fp>\n\u003Cp>It may seem that having the system work now is enough. But it needs to keep working for a long time even after the person who introduced it is gone. A system that the company cannot maintain becomes an operational risk. Building or introducing something new also seems to come with responsibility for it.\u003C\u002Fp>\n\u003Cp>Thank you for reading this long article.\u003C\u002Fp>\n"]