<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Christian Winnen</title>
    <description>The latest articles on DEV Community by Christian Winnen (@chriswinnen).</description>
    <link>https://dev.to/chriswinnen</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3224096%2Fcab6154d-57f6-44c9-af04-f8bc86780a65.jpeg</url>
      <title>DEV Community: Christian Winnen</title>
      <link>https://dev.to/chriswinnen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chriswinnen"/>
    <language>en</language>
    <item>
      <title>You've leaked a secret in your git repository - now what?</title>
      <dc:creator>Christian Winnen</dc:creator>
      <pubDate>Fri, 30 May 2025 02:40:00 +0000</pubDate>
      <link>https://dev.to/chriswinnen/youve-leaked-a-secret-in-your-git-repository-now-what-2od6</link>
      <guid>https://dev.to/chriswinnen/youve-leaked-a-secret-in-your-git-repository-now-what-2od6</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Prevention&lt;/li&gt;
&lt;li&gt;Reaction&lt;/li&gt;
&lt;li&gt;Personal experience&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Unintentional &lt;strong&gt;secrets leaks&lt;/strong&gt; in git repositories or any version control for that matter, are one of the most prevalent root causes for initial access of threat actors. &lt;br&gt;
According to the &lt;em&gt;State of Secret Sprawls 2025&lt;/em&gt; report&lt;sup id="fnref1"&gt;1&lt;/sup&gt;, GitGuardian detected almost &lt;strong&gt;24 million secrets&lt;/strong&gt; in public GitHub commits in 2024!&lt;br&gt;
Examples of &lt;em&gt;secrets&lt;/em&gt; are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Access Keys,&lt;/li&gt;
&lt;li&gt;API tokens, &lt;/li&gt;
&lt;li&gt;a username:password combination,&lt;/li&gt;
&lt;li&gt;encryption keys,&lt;/li&gt;
&lt;li&gt;or anything you define as "&lt;em&gt;secret&lt;/em&gt;" which should not be made public&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the following blog post I will explore different options of preventing leaking secrets in the first place and what to do when you were unable to prevent it.&lt;/p&gt;



&lt;p&gt;&lt;a id="prevention"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Prevention
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"An ounce of prevention is worth a pound of cure"&lt;/em&gt; - Benjamin Franklin, 1736&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Although Benjamin Franklin famously used that proverb to advise fire-threatened Philadelphians, it holds true in the context of IT Security hygiene. When you can prevent your secrets from becoming public you don't have to deal with the aftermath of leaked secrets. The best way to prevent publishing your secrets is to &lt;strong&gt;detect&lt;/strong&gt; them in your code - this is called &lt;strong&gt;secret detection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There is an abundance of commercial and free secret detection tools out there. OWASP recommends&lt;sup id="fnref2"&gt;2&lt;/sup&gt; the following Secret Detection tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.gitguardian.com" rel="noopener noreferrer"&gt;GitGuardian&lt;/a&gt; - Commercial product that also offers a free version&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/gitleaks/gitleaks" rel="noopener noreferrer"&gt;gitleaks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/SAP/credential-digger" rel="noopener noreferrer"&gt;SAP's Credential Dig'ger&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/trufflesecurity/trufflehog" rel="noopener noreferrer"&gt;Truffle Hog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yelp/detect-secrets" rel="noopener noreferrer"&gt;Yelp's detect-secrets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.arnica.io/use-cases/hard-code-secrets" rel="noopener noreferrer"&gt;Arnica&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A "good" secret detection tool should offer the following capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Extensive Secret Type Coverage&lt;/strong&gt;: Look for tools that spare you the effort of writing custom RegEx for secret detection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alerting workflow&lt;/strong&gt;: When your tool does detect a leaked secret you want to be notified immediately via your preferred communication method. Think e-mail, Slack messages, JIRA tickets, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration and Workflow Compatibility&lt;/strong&gt;: It should integrate seamlessly into your Version Control Systems (VCS), CI/CD pipelines, and IDE. Wouldn't it be nice if your tool could scan for secrets before you &lt;code&gt;git push&lt;/code&gt; or even &lt;code&gt;git commit&lt;/code&gt;? Check out the &lt;a href="https://pre-commit.com" rel="noopener noreferrer"&gt;pre-commit&lt;/a&gt; framework.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whitelisting and approval&lt;/strong&gt;: Sometimes you may want to intentionally allow secrets to be made public. Your tool should be able to handle such edge-cases to allow and/or ignore a secret occurrence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've been using GitGuardian in both work-related as well as my own projects and can highly recommend it.&lt;/p&gt;



&lt;p&gt;&lt;a id="reaction"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Reaction
&lt;/h2&gt;


  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsk1rlns6y32qvrkf1h6s.jpg" alt="Mulan meme about API token" width="800" height="489"&gt;Once you've leaked your secret you can't put the genie back in the bottle.
  


&lt;p&gt;Despite all of these tools and awareness for security best practices it can still happen that you accidentally leak a secret - we're all human after all. Here's what you should do in that case.&lt;/p&gt;
&lt;h3&gt;
  
  
  0. Don't panic
&lt;/h3&gt;

&lt;p&gt;Discovering an incident where you've compromised secret information by publishing it publicly can and will be stressful. However, it's crucial not to panic but instead &lt;strong&gt;remain calm and take swift but planned action&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Rotate or revoke the secret
&lt;/h3&gt;

&lt;p&gt;Once you've published a secret to a public git repository you should &lt;strong&gt;assume breach&lt;/strong&gt;, meaning that you should treat the secret as compromised no matter if it's only been online for a few minutes. Today's threat actors use sophisticated tools and techniques that allow them to move at rapid speed once they've discovered valid credentials. Chris Farris has done some research&lt;sup id="fnref3"&gt;3&lt;/sup&gt; on how long it takes for public AWS Access Keys to be discovered and actively used for nefarious activities.&lt;/p&gt;

&lt;p&gt;That's why you should immediately rotate or revoke the secret. Your approach will vary depending on the type of secret involved in the compromise. Sometimes it's possible to "&lt;em&gt;rotate&lt;/em&gt;" a secret (e.g. changing a password for a user). Other times you can only "&lt;em&gt;revoke&lt;/em&gt;", "&lt;em&gt;delete&lt;/em&gt;", or "&lt;em&gt;invalidate&lt;/em&gt;" a secret.&lt;/p&gt;

&lt;p&gt;The goal is to make the leaked secret &lt;strong&gt;unusable&lt;/strong&gt; for potential attackers.&lt;/p&gt;
&lt;h3&gt;
  
  
  Clean up your git history (optional)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Leaking secrets in public repositories on GitHub and then removing them, is just like accidentally posting an embarrassing tweet, deleting it and just hoping no one saw it or took a screenshot.&lt;sup id="fnref4"&gt;4&lt;/sup&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You may be tempted to simply "&lt;em&gt;overwrite&lt;/em&gt;" a leaked secret by committing and pushing updated versions of your code to your git repository. However, Git is designed to keep an entire history of your work. And that also includes your commit history where attackers could still find an older version of your code that contains a leaked secret.&lt;/p&gt;

&lt;p&gt;Once you've revoked your secret, it cannot be used anymore. Nonetheless, exposing secrets in your version control looks unprofessional and might raise concerns (even when they are expired). That's why you should consider &lt;strong&gt;rewriting your git history&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One tool to efficiently rewrite your git history is &lt;code&gt;git-repo-filter&lt;/code&gt;. It is also what GitHub recommends &lt;sup id="fnref5"&gt;5&lt;/sup&gt; when removing sensitive data from a repository.&lt;br&gt;
I've listed the steps to rewrite your git history below.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 0: Install &lt;code&gt;git-filter-repo&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;If you haven't already, you can follow the instructions in the official &lt;a href="https://github.com/newren/git-filter-repo/blob/main/INSTALL.md" rel="noopener noreferrer"&gt;installation guide&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 1: Backup your repository
&lt;/h4&gt;

&lt;p&gt;Seriously, copy your entire Git repository to another location. If something goes wrong, you can always revert to this backup. &lt;/p&gt;
&lt;h4&gt;
  
  
  Step 2: Ensure a clean working directory
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;git-filter-repo&lt;/code&gt; requires a clean working directory and no uncommitted changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have anything uncommitted, stash it (&lt;code&gt;git stash&lt;/code&gt;) or commit it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3: Remove all remote origins (temporarily)
&lt;/h4&gt;

&lt;p&gt;This prevents accidental pushing of the rewritten history to your remote before you're ready.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote remove origin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll add it back later.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 4: Constructing the &lt;code&gt;git-filter-repo&lt;/code&gt; command
&lt;/h4&gt;

&lt;p&gt;Let's say you want to replace a specific string (&lt;code&gt;find&lt;/code&gt;) with another string (&lt;code&gt;replace&lt;/code&gt;) from a specific file (&lt;code&gt;file_name&lt;/code&gt;) in all commits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git filter-repo &lt;span class="nt"&gt;--path&lt;/span&gt; file_name &lt;span class="nt"&gt;--replace-text&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'find==&amp;gt;replace'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down this command&lt;sup id="fnref6"&gt;6&lt;/sup&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git filter-repo&lt;/code&gt;: The command to invoke the tool.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--path file_name&lt;/code&gt;: This tells &lt;code&gt;git-filter-repo&lt;/code&gt; to focus its operation only on the &lt;code&gt;file_name&lt;/code&gt; file. This is crucial because it makes the operation faster and safer, as it won't touch other files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--replace-text&lt;/code&gt;: This is the core action. It instructs it to perform text replacement. It expects a file (or standard input) where each line defines a &lt;code&gt;find==&amp;gt;replace&lt;/code&gt; pattern.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;(echo 'find==&amp;gt;replace')&lt;/code&gt;: In essence, this dynamically creates a temporary "file" containing the single line &lt;code&gt;find==&amp;gt;replace&lt;/code&gt; and passes its path to the &lt;code&gt;--replace-text&lt;/code&gt; option. This avoids having to create a separate text file (&lt;code&gt;replace_rules.txt&lt;/code&gt;) just for this one rule.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 5: Re-add your remote origin
&lt;/h4&gt;

&lt;p&gt;After you've run the command, your local history is rewritten. You need to re-establish the connection to your remote repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote add origin https://&amp;lt;URL_TO_YOUR_REMOTE_GIT&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Force Push the rewritten history
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;CAUTION&lt;/strong&gt; 🚨&lt;br&gt;
Rewriting Git history is a powerful operation that fundamentally alters the project's commit log, leading to &lt;strong&gt;disrupted workflows for collaborators who will need to re-clone or force update their repositories, and potential data loss if not executed carefully.&lt;/strong&gt; Inform any collaborators before performing this step, as they will need to re-clone or perform complex Git operations on their end. It can also complicate auditing and make it harder to trace the true lineage of changes, as commit hashes will change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the most critical step and requires overwriting the remote history.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push &lt;span class="nt"&gt;--force-with-lease&lt;/span&gt; &lt;span class="nt"&gt;--set-upstream&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--force-with-lease&lt;/code&gt;: This is a safer form of force push. It will only push if the remote branch has not changed since you last fetched it. This prevents overwriting changes made by someone else in the interim.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--set-upstream origin main&lt;/code&gt;: This tells Git that your local &lt;code&gt;main&lt;/code&gt; branch should track the &lt;code&gt;main&lt;/code&gt; branch on the &lt;code&gt;origin&lt;/code&gt; remote.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 7: Verify the changes
&lt;/h4&gt;

&lt;p&gt;Finally, confirm that the sensitive information has been successfully removed from your repository's history.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Online Verification: Go to your GitHub repository on the web, navigate to the file that contained the secret, and click on "History" or "Blame." You should see the line replaced in all commits.&lt;/li&gt;
&lt;li&gt;New Clone Test: Clone your repository into a fresh, empty directory and inspect older commits (e.g., &lt;code&gt;git log --oneline&lt;/code&gt;, then &lt;code&gt;git show &amp;lt;old-commit-sha&amp;gt;:providers.tf&lt;/code&gt;) to ensure the token is no longer present anywhere in the history.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Monitor your logs for suspicious activities
&lt;/h3&gt;

&lt;p&gt;As with any security incident, it's important to monitor for suspicious activities. Attackers frequently leverage compromised credentials to gain unauthorized access, move laterally within your systems, or exfiltrate sensitive data. By scrutinizing authentication logs, API activity, and access patterns for anomalies – such as logins from unusual locations, unexpected API calls, or resource creation outside of normal operations – you can quickly identify and respond to any post-compromise activity, limiting potential damage and preventing further breaches.&lt;/p&gt;




&lt;p&gt;&lt;a id="personal_experience"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Personal experience
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"By three methods we may learn wisdom: First, by reflection, which is noblest; Second, by imitation, which is easiest; and third by experience, which is the bitterest."&lt;/em&gt; - Confucius&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've recently chosen the bitterest option and learned wisdom by experience.&lt;/p&gt;

&lt;p&gt;I had hardcoded (&lt;em&gt;bad...&lt;/em&gt;) the &lt;code&gt;api_token&lt;/code&gt; of my CloudFlare terraform provider configuration in my &lt;code&gt;providers.tf&lt;/code&gt; and then pushed it to a public GitHub repository (&lt;em&gt;worse...&lt;/em&gt;). Here's what I did in response to that:&lt;/p&gt;

&lt;h3&gt;
  
  
  I revoked my CloudFlare API Token
&lt;/h3&gt;

&lt;p&gt;Luckily, Cloudflare offers the option to &lt;em&gt;roll&lt;/em&gt; an existing API token. This essentially &lt;strong&gt;invalidates&lt;/strong&gt; the "old" API token. It also has the benefit of keeping the existing permission scopes. &lt;/p&gt;

&lt;p&gt;CloudFlare offers an &lt;a href="https://developers.cloudflare.com/api/resources/accounts/subresources/tokens/methods/verify/" rel="noopener noreferrer"&gt;API&lt;/a&gt; to verify whether a token works. After invalidating my leaked token I made sure that it would be unusable for anyone who may have discovered it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.cloudflare.com/client/v4/accounts/&amp;lt;MY_ACCOUNT_ID&amp;gt;/tokens/verify"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer abcd-1234-efgh-56789"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response I got validated that the API Token is no longer working:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"errors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invalid API Token"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I could have stopped here, since the published API token was now rendered useless. However, I decided to go the extra mile and apply my lessons-learnt.&lt;/p&gt;

&lt;h3&gt;
  
  
  I changed my git history
&lt;/h3&gt;

&lt;p&gt;As described in the previous section, I used &lt;code&gt;git-filter-repo&lt;/code&gt; to replace my hardcoded API token throughout my entire git history.&lt;/p&gt;

&lt;p&gt;For my example I used the following command to replace the API token (&lt;code&gt;abcd-1234-efgh-56789&lt;/code&gt;) with a redacted version (&lt;code&gt;REMOVED_FROM_GIT_HISTORY&lt;/code&gt;) in the &lt;code&gt;providers.tf&lt;/code&gt; file in my entire git history:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git filter-repo &lt;span class="nt"&gt;--path&lt;/span&gt; providers.tf &lt;span class="nt"&gt;--replace-text&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'abcd-1234-efgh-56789==&amp;gt;REMOVED_FROM_GIT_HISTORY'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After performing all the necessary steps I was able to verify that my commit history no longer showed the hardcoded API token, but a redacted value instead:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpupjzkjv2demjoz54pt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpupjzkjv2demjoz54pt.png" alt="The old commits no longer show a hardcoded API token." width="800" height="297"&gt;&lt;/a&gt;&lt;br&gt;It is possible to rewrite history - with a bit of extra work.
  &lt;/p&gt;

&lt;h3&gt;
  
  
  I set up ggshield, pre-commit &amp;amp; pre-push secret scanning, and installed the GitGuardian Visual Studio Code extension
&lt;/h3&gt;

&lt;p&gt;To avoid accidentally leaking secrets in the future, I went ahead and implemented what's called "Shift Left Security".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Shift-left security is the practice of performing code and software security assurance processes as early as possible in the software development lifecycle (SDLC).&lt;/em&gt; - definition from the Wiz Academy&lt;sup id="fnref7"&gt;7&lt;/sup&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Setting up ggshield locally
&lt;/h3&gt;

&lt;p&gt;ggshield&lt;sup id="fnref8"&gt;8&lt;/sup&gt; is the command line tool for the GitGuardian secret detection engine. You can use it to search for secrets in files, repositories, Docker images and more.&lt;br&gt;
Below is an example of scanning a specific file (&lt;code&gt;providers.tf&lt;/code&gt;) for secrets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ggshield secret scan path providers.tf                                       
Scanning... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 1 / 1

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; providers.tf: 1 secret detected

&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; Secret detected: Generic High Entropy Secret
   Validity: No Checker
   Occurrences: 1
   Known by GitGuardian dashboard: NO
   Incident URL: N/A
   Secret SHA: 3e56b8b21a7e2846a37c797a5115918092442c161e47c067dc8894dd29868e58
   Detector documentation: https://docs.gitguardian.com/secrets-detection/secrets-detection-engine/detectors/generics/generic_high_entropy_secret

12 | &lt;span class="c"&gt;# Hardcoded credentials for initial test&lt;/span&gt;
13 | provider &lt;span class="s2"&gt;"cloudflare"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
14 |   api_token &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"abcd*********************-****6789"&lt;/span&gt;
                    |_____________apikey_____________|
15 | &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows that &lt;code&gt;ggshield&lt;/code&gt; was able to detect my hardcoded API token and alert me on it. That's a good start! However, having to manually run the secret detection tool is not a great "Developer Experience".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;TIP&lt;/strong&gt; 💡 &lt;br&gt;
Running &lt;code&gt;ggshield&lt;/code&gt; will create a local cache of the detected secrets (&lt;code&gt;.cache_ggshield&lt;/code&gt;). You may want to add this to your &lt;code&gt;.gitignore&lt;/code&gt; to prevent it from being accidentally committed to your version control system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Setting up pre-commit and pre-push scans
&lt;/h3&gt;

&lt;p&gt;GitGuardian offers a way to automatically scan your code for secrets &lt;strong&gt;before&lt;/strong&gt; your commits and/or pushes. That means you don't have to explicitly invoke &lt;code&gt;ggshield&lt;/code&gt; manually to find the secrets in your code.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pre-commit hook
&lt;/h4&gt;

&lt;p&gt;A pre-commit hook is a client-side git hook that runs right before the commit is created. &lt;/p&gt;

&lt;p&gt;To enable pre-commit scanning, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ggshield &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--mode&lt;/span&gt; global
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitGuardian will now warn and prevent you from commiting code that contains secrets. In the example below, I tried to commit the &lt;code&gt;providers.tf&lt;/code&gt; file which contained the hardcoded API Token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="s2"&gt;"Hardcoded the api_token"&lt;/span&gt;
Scanning... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 1 / 1

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; commit://staged/providers.tf: 1 secret detected

&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; Secret detected: Generic High Entropy Secret
   Validity: No Checker
   Occurrences: 1
   Known by GitGuardian dashboard: NO
   Incident URL: N/A
   Secret SHA: 3e56b8b21a7e2846a37c797a5115918092442c161e47c067dc8894dd29868e58
   Secret &lt;span class="k"&gt;in &lt;/span&gt;Secrets Manager: False
   Detector documentation: https://docs.gitguardian.com/secrets-detection/secrets-detection-engine/detectors/generics/generic_high_entropy_secret

      | &lt;span class="se"&gt;\ &lt;/span&gt;No newline at end of file
   12 | +# Hardcoded credentials &lt;span class="k"&gt;for &lt;/span&gt;initial &lt;span class="nb"&gt;test
   &lt;/span&gt;13 | +provider &lt;span class="s2"&gt;"cloudflare"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   14 | +  api_token &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"abcd*********************-****6789"&lt;/span&gt;
                        |_____________apikey_____________|
   15 | +&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; How to remediate

  Since the secret was detected before the commit was made:
  1. replace the secret with its reference &lt;span class="o"&gt;(&lt;/span&gt;e.g. environment variable&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
  2. commit again.

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;Apply with caution] If you want to bypass ggshield &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;positive or other reason&lt;span class="o"&gt;)&lt;/span&gt;, run:
  - &lt;span class="k"&gt;if &lt;/span&gt;you use the pre-commit framework:

    &lt;span class="nv"&gt;SKIP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ggshield git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;your message&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's examine this in more detail:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitGuardian &lt;strong&gt;rejected&lt;/strong&gt; my commit because it found a secret&lt;/li&gt;
&lt;li&gt;It &lt;strong&gt;highlighted&lt;/strong&gt; where the secret was found in my code (&lt;code&gt;Line 14&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;It &lt;strong&gt;suggested remediation steps&lt;/strong&gt; (&lt;code&gt;replce the secret with environment variable&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;It showed how to &lt;strong&gt;ignore&lt;/strong&gt; the secret finding (by adding&lt;code&gt;SKIP=ggshield&lt;/code&gt; before the &lt;code&gt;git commit&lt;/code&gt; command)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Pre-push hook
&lt;/h4&gt;

&lt;p&gt;A pre-push hook is a client-side git hook that runs right before a reference is pushed to a remote (git push). &lt;/p&gt;

&lt;p&gt;To enable pre-push scanning, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ggshield &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--mode&lt;/span&gt; global &lt;span class="nt"&gt;-t&lt;/span&gt; pre-push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitGuardian will now warn and prevent you from pushing code that contains secrets. In the example below, I tried to push the &lt;code&gt;providers.tf&lt;/code&gt; file which contained the hardcoded API Token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push
Scanning... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 1 / 1

commit d6803caac9883af4ab4108fe037553313afa3922
Date: Fri May 30 10:53:00 2025 +1000

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; commit://d6803caac9883af4ab4108fe037553313afa3922/providers.tf: 1 secret detected

&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; Secret detected: Generic High Entropy Secret
   Validity: No Checker
   Occurrences: 1
   Known by GitGuardian dashboard: NO
   Incident URL: N/A
   Secret SHA: 3e56b8b21a7e2846a37c797a5115918092442c161e47c067dc8894dd29868e58
   Secret &lt;span class="k"&gt;in &lt;/span&gt;Secrets Manager: False
   Detector documentation: https://docs.gitguardian.com/secrets-detection/secrets-detection-engine/detectors/generics/generic_high_entropy_secret

      | &lt;span class="se"&gt;\ &lt;/span&gt;No newline at end of file
   12 | +# Hardcoded credentials &lt;span class="k"&gt;for &lt;/span&gt;initial &lt;span class="nb"&gt;test
   &lt;/span&gt;13 | +provider &lt;span class="s2"&gt;"cloudflare"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   14 | +  api_token &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"abcd*********************-****6789"&lt;/span&gt;
                        |_____________apikey_____________|
   15 | +&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; How to remediate

  Since the secret was detected before the push BUT after the commit, you need to:
  1. rewrite the git &lt;span class="nb"&gt;history &lt;/span&gt;making sure to replace the secret with its reference &lt;span class="o"&gt;(&lt;/span&gt;e.g. environment variable&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
  2. push again.

  To prevent having to rewrite git &lt;span class="nb"&gt;history &lt;/span&gt;&lt;span class="k"&gt;in &lt;/span&gt;the future, setup ggshield as a pre-commit hook:
    https://docs.gitguardian.com/ggshield-docs/integrations/git-hooks/pre-commit

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;Apply with caution] If you want to bypass ggshield &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;positive or other reason&lt;span class="o"&gt;)&lt;/span&gt;, run:
  - &lt;span class="k"&gt;if &lt;/span&gt;you use the pre-commit framework:

    &lt;span class="nv"&gt;SKIP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ggshield-push git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's examine this in more detail:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitGuardian &lt;strong&gt;rejected&lt;/strong&gt; my push because it found a secret.&lt;/li&gt;
&lt;li&gt;It &lt;strong&gt;highlighted&lt;/strong&gt; where the secret was found in my code (in commit &lt;code&gt;d6803caac9883af4ab4108fe037553313afa3922&lt;/code&gt; on &lt;code&gt;Line 14&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;It &lt;strong&gt;suggested remediation steps&lt;/strong&gt; (&lt;code&gt;replce the secret with environment variable&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;It showed how to &lt;strong&gt;ignore&lt;/strong&gt; the secret finding (by adding&lt;code&gt;SKIP=ggshield&lt;/code&gt; before the &lt;code&gt;git push&lt;/code&gt; command).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setting up the Visual Studio Code integration
&lt;/h3&gt;

&lt;p&gt;GitGuardian also offers an integration for Visual Studio Code (VSC)&lt;sup id="fnref9"&gt;9&lt;/sup&gt;.&lt;br&gt;
Once you've installed the VSC integration, GitGuardian will highlight it in your editor:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9jz8ct25nggzu0hif09w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9jz8ct25nggzu0hif09w.png" alt="Example of a highlighted secret that was detected" width="800" height="306"&gt;&lt;/a&gt;&lt;br&gt;GitGuardian will highlight all the secrets it finds in your code.
  &lt;/p&gt;

&lt;h2&gt;
  
  
  The easiest method of learning wisdom
&lt;/h2&gt;

&lt;p&gt;My recent experience was a bitter lesson, but one that ultimately strengthened my security posture and reinforced the importance of proactive defense. &lt;/p&gt;

&lt;p&gt;You don't have to learn this wisdom the "bitter" way yourself. You can use Confucius's easiest method of learning wisdom (&lt;em&gt;"imitation"&lt;/em&gt;) by taking action today:&lt;/p&gt;

&lt;p&gt;Integrate a secret detection tool like GitGuardian into your workflow and set up those crucial pre-commit and pre-push hooks. It’s a small effort that yields monumental security benefits, ensuring your secrets stay secret.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://www.gitguardian.com/state-of-secrets-sprawl-report-2025" rel="noopener noreferrer"&gt;The State of Secrets Sprawl 2025&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://owasp.org/www-community/Free_for_Open_Source_Application_Security_Tools" rel="noopener noreferrer"&gt;OWASP's free for Open Source Application Security Tools&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;a href="https://www.chrisfarris.com/post/public-access-key-2023/" rel="noopener noreferrer"&gt;Public Access Keys - 2023&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;&lt;a href="https://blog.gitguardian.com/leaking-secrets-on-github-what-to-do/" rel="noopener noreferrer"&gt;Exposing secrets on GitHub: What to do after leaking credentials and API keys&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;Check out &lt;a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository" rel="noopener noreferrer"&gt;Removing sensitive data from a repository&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;Check out the &lt;a href="https://htmlpreview.github.io/?https://github.com/newren/git-filter-repo/blob/docs/html/git-filter-repo.html" rel="noopener noreferrer"&gt;git-repo-filter manual&lt;/a&gt; for more details. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;Check out &lt;a href="https://www.wiz.io/academy/shift-left-security" rel="noopener noreferrer"&gt;Shift Left Explained: What It Means to Shift Security Left&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn8"&gt;
&lt;p&gt;&lt;a href="https://www.gitguardian.com/ggshield" rel="noopener noreferrer"&gt;ggshield documentation&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn9"&gt;
&lt;p&gt;You can install it from the &lt;a href="https://marketplace.visualstudio.com/items?itemName=gitguardian-secret-security.gitguardian" rel="noopener noreferrer"&gt;Visual Studio Marketplace&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>shiftleft</category>
      <category>devsecops</category>
      <category>security</category>
    </item>
  </channel>
</rss>
