<?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: Rxkov</title>
    <description>The latest articles on DEV Community by Rxkov (@rxkov).</description>
    <link>https://dev.to/rxkov</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F325163%2F24081e6e-e784-4ede-8762-5156ae7a1dc7.jpg</url>
      <title>DEV Community: Rxkov</title>
      <link>https://dev.to/rxkov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rxkov"/>
    <language>en</language>
    <item>
      <title>Username OSINT Is a Graph of Attested Pages</title>
      <dc:creator>Rxkov</dc:creator>
      <pubDate>Sat, 03 Oct 2026 22:18:33 +0000</pubDate>
      <link>https://dev.to/rxkov/username-osint-is-a-graph-of-attested-pages-48j6</link>
      <guid>https://dev.to/rxkov/username-osint-is-a-graph-of-attested-pages-48j6</guid>
      <description>&lt;p&gt;A hunt that stops at &lt;code&gt;https://twitter.com/{handle}&lt;/code&gt; is a template. It is not evidence. The page may 404. The same handle may belong to three people. A desk that stores that URL as a node is lying to the operator.&lt;/p&gt;

&lt;p&gt;MAGO ships username hunts as an identity graph on &lt;a href="https://mago.team" rel="noopener noreferrer"&gt;mago.team&lt;/a&gt;. Nodes are attested pages. Edges are same-person relations pulled from JSON the platforms already publish.&lt;/p&gt;

&lt;h2&gt;
  
  
  Templates are not identity
&lt;/h2&gt;

&lt;p&gt;HEAD requests against a guessed profile URL tell you the host answered. They do not tell you the person exists. GitHub, GitLab, Reddit, Hacker News, and Keybase each expose a JSON document for a named account. That document is the record. The HTML chrome is decoration.&lt;/p&gt;

&lt;p&gt;GitHub's user payload includes &lt;code&gt;blog&lt;/code&gt;, &lt;code&gt;twitter_username&lt;/code&gt;, and &lt;code&gt;social_accounts&lt;/code&gt;. Those fields are the operator's next hops. They are not guessed. They are claimed by the account owner on GitHub's API.&lt;/p&gt;

&lt;p&gt;GitLab users, Reddit &lt;code&gt;about.json&lt;/code&gt;, HN Algolia hits, and Keybase proofs follow the same rule. Fetch the document. Parse fields. Keep the URL only if the document names the handle.&lt;/p&gt;

&lt;h2&gt;
  
  
  The documents you actually fetch
&lt;/h2&gt;

&lt;p&gt;GitHub: &lt;code&gt;GET https://api.github.com/users/{handle}&lt;/code&gt;. Read &lt;code&gt;login&lt;/code&gt;, &lt;code&gt;blog&lt;/code&gt;, &lt;code&gt;twitter_username&lt;/code&gt;, &lt;code&gt;html_url&lt;/code&gt;. Then &lt;code&gt;GET /users/{handle}/social_accounts&lt;/code&gt; for &lt;code&gt;{provider, url}&lt;/code&gt; pairs. Dev.to also publishes &lt;code&gt;GET https://dev.to/api/users/by_username?url={handle}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;GitLab: &lt;code&gt;GET https://gitlab.com/api/v4/users?username={handle}&lt;/code&gt;. The first object is the person if the list is not empty. Website and bio sit on that object.&lt;/p&gt;

&lt;p&gt;Reddit: &lt;code&gt;GET https://www.reddit.com/user/{handle}/about.json&lt;/code&gt;. The live handle is &lt;code&gt;data.name&lt;/code&gt;. Subreddit title and public description are extra labels, not extra people.&lt;/p&gt;

&lt;p&gt;Hacker News: &lt;code&gt;GET https://hn.algolia.com/api/v1/users/{handle}&lt;/code&gt;. A 404 is a missing user. A hit is a named account with karma and about text.&lt;/p&gt;

&lt;p&gt;Keybase: &lt;code&gt;GET https://keybase.io/_/api/1.0/user/lookup.json?usernames={handle}&lt;/code&gt;. Proofs in that payload name GitHub, X, Reddit, and a website. Those proofs are the edges.&lt;/p&gt;

&lt;p&gt;None of these calls need a browser. None of them need a 200 from &lt;code&gt;twitter.com/{handle}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A HEAD probe misses four things. First, a parked profile that still 200s. Second, a renamed handle whose old URL 301s to someone else. Third, a display name that is not the login. Fourth, social links that live only in JSON. The graph records the document. It does not record the folklore.&lt;/p&gt;

&lt;h2&gt;
  
  
  Same person is a relation, not a vibe
&lt;/h2&gt;

&lt;p&gt;Two nodes share a person when a document on one platform names the other. GitHub &lt;code&gt;twitter_username&lt;/code&gt; pointing at X. A Keybase proof pointing at GitHub. A blog URL that matches a GitLab website field.&lt;/p&gt;

&lt;p&gt;Confidence is not a slider. It is whether the source page was fetched and parsed. MAGO stores that as an attested page record on the investigation. The graph is the present step of collect, extract, correlate, present.&lt;/p&gt;

&lt;p&gt;A list of green ticks is a scanner. An investigation is a workspace that keeps those relations next to the hunt that produced them.&lt;/p&gt;

&lt;p&gt;Sherlock-style checkers enumerate sites. MAGO correlates claimed accounts. Enumeration is a list. Correlation is a graph. Operators already have lists. They need the second thing on a desk that already holds the hunt. The hunt that produced the GitHub node is the same hunt that billed milliscale credits. The graph does not fork that billed order.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the desk actually does
&lt;/h2&gt;

&lt;p&gt;Open an investigation on mago.team. Name it. Hunt a username from that case. The scan still quotes, queues, and writes a report. The identity graph is extra surface on the same order: nodes for GitHub, GitLab, Reddit, HN, Keybase when the JSON exists.&lt;/p&gt;

&lt;p&gt;Edges carry labels such as same-handle or claimed-account. The canvas lives in the investigation workspace. It is not a marketing force graph on the home page.&lt;/p&gt;

&lt;p&gt;The hunt bar stays hunt. Creating a case is a title and Open. Mixing those verbs is how desks turn into SaaS forms.&lt;/p&gt;

&lt;p&gt;Credits still debit by module. Email hunts and domain hunts keep their own spell costs. Username identity is one more surface on the same milliscale wallet. You do not buy a second product to keep the person.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this belongs on mago.team
&lt;/h2&gt;

&lt;p&gt;Operators buy a hunt, then they need a place to keep the person they found. A report is a snapshot. An investigation is the file that survives the next pivot.&lt;/p&gt;

&lt;p&gt;Run the next username from &lt;a href="https://mago.team" rel="noopener noreferrer"&gt;https://mago.team&lt;/a&gt;. Open an investigation first. Let the graph fill from attested pages, not from URL folklore.&lt;/p&gt;

</description>
      <category>osint</category>
      <category>username</category>
      <category>identity</category>
      <category>security</category>
    </item>
    <item>
      <title>An Email Is a Hash, a Commit, and a Mailbox Policy</title>
      <dc:creator>Rxkov</dc:creator>
      <pubDate>Tue, 29 Sep 2026 20:45:48 +0000</pubDate>
      <link>https://dev.to/rxkov/an-email-is-a-hash-a-commit-and-a-mailbox-policy-35ih</link>
      <guid>https://dev.to/rxkov/an-email-is-a-hash-a-commit-and-a-mailbox-policy-35ih</guid>
      <description>&lt;p&gt;Most email OSINT tutorials start at a breach index. Start at the mailbox string itself. Three public facts fall out of one address before anyone pays for a dump.&lt;/p&gt;

&lt;p&gt;The local part plus the domain is a Gravatar hash. Git stores the same string as a commit author. The domain publishes SPF and DMARC. Together they tell you who the person is, where they commit, and whether anyone can spoof the name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hash the mailbox, do not guess the face
&lt;/h2&gt;

&lt;p&gt;Gravatar still keys profiles on a digest of the trimmed, lowercased address. Automattic documents the identifier in the &lt;a href="https://docs.gravatar.com/rest/hash/" rel="noopener noreferrer"&gt;REST hash guide&lt;/a&gt;. The public JSON is &lt;code&gt;https://en.gravatar.com/{hash}.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;

&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user@example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://en.gravatar.com/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;entry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;displayName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;preferredUsername&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;thumbnailUrl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A 200 with &lt;code&gt;displayName&lt;/code&gt; is a public card. Display name, username, about text, and linked accounts are opt-in. A 404 means no public profile for that hash. It does not mean the mailbox is fake.&lt;/p&gt;

&lt;p&gt;hashtray and similar tools reverse the hash by generating candidate addresses from the public card and hashing them until one matches. That is a dictionary attack on a public MD5. It is not a Gravatar "decrypt". Treat a reversed mailbox as a hypothesis until another source repeats it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Git already published the author
&lt;/h2&gt;

&lt;p&gt;Every Git commit embeds &lt;code&gt;author&lt;/code&gt; and &lt;code&gt;committer&lt;/code&gt; as name plus email. GitHub shows that metadata on public repos unless the user enabled email privacy. The GitHub API exposes it on commit objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://api.github.com/search/commits?q=author-email:user@example.com
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Accept header must be &lt;code&gt;application/vnd.github+json&lt;/code&gt;. A personal token raises the rate limit. Hits return repo, SHA, and the author login GitHub attached to that email.&lt;/p&gt;

&lt;p&gt;GitHub documents two noreply forms. &lt;code&gt;ID+login@users.noreply.github.com&lt;/code&gt; and &lt;code&gt;login@users.noreply.github.com&lt;/code&gt;. Both name the login. A corporate mailbox on public commits is a stronger identity signal than a handle match on a signup page.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.github.com/en/rest/search/search#search-commits" rel="noopener noreferrer"&gt;commit search API&lt;/a&gt; is preview-stable under &lt;code&gt;application/vnd.github+json&lt;/code&gt;. Sort by committer-date if you need the last live use of that mailbox. A 2019 commit and a 2026 commit are different leads.&lt;/p&gt;

&lt;p&gt;Do not treat one commit as same-person proof. Shared CI bots, rewritten history, and &lt;code&gt;--author&lt;/code&gt; spoofing exist. Two repos, a Gravatar card, and a matching login is a working cluster. One hit is a lead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The domain tells you if the name can be forged
&lt;/h2&gt;

&lt;p&gt;Look up the registrable domain, not the person, for mail policy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dig +short TXT example.com
dig +short TXT _dmarc.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;v=spf1 ... -all&lt;/code&gt; is an explicit fail. &lt;code&gt;~all&lt;/code&gt; is softfail. A missing SPF record is not a pass. DMARC &lt;code&gt;p=none&lt;/code&gt; is monitor-only. &lt;code&gt;p=quarantine&lt;/code&gt; and &lt;code&gt;p=reject&lt;/code&gt; are the policies that stop spoofed From headers at receiving MTAs.&lt;/p&gt;

&lt;p&gt;A mailbox on a domain with &lt;code&gt;p=none&lt;/code&gt; can be impersonated in a phishing wave even when the person is real. That fact belongs in the identity report next to the Gravatar photo. It is not a side quest.&lt;/p&gt;

&lt;p&gt;WHOIS on the same domain is registrar data. RU-CENTER, GoDaddy, and Squarespace abuse inboxes are not the human. Put those rows in infrastructure. Keep them out of the name field.&lt;/p&gt;

&lt;h2&gt;
  
  
  One selector, three hops
&lt;/h2&gt;

&lt;p&gt;The useful first pass is boring on purpose.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hash the mailbox. Read the Gravatar JSON.&lt;/li&gt;
&lt;li&gt;Search public commits for &lt;code&gt;author-email&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Read SPF and DMARC on the domain.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Stop there if all three are empty. Pay for a breach index only when the public graph is thin and the case still needs it. Have I Been Pwned and similar services are keyed APIs. They are not the first hop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mago.team/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=email-hash" rel="noopener noreferrer"&gt;mago.team&lt;/a&gt; runs this identity pack on a pasted mailbox. Gravatar, GitHub, and mail policy sit in the same report. Credits follow the spell table. The hunt is the mailbox. The dump is optional.&lt;/p&gt;

&lt;h2&gt;
  
  
  What not to mix into the name field
&lt;/h2&gt;

&lt;p&gt;WHOIS &lt;code&gt;registrant_name&lt;/code&gt; is often the registrar. "Regional Network Information Center, JSC dba RU-CENTER" is not a human identity. Squarespace &lt;code&gt;abuse-complaints@&lt;/code&gt; and NIC.RU &lt;code&gt;tld-abuse@&lt;/code&gt; are role mailboxes. Put them under registrar. Keep them out of the person's card.&lt;/p&gt;

&lt;p&gt;GitHub handle collision is the other trap. A 200 on &lt;code&gt;https://github.com/alice&lt;/code&gt; for mailbox &lt;code&gt;alice@brand.com&lt;/code&gt; is a page, not same-person proof. The commit search with &lt;code&gt;author-email&lt;/code&gt; is the join key. The handle is a maybe.&lt;/p&gt;

&lt;p&gt;Disposable domains fail this pipeline on purpose. No Gravatar card. No corporate DMARC. No commit graph. That emptiness is the finding. Do not pad it with unconfirmed social URLs.&lt;/p&gt;

&lt;p&gt;The first-pass identity report should be short. Photo if Gravatar has one. Login if commits join. Mail policy if the domain publishes it. Everything else is a pivot with a cost.&lt;/p&gt;

</description>
      <category>osint</category>
      <category>email</category>
      <category>identity</category>
      <category>github</category>
    </item>
    <item>
      <title>GitHub Dorks Still Work When You Scope Them to One Org</title>
      <dc:creator>Rxkov</dc:creator>
      <pubDate>Tue, 29 Sep 2026 20:45:12 +0000</pubDate>
      <link>https://dev.to/rxkov/github-dorks-still-work-when-you-scope-them-to-one-org-4hk8</link>
      <guid>https://dev.to/rxkov/github-dorks-still-work-when-you-scope-them-to-one-org-4hk8</guid>
      <description>&lt;p&gt;GitGuardian counted 28,649,024 new hardcoded secrets in public GitHub commits in 2025. That is a 34 percent rise from 2024, the largest jump in their series. Teams still treat a global &lt;code&gt;filename:.env password&lt;/code&gt; search as recon. It is a firehose.&lt;/p&gt;

&lt;p&gt;The index still holds the secrets. The query has to name an org, a user, or a domain. Unscoped dorks produce a feed. Scoped dorks produce a target.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two search languages, one trap
&lt;/h2&gt;

&lt;p&gt;GitHub now ships two code-search dialects. The website uses the &lt;a href="https://docs.github.com/en/search-github/github-code-search/understanding-github-code-search-syntax" rel="noopener noreferrer"&gt;2023 code search syntax&lt;/a&gt;. Qualifiers are &lt;code&gt;path:&lt;/code&gt;, &lt;code&gt;org:&lt;/code&gt;, &lt;code&gt;repo:&lt;/code&gt;, &lt;code&gt;language:&lt;/code&gt;, and &lt;code&gt;NOT is:fork&lt;/code&gt;. &lt;code&gt;filename:&lt;/code&gt; is not in that table.&lt;/p&gt;

&lt;p&gt;The REST endpoint &lt;code&gt;GET /search/code&lt;/code&gt; still speaks the &lt;a href="https://docs.github.com/en/rest/search/search#search-code" rel="noopener noreferrer"&gt;legacy search API&lt;/a&gt;. There &lt;code&gt;filename:.env&lt;/code&gt; and &lt;code&gt;user:acme&lt;/code&gt; still work. A token is required. Rate limits are 10 requests per minute on the core search route.&lt;/p&gt;

&lt;p&gt;Paste a 2018 cheat sheet into github.com/search and the hits look empty. The operator did not die. The qualifier did. &lt;code&gt;path:/(^|\/)\.env$/&lt;/code&gt; is the current way to pin a basename. &lt;code&gt;path:*.pem&lt;/code&gt; is the glob form.&lt;/p&gt;

&lt;p&gt;mago.team &lt;code&gt;github_dorks&lt;/code&gt; talks to the REST search API. It scopes &lt;code&gt;user:{target}&lt;/code&gt; for a handle, or &lt;code&gt;"domain.tld"&lt;/code&gt; as a quoted term for a company site. The spell needs a free &lt;code&gt;GH_TOKEN&lt;/code&gt;. Without it the job is skipped, not faked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Queries that still hit in 2026
&lt;/h2&gt;

&lt;p&gt;Keep the org in every query. Drop the rest of GitHub.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;org:TARGET path:/(^|\/)\.env$/ AWS_SECRET
org:TARGET path:*.pem "BEGIN PRIVATE KEY" NOT is:fork
org:TARGET "AKIA" NOT is:fork NOT path:test
org:TARGET path:*.npmrc _authToken
org:TARGET "xoxb-" OR "xoxp-" NOT is:fork
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;REST equivalents for a script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;filename:.env AWS_SECRET user:TARGET
filename:.pem "BEGIN PRIVATE KEY" user:TARGET
AWS_SECRET "TARGET.com"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitGuardian's 2026 report put AI-assisted commits at roughly twice the leak rate of the GitHub-wide baseline. Generic secrets remain the majority class. Partner push protection still misses &lt;code&gt;DATABASE_URL&lt;/code&gt;, home-grown bearer tokens, and &lt;code&gt;.npmrc&lt;/code&gt; &lt;code&gt;_authToken&lt;/code&gt; lines.&lt;/p&gt;

&lt;p&gt;A 2025 InfoQ write-up of the prior report put generic credentials at 58 percent of detected leaks. A regex that only fires on Stripe and AWS misses most of the volume. Scoped &lt;code&gt;.env&lt;/code&gt; search still pays.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a hit is not
&lt;/h2&gt;

&lt;p&gt;A code-search hit is a public string in a default-branch blob under 384 KB. It is not proof the secret is live. It is not proof the repo is still the company's. Forks copy history. Vendored trees copy other people's keys.&lt;/p&gt;

&lt;p&gt;GitHub documents &lt;code&gt;is:fork&lt;/code&gt;, &lt;code&gt;is:archived&lt;/code&gt;, &lt;code&gt;is:vendored&lt;/code&gt;, and &lt;code&gt;is:generated&lt;/code&gt;. Exclude them on the first pass.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;org:TARGET path:/(^|\/)\.env$/ NOT is:fork NOT is:archived
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not open the credential. Rotate if it is yours. File a disclosure if it is not. Pasting a live key into a chat is a second leak.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run it as a pipeline
&lt;/h2&gt;

&lt;p&gt;A useful pass is three buckets, not 200 dorks.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Env and config: &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;.npmrc&lt;/code&gt;, &lt;code&gt;credentials&lt;/code&gt;, &lt;code&gt;docker-compose*.yml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Key material: PEM, &lt;code&gt;AKIA&lt;/code&gt;, Slack &lt;code&gt;xoxb-&lt;/code&gt;, Stripe &lt;code&gt;sk_live_&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Company string: the apex domain next to &lt;code&gt;password&lt;/code&gt; or &lt;code&gt;api_key&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Record the query, the &lt;code&gt;total_count&lt;/code&gt;, and three sample paths. That is enough to decide the next hop: rotate, disclose, or ignore.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mago.team/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=github-dorks" rel="noopener noreferrer"&gt;mago.team&lt;/a&gt; runs that pass as &lt;code&gt;github_dorks&lt;/code&gt; on a handle or org. Hits land in the same report as DNS, WHOIS, and JS. The hunt is one selector. The dorks are one spell, not a weekend of curl.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the 2018 lists still circulate
&lt;/h2&gt;

&lt;p&gt;Cheat sheets keep listing &lt;code&gt;filename:.env password&lt;/code&gt; with no org. That query searches the planet. GitHub code search ranks recent public blobs. The first page is random startups, course repos, and copied Docker samples.&lt;/p&gt;

&lt;p&gt;GitGuardian's 2026 public-relations note added two numbers that matter for hunters. Secret leak rates in AI-assisted commits ran about double the GitHub-wide baseline across 2025. MCP config files in the study exposed 24,008 unique secrets. Copilot-enabled public repos in the 2025 report leaked at 6.4 percent, against 4.6 percent overall.&lt;/p&gt;

&lt;p&gt;Those are volume stats. They do not tell you which org is yours. An unscoped dork of &lt;code&gt;AKIA&lt;/code&gt; will show AWS keys that are not in your incident. An &lt;code&gt;org:TARGET AKIA&lt;/code&gt; query either hits or it does not. That is the difference between a feed and a case.&lt;/p&gt;

&lt;p&gt;The 2025 report also said 70 percent of secrets leaked in 2022 were still valid years later. Rotation is the control. Search is how you find the copy that nobody rotated. Scope the search or you will rotate the wrong tenant.&lt;/p&gt;

&lt;p&gt;REST search indexes default branches and files under 384 KB. History-only leaks need &lt;code&gt;git log -p&lt;/code&gt; on a clone, or a tool that walks commits. Code search will not see a key that lived in one commit and was rewritten out of HEAD. Say that in the report. Do not claim "GitHub is clean" because &lt;code&gt;total_count&lt;/code&gt; was zero.&lt;/p&gt;

&lt;p&gt;Global cheat sheets will keep circulating. They search the planet. The incident is in one org.&lt;/p&gt;

</description>
      <category>security</category>
      <category>osint</category>
      <category>github</category>
      <category>secrets</category>
    </item>
    <item>
      <title>Your JS Secret Scanner Is Reading webpack Polyfills</title>
      <dc:creator>Rxkov</dc:creator>
      <pubDate>Tue, 29 Sep 2026 20:44:37 +0000</pubDate>
      <link>https://dev.to/rxkov/your-js-secret-scanner-is-reading-webpack-polyfills-46c9</link>
      <guid>https://dev.to/rxkov/your-js-secret-scanner-is-reading-webpack-polyfills-46c9</guid>
      <description>&lt;p&gt;Keyword secret scanners love the word &lt;code&gt;password&lt;/code&gt; inside JavaScript. Next.js ships a URL polyfill that parses &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt; off every URL. The password is often an empty string. The scanner still screams CRITICAL.&lt;/p&gt;

&lt;p&gt;The finding is a parser, not a credential. If the report attributes it to the target, the grade is a lie.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the empty password comes from
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://url.spec.whatwg.org/#url-password" rel="noopener noreferrer"&gt;WHATWG URL Standard&lt;/a&gt; stores userinfo as username and password. &lt;code&gt;new URL("https://example.com/path")&lt;/code&gt; has an empty password. Polyfills copy that object into the bundle so old browsers can parse URLs.&lt;/p&gt;

&lt;p&gt;Chromium and Node both expose &lt;code&gt;url.password&lt;/code&gt; as a string. Empty is valid. A scanner that flags any identifier named &lt;code&gt;password&lt;/code&gt; cannot tell a URL field from &lt;code&gt;const password = "hunter2"&lt;/code&gt;. The second is a secret. The first is the platform.&lt;/p&gt;

&lt;p&gt;Next.js has long inlined those polyfills in a &lt;code&gt;polyfills-*.js&lt;/code&gt; chunk under &lt;code&gt;/_next/static/chunks/&lt;/code&gt;. The minified code still contains the property name &lt;code&gt;password&lt;/code&gt; and the helper &lt;code&gt;cannotBeABaseURL&lt;/code&gt;. A naive &lt;code&gt;password_in_code&lt;/code&gt; rule fires on that chunk for every Next site on the internet.&lt;/p&gt;

&lt;p&gt;Google Tag Manager and Shopify pixels produce the same class of hit. A third-party shop page in urlscan mentions the target in a query string. The JS job then "finds" an OAuth client id that belongs to the shop, not the seed domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  First-party or it does not count
&lt;/h2&gt;

&lt;p&gt;A JS finding is on-target when the script URL is the apex or a subdomain of the seed. &lt;code&gt;app.example.com&lt;/code&gt; is in. &lt;code&gt;cdn.shopify.com&lt;/code&gt; is out. &lt;code&gt;accounts.google.com&lt;/code&gt; is out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_first_party&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;script_host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;apex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;script_host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;apex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drop the finding when every extracted URL in the blob sits off-apex. Keep it when the bundle is &lt;code&gt;https://example.com/_next/static/chunks/main-*.js&lt;/code&gt; and the secret is a real &lt;code&gt;AKIA&lt;/code&gt; or &lt;code&gt;sk_live_&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;Polyfill markers that have no business in a customer HUD:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cannotBeABaseURL
password:""
password\":\"\"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those strings are URL parser furniture. They are not a dumped admin password.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the HUD should show instead
&lt;/h2&gt;

&lt;p&gt;A useful JS section lists endpoints and real secrets on first-party hosts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/api/v1/forms&lt;/code&gt; on &lt;code&gt;https://example.com&lt;/code&gt; is an endpoint. INFO.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AKIA&lt;/code&gt; plus 16 more chars in &lt;code&gt;https://example.com/static/app.js&lt;/code&gt; is a key. HIGH or CRITICAL after a shape check.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;password=""&lt;/code&gt; in &lt;code&gt;polyfills-*.js&lt;/code&gt; is noise. Delete it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;mago.team &lt;code&gt;js_analyzer&lt;/code&gt; follows only HTTP 200 origins on the scanned selector. Off-target urlscan neighbours do not get queued. The report builder then drops polyfill and off-apex &lt;code&gt;js_secret&lt;/code&gt; rows so they cannot tank the aggregate score.&lt;/p&gt;

&lt;p&gt;A recon grade of A+ next to a page of CRITICAL JS hits is how you lose the operator. The polyfill was never a credential. The empty password was never a dump.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to verify on one host
&lt;/h2&gt;

&lt;p&gt;Fetch the homepage. Collect script src values. Keep hosts on the apex. Scan those bodies for key shapes, not the word &lt;code&gt;password&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AKIA[0-9A-Z]{16}
sk_live_[0-9a-zA-Z]{8,}
-----BEGIN (RSA )?PRIVATE KEY-----
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the only hit is &lt;code&gt;password&lt;/code&gt; next to &lt;code&gt;cannotBeABaseURL&lt;/code&gt;, the scanner is reading the platform. Switch the rule. Paste the same host into &lt;a href="https://mago.team/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=js-polyfill" rel="noopener noreferrer"&gt;mago.team&lt;/a&gt; and compare the JS section. The pipeline should stay quiet unless a first-party bundle leaked a key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this wrecks a domain grade
&lt;/h2&gt;

&lt;p&gt;A typical Next marketing site has one polyfill chunk and a GTM snippet. A keyword scanner emits 20 CRITICAL &lt;code&gt;js_secret&lt;/code&gt; rows. The aggregate score falls off a cliff. The operator sees "compromised." The page is a stock &lt;code&gt;create-next-app&lt;/code&gt; output.&lt;/p&gt;

&lt;p&gt;mago.team dropped those rows after they showed up as CRITICAL on properties that were not breached. The filter lives in the report builder. New scans do not store the polyfill hit. Old reports still show it until a rescan.&lt;/p&gt;

&lt;p&gt;urlscan adds a second lie. A neighbour page with a 200 can mention the seed in a Google tag. If the follow-up JS job uses that URL as origin, Shopify pixels and Keycloak query strings land in the customer HUD. Restrict follow-ups to HTTP 200 on the seed apex and its subdomains.&lt;/p&gt;

&lt;p&gt;Header-grade D and DMARC &lt;code&gt;p=none&lt;/code&gt; are real. They should move the ring. A polyfill password should not. Mix them and nobody trusts the number.&lt;/p&gt;

&lt;p&gt;The test is simple. If the same CRITICAL fires on every Next.js site you scan, it is the framework. Delete the rule. Keep the AKIA rule. Keep the first-party host check. Then the JS section is short enough to read.&lt;/p&gt;

</description>
      <category>security</category>
      <category>osint</category>
      <category>javascript</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>GitHub Push Protection Is a Pattern Match, Not a Secrets Boundary</title>
      <dc:creator>Rxkov</dc:creator>
      <pubDate>Thu, 17 Sep 2026 21:06:07 +0000</pubDate>
      <link>https://dev.to/rxkov/github-push-protection-is-a-pattern-match-not-a-secrets-boundary-2bf3</link>
      <guid>https://dev.to/rxkov/github-push-protection-is-a-pattern-match-not-a-secrets-boundary-2bf3</guid>
      <description>&lt;p&gt;GitGuardian counted 28,649,024 new hardcoded secrets in public GitHub commits in 2025. That is a 34 percent rise from 2024, the largest single-year jump in their series. Teams still treat GitHub secret scanning as if it made a public repository safe for credentials. It did not. It delayed a subset of partner-shaped tokens.&lt;/p&gt;

&lt;p&gt;Push protection is a pattern match at the remote. Generic secrets, git history, gists, and a one-click bypass sit outside that match. Partner notification buys minutes for AWS and Stripe. It does not inventory what you already committed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What GitHub actually blocks
&lt;/h2&gt;

&lt;p&gt;Secret scanning on public repositories is free and automatic. GitHub documents the scope: git history on all branches, issues, pull requests, Discussions, wikis, and secret gists. Partner patterns get forwarded to the issuer. GitHub personal access tokens leaked in public repos are revoked by GitHub itself.&lt;/p&gt;

&lt;p&gt;Push protection is the earlier check. It refuses a push that contains a detector GitHub has high confidence in. The &lt;a href="https://github.blog/changelog/2026-06-17-secret-scanning-updates-june-2026/" rel="noopener noreferrer"&gt;June 2026 changelog&lt;/a&gt; added default push-protection for Cloudflare tokens, OpenRouter keys, Supabase PATs, and others. The list grows every few months. That is the product working as designed.&lt;/p&gt;

&lt;p&gt;The list is also the limit. GitHub's pattern table splits detectors into partner, user-alert, push-protection-default, and configurable. Generic connection strings and home-grown API keys sit in the generic or custom buckets. Those are not the default push-protection set. GitGuardian's 2025 report, covering 2024, put generic credentials at 58 percent of detected leaks. A regex that only fires on Stripe and AWS will miss most of the volume.&lt;/p&gt;

&lt;p&gt;Validity checks are a second product, not the same as partner notification. Partner notification tells Stripe or AWS that a public repo just received their token. Validity checks tell you whether a secret in your alert is still live. GitHub documents the split. Mix the two and the team waits for a vendor. The vendor never sees a custom API key.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four holes that remain
&lt;/h2&gt;

&lt;p&gt;The first hole is generic secrets. A &lt;code&gt;DATABASE_URL&lt;/code&gt; with a password. A Postman &lt;code&gt;PMAK-&lt;/code&gt; key that is not yet a default detector. A bearer token with no vendor prefix. Push protection does not block what it cannot name.&lt;/p&gt;

&lt;p&gt;Code search still finds the leftovers. These queries are boring on purpose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;path&lt;/span&gt;:.&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;
&lt;span class="n"&gt;filename&lt;/span&gt;:.&lt;span class="n"&gt;npmrc&lt;/span&gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;authToken&lt;/span&gt;
&lt;span class="s2"&gt;"Authorization: Bearer"&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt;:&lt;span class="n"&gt;md&lt;/span&gt;
&lt;span class="n"&gt;filename&lt;/span&gt;:&lt;span class="n"&gt;mcp&lt;/span&gt;.&lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;None of those strings is a partner detector. All of them are how internal APIs, package registries, and agent configs actually ship credentials. GitHub will index the file. It will not refuse the push.&lt;/p&gt;

&lt;p&gt;The second hole is history. Secret scanning will alert on an old commit. Push protection will not un-commit it. Rotation is the fix. Rewriting history is optional and often skipped. GitGuardian retested secrets first seen in 2022. 64 percent were still valid in January 2026.&lt;/p&gt;

&lt;p&gt;The third hole is the bypass. A developer with write access can push through protection by giving a reason. Organizations can lock this down with delegated bypass. Most public repos never turn that lock on. The control then becomes a dialog, not a gate.&lt;/p&gt;

&lt;p&gt;The fourth hole is the private-to-public flip. Push protection on private repos is a paid GitHub Secret Protection feature. Secrets committed while the repo was private become public the moment visibility changes. GitGuardian found internal repositories six times more likely to contain hardcoded secrets than public ones. The day you open-source the prototype, you publish the leftover keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI commits doubled the leak rate
&lt;/h2&gt;

&lt;p&gt;GitGuardian scanned public commits through 2025. Secret leak rates in AI-assisted code were roughly double the GitHub-wide baseline. Leaks tied to AI services rose 81 percent year over year, to 1,275,105. &lt;a href="https://blog.mago.team/post/malicious-mcp-servers-supply-chain-problem-ai-agents" rel="noopener noreferrer"&gt;MCP server&lt;/a&gt; docs often tell people to put credentials in config files. The same report attributes more than 24,000 exposed secrets to that pattern.&lt;/p&gt;

&lt;p&gt;GitHub is adding detectors after the fact. Lovable Labs joined the partner program in August 2026. GitGuardian detected 113,000 new DeepSeek API keys in 2025, before a detector existed. The gap between a new SaaS token format and a default push-protection rule is measured in months. Agents generate the config in seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a practitioner actually does
&lt;/h2&gt;

&lt;p&gt;Turn on push protection and leave it on. That is table stakes, not a strategy. Then treat GitHub as one surface among several.&lt;/p&gt;

&lt;p&gt;Scan for generic patterns, not only partner prefixes. &lt;code&gt;gitleaks&lt;/code&gt; and &lt;code&gt;trufflehog&lt;/code&gt; still catch connection strings GitHub will not block. Run them on history, not only HEAD.&lt;/p&gt;

&lt;p&gt;Assume every public gist and every fork is in scope. GitHub scans secret gists. It does not make the gist private for you. A gist created to debug a webhook is still a public document with a guessable URL until you delete it.&lt;/p&gt;

&lt;p&gt;Forks copy the blob. If the parent later rotates, the fork keeps the old value until someone force-pushes or deletes the repo. Secret scanning on the parent does not fan out a revoke to every fork. That is your job.&lt;/p&gt;

&lt;p&gt;Rotate on alert. Do not wait for a rewrite of git history. The credential is the asset. The commit is the evidence.&lt;/p&gt;

&lt;p&gt;If the work is a bounty or a vendor review, search what GitHub will not. Filename filters, &lt;code&gt;.env&lt;/code&gt; remnants, &lt;code&gt;Authorization: Bearer&lt;/code&gt; in examples, MCP json with &lt;code&gt;env&lt;/code&gt; blocks. Related method on this site: &lt;a href="https://blog.mago.team/post/github-osint-o-que-desenvolvedores-expoem-sem-perceber" rel="noopener noreferrer"&gt;GitHub OSINT&lt;/a&gt; and [five leak surfaces &lt;a href="https://blog.mago.team/glossary/HIBP" rel="noopener noreferrer"&gt;HIBP&lt;/a&gt; will not show](&lt;a href="https://blog.mago.team/post/five-credential-leak-surfaces-hibp-wont-show" rel="noopener noreferrer"&gt;https://blog.mago.team/post/five-credential-leak-surfaces-hibp-wont-show&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mago.team" rel="noopener noreferrer"&gt;mago.team&lt;/a&gt; runs that pass as a pipeline. The local encoding of the same idea is &lt;a href="https://github.com/rxkov/spy" rel="noopener noreferrer"&gt;spy&lt;/a&gt;. Neither replaces rotation. Both keep the first hop honest.&lt;/p&gt;

&lt;p&gt;GitHub's scanner is a good delay. It is not a boundary. Plan the inventory as if the delay failed, because for generic secrets it already did.&lt;/p&gt;

</description>
      <category>security</category>
      <category>osint</category>
      <category>github</category>
      <category>infosec</category>
    </item>
    <item>
      <title>The OPSEC of OSINT Tooling - s.py</title>
      <dc:creator>Rxkov</dc:creator>
      <pubDate>Thu, 17 Sep 2026 19:40:05 +0000</pubDate>
      <link>https://dev.to/rxkov/the-opsec-of-osint-tooling-spy-1ii3</link>
      <guid>https://dev.to/rxkov/the-opsec-of-osint-tooling-spy-1ii3</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1z1466ppd53ggnq26mor.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1z1466ppd53ggnq26mor.jpg" alt=" " width="768" height="1152"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every OSINT query has two audiences: you, and whoever operates the resolver, the API, and the log pipeline behind it.&lt;/p&gt;

&lt;p&gt;If collection runs in someone else's cloud, your target list is shared. That can be acceptable, but it is an OPSEC choice that must be intentional. The industry default is convenience: analysts paste a domain into a web form, then debate TTPs in the final report. The primary TTP—where the query actually executed—gets ignored.&lt;/p&gt;

&lt;p&gt;A local toolkit keeps the first hop honest. DNS, WHOIS, certificate transparency, and passive IP enrichment rely on public data. There is no technical reason this data must transit a third-party vendor before reaching your notes.&lt;/p&gt;

&lt;p&gt;spy runs these checks locally:&lt;/p&gt;

&lt;p&gt;python3 s.py run dns_deep example.com&lt;br&gt;
python3 s.py run whois_osint example.com&lt;br&gt;
python3 s.py run cert_transparency example.com&lt;br&gt;
python3 s.py intel 1.2.3.4 --json&lt;/p&gt;

&lt;p&gt;Active probes are gated. Without a defined scope, execution is blocked. This applies the same principle used in senior agent harnesses: no tool call without limits.&lt;/p&gt;

&lt;p&gt;Use a hosted scanner like mago.team when you need broad coverage without managing infrastructure. Use a local pipeline when target lists are sensitive or when methodologies must survive vendor lock-in.&lt;/p&gt;

&lt;p&gt;Convenience is not a strategy. Choose your approach deliberately.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/rxkov" rel="noopener noreferrer"&gt;
        rxkov
      &lt;/a&gt; / &lt;a href="https://github.com/rxkov/spy" rel="noopener noreferrer"&gt;
        spy
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      OSINT and reconnaissance toolkit
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;spy&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;OSINT and reconnaissance. The method is the pipeline.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;spy encodes DNS, WHOIS, certificate transparency, identity correlation, and
public threat feeds as named passes that run on your machine. No account
No SaaS hop unless you choose one.&lt;/p&gt;
&lt;p&gt;Authorized targets only.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why spy&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Most OSINT is still a shopping list in someone's head. Open this registrar.
Query that CT log. Remember SPF. The sequence dies when the analyst is tired.&lt;/p&gt;
&lt;p&gt;A checklist that cannot be executed the same way twice is folklore.&lt;/p&gt;
&lt;p&gt;spy treats collection as a &lt;strong&gt;pipeline&lt;/strong&gt;: stable order, stable output, inspectable
steps. You can drop a stage. You can rerun last quarter and diff the JSON. You
can refuse to send the target to a third party.&lt;/p&gt;
&lt;p&gt;That last point is OPSEC, not preference. Every query has two audiences: you,
and whoever logs the resolver.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Install&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Python 3.11+.&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;git clone https://github.com/rxkov/spy.git
&lt;span class="pl-c1"&gt;cd&lt;/span&gt; spy
python3 -m venv .venv
&lt;/pre&gt;…
&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/rxkov/spy" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>osint</category>
      <category>webdev</category>
      <category>programming</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>Your S3 Bucket Might Be Public Right Now. We'll Tell You First.</title>
      <dc:creator>Rxkov</dc:creator>
      <pubDate>Sun, 06 Sep 2026 05:59:53 +0000</pubDate>
      <link>https://dev.to/rxkov/your-s3-bucket-might-be-public-right-now-well-tell-you-first-2lk1</link>
      <guid>https://dev.to/rxkov/your-s3-bucket-might-be-public-right-now-well-tell-you-first-2lk1</guid>
      <description>&lt;h1&gt;
  
  
  Your S3 Bucket Might Be Public Right Now. We'll Tell You First.
&lt;/h1&gt;

&lt;p&gt;GrayhatWarfare's public index holds 470,600 open buckets today. An attacker searches your company name, filters by &lt;code&gt;.env&lt;/code&gt; or &lt;code&gt;backup.sql&lt;/code&gt;, and has a working target list in under a minute — no cost, no advanced technical skill, no trace in your logs.&lt;/p&gt;

&lt;p&gt;Cloud storage misconfiguration is the number one cause of cloud data exposure. Not zero-days, not provider failures — wrong configuration. Public S3 buckets, Firebase databases left in test mode, Azure containers with anonymous read enabled: all discoverable by anyone with an internet connection. The gap between "configured" and "audited" is where breaches happen — and GrayhatWarfare fills that gap within two weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem Is Visibility, Not Technology
&lt;/h2&gt;

&lt;p&gt;AWS, Google, and Microsoft ship robust controls to block public access. The issue isn't the provider. It's what happens between the moment a developer spins up a bucket in test mode to speed up development and the moment someone notices that configuration went to production.&lt;/p&gt;

&lt;p&gt;In most organizations, nobody notices. Not because the team is incompetent — but because proactive cloud storage audits aren't part of the standard operational flow. When the alert arrives, if it arrives, it's an external incident report, a researcher notification, or a headline.&lt;/p&gt;

&lt;p&gt;This isn't a hypothetical. In 2019, Chtrbox — an influencer marketing firm — left their Firebase database in test mode in production. Result: data on 49 million Instagram users exposed publicly, including emails, phone numbers, and location data. The disclosure reached TechCrunch before it reached the company's own security team.&lt;/p&gt;

&lt;p&gt;In another incident, a single S3 misconfiguration exposed 273,000 bank transfer PDFs — routing numbers, account numbers, full transaction records. Discovered through passive reconnaissance. No active exploitation. No warning.&lt;/p&gt;

&lt;p&gt;Gartner projects that 99% of cloud security failures through 2026 will be customer-side configuration failures. The question isn't whether you have a misconfiguration. It's whether you find it first.&lt;/p&gt;

&lt;h2&gt;
  
  
  What intel.mago.team Does for Your Team
&lt;/h2&gt;

&lt;p&gt;intel.mago.team scans your cloud infrastructure across the same vectors attackers look for — S3, Firebase, Azure Blob, GCS — and delivers an exposure report before the problem appears in a public index like GrayhatWarfare.&lt;/p&gt;

&lt;p&gt;Every exposure found comes with full context: which resource is exposed, what type of data is accessible, severity level, and the specific remediation step. No manual analysis on your end. You open the report, prioritize by severity, act.&lt;/p&gt;

&lt;p&gt;The vectors covered are exactly those behind the largest cloud data breaches in the last five years:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;S3 buckets with public read or write permissions&lt;/li&gt;
&lt;li&gt;Firebase rules in test mode that survived past their 30-day window&lt;/li&gt;
&lt;li&gt;Azure Blob containers with anonymous read enabled&lt;/li&gt;
&lt;li&gt;GCS buckets with IAM bindings open to all users&lt;/li&gt;
&lt;li&gt;Configuration files, API keys, and &lt;code&gt;.env&lt;/code&gt; files in public storage&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Difference Between Finding and Being Found
&lt;/h2&gt;

&lt;p&gt;Teams that scan proactively find the exposure on their own schedule, with time to act before data is copied. Teams that don't scan receive notification from the outside — from a researcher who already accessed the files, or worse.&lt;/p&gt;

&lt;p&gt;The difference between the two scenarios isn't technical sophistication. It's knowing the problem exists while it's still yours to resolve.&lt;/p&gt;

&lt;p&gt;Run a scan at &lt;a href="https://intel.mago.team" rel="noopener noreferrer"&gt;intel.mago.team&lt;/a&gt; against your infrastructure today. If there's an exposure, you'll know before any attacker does. If there isn't, you have auditable evidence that the check was done — not just the hope that everything is fine.&lt;/p&gt;

&lt;p&gt;Your S3 bucket might be public right now. Find out first.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>API Security Testing: OWASP Top 10 for APIs</title>
      <dc:creator>Rxkov</dc:creator>
      <pubDate>Sun, 06 Sep 2026 05:58:41 +0000</pubDate>
      <link>https://dev.to/rxkov/api-security-testing-owasp-top-10-for-apis-19ff</link>
      <guid>https://dev.to/rxkov/api-security-testing-owasp-top-10-for-apis-19ff</guid>
      <description>&lt;p&gt;T-Mobile, Peloton, and Optus all had active security programs. All three were breached through API vulnerabilities that existing tests never caught, because the exploited endpoints were not in anyone's inventory.&lt;/p&gt;

&lt;p&gt;Security teams that rely on pre-deploy testing pass audits and still get breached. API attack surface drifts after deploy. No static scanner can test what it doesn't know exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your OpenAPI Spec Is Not Your Attack Surface
&lt;/h2&gt;

&lt;p&gt;Production APIs diverge from documentation within weeks. Shadow endpoints accumulate through feature flags, deprecated versions that were never deactivated, and side-channel leaks in microservices. None of these endpoints are visible to scanners that operate on code.&lt;/p&gt;

&lt;p&gt;Security teams running comprehensive discovery for the first time consistently find more endpoints than documented — the gap between the specification and what runs in production is substantial. Shadow APIs account for a significant share of API traffic at mid-to-large enterprises, with security teams unaware they exist.&lt;/p&gt;

&lt;p&gt;The 2022 Cequence report recorded 5 billion malicious requests out of 16.7 billion total, all targeting unknown and unmanaged APIs. OWASP API9:2023 names this Improper Inventory Management: beta endpoints frequently lack the rate limiting and authentication equivalent to production, and the absence of documentation lets them be exploited without the security team knowing they exist.&lt;/p&gt;

&lt;p&gt;The 2022 Optus breach made the cost concrete. An undocumented, unauthenticated endpoint exposed data on 9.7 million customers. The endpoint appeared in no OpenAPI spec. SAST and DAST inherit the developer's endpoint list and never test what they don't know exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  BOLA Has Led the List for Five Years Because Auth Only Breaks at Runtime
&lt;/h2&gt;

&lt;p&gt;Object-level authorization failures are not detectable through static analysis. They require real user IDs, real objects in the database, and runtime context. Every static scanner misses this category by design.&lt;/p&gt;

&lt;p&gt;The 2021 Peloton breach exposed more than 4 million user records, including accounts configured as private. Authenticated users could access any other user's data by swapping the ID in the request path. Peloton had authentication working. It had no per-object authorization check on each endpoint.&lt;/p&gt;

&lt;p&gt;T-Mobile in 2023 saw 37 million customers affected by an API without adequate authorization checks. The class action settlement reached 350 million dollars. OWASP has ranked BOLA as API1:2023 for five consecutive years, and the category appears in roughly 40 percent of all documented API attacks.&lt;/p&gt;

&lt;p&gt;The technical reason is straightforward: BOLA requires active runtime context. A valid user session, real objects in the database, and a sequence of requests that crosses ownership boundaries between users. No code scanner can simulate that context. The vulnerability is not in the source code -- it lives in business logic executing with production data. That structural gap is why BOLA has led the list since 2019.&lt;/p&gt;

&lt;h2&gt;
  
  
  JWT Misconfigs Survive Code Review Because They Are Config Bugs, Not Code Bugs
&lt;/h2&gt;

&lt;p&gt;The most critical JWT vulnerabilities exist in the deployed configuration, independent of source code. Updating the library version does not fix a misconfigured token validation endpoint in the API gateway.&lt;/p&gt;

&lt;p&gt;CVE-2024-31033 affects JJWT: incorrectly configured signing keys allow token forgery with weaker authentication than expected. CVE-2026-29000 affects pac4j-jwt in versions before 4.5.9, 5.7.9, and 6.3.3. An attacker with access only to the RSA public key can forge JWTs with arbitrary claims and authenticate as any user, including administrators.&lt;/p&gt;

&lt;p&gt;The alg:none attack works as follows: the attacker removes the token signature and sets the algorithm field to none. The server accepts the token if it does not explicitly reject that value in configuration. The RS256-to-HS256 confusion attack uses the RSA public key as an HMAC secret. An attacker who knows the public key signs tokens that the server accepts as valid.&lt;/p&gt;

&lt;p&gt;SAST sees the JWT library import in the code. It does not see the token validation configuration deployed in the API gateway or server environment variables. The code can be correct while the configuration is wrong. That distinction is what makes this vulnerability class survive complete code reviews. Tools that test active endpoints, like the jwt_scanner at intel.mago.team, detect these misconfigurations at runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  The OWASP API Top 10 Is a Taxonomy, Not a Detection Workflow
&lt;/h2&gt;

&lt;p&gt;Most practitioners use the OWASP Top 10 as a code review checklist. The actual detection workflow requires active endpoint discovery, runtime probing, and drift comparison against inventory. No static tool executes these steps.&lt;/p&gt;

&lt;p&gt;The major API security vendor blogs follow the same pattern: they explain the categories with generic examples and provide no end-to-end detection methodology. SAST identifies code patterns but does not detect authorization gaps, configuration drift, or undocumented endpoints. DAST tests running applications but needs a known endpoint list, so shadow APIs are never tested.&lt;/p&gt;

&lt;p&gt;Authorization failures, business logic abuse, and multi-step flow exploitation depend on runtime context and sequencing that static analysis cannot see. The fundamental gap is this: knowing which vulnerability categories exist is not the same as knowing whether your deployed API is vulnerable to them. The taxonomy describes what can happen. The detection workflow determines what is happening now, with your active endpoints.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Detection Workflow Starts with Discovery, Not Scanning
&lt;/h2&gt;

&lt;p&gt;Effective API security testing follows a fixed sequence: passive discovery to find what exists, fingerprinting to understand the stack, targeted probing for each category. Skipping the first step means scanning a fraction of the real surface.&lt;/p&gt;

&lt;p&gt;Step 1: discovery via subdomain enumeration, JavaScript bundle extraction, path traversal on API gateways, and fingerprinting with Shodan and Censys. Step 2: inventory baseline, mapping discovered endpoints against the documented spec and flagging shadow endpoints and deprecated versions still active. Step 3: authentication probing on each endpoint, testing auth bypass, alg:none acceptance in JWT, and missing required authentication headers.&lt;/p&gt;

&lt;p&gt;Step 4: BOLA and BFLA testing. For each endpoint that handles objects, probe with valid authentication but a different user context and verify whether the response is 403 or 200 with another user's data. Step 5: continuous comparison, re-running discovery weekly and generating a diff against the last known inventory to detect drift. Each step depends on the previous one. Without full discovery, scanning tests only the endpoints you already documented.&lt;/p&gt;

&lt;h2&gt;
  
  
  Runtime and Development Posture Diverge at Deploy Time
&lt;/h2&gt;

&lt;p&gt;Protecting production APIs requires continuous external testing against active endpoints, from the same perspective an attacker has. Internal testing from the development side does not detect the configuration, routing, and endpoint drift that emerges in production.&lt;/p&gt;

&lt;p&gt;The scenario documented in OWASP API9:2023 is direct: a beta API without rate limiting was used to brute-force password reset tokens. The production API had rate limiting. The beta version did not. The security team tested the production version and passed the audit.&lt;/p&gt;

&lt;p&gt;The scale of the problem is growing faster than review cycles can track. Organizations with more than 100 API endpoints went from 4 percent in 2024 to 38 percent in 2025. 84 percent of security professionals reported API-related incidents in the past 12 months, most involving endpoints unknown to the internal team.&lt;/p&gt;

&lt;p&gt;External API testing, conducted from outside the network perimeter, detects routing misconfigurations, exposed internal endpoints, and authentication gaps that internal tools cannot reach. Each new API version creates shadow endpoints until the documentation is updated. The intel.mago.team approach combines continuous external discovery with jwt_scanner and technology fingerprinting on active endpoints, not on code.&lt;/p&gt;

&lt;p&gt;The scanner testing your pipeline doesn't know what the deploy delivered.&lt;/p&gt;

</description>
      <category>osint</category>
      <category>security</category>
    </item>
    <item>
      <title>Threat Intelligence 101: From Raw Data to Actionable Insights</title>
      <dc:creator>Rxkov</dc:creator>
      <pubDate>Sun, 06 Sep 2026 05:58:01 +0000</pubDate>
      <link>https://dev.to/rxkov/threat-intelligence-101-from-raw-data-to-actionable-insights-185d</link>
      <guid>https://dev.to/rxkov/threat-intelligence-101-from-raw-data-to-actionable-insights-185d</guid>
      <description>&lt;p&gt;The SOC got the alert at 2 a.m. By the time the analyst correlated the IOC with the logs and confirmed relevance, the attacker had already moved laterally into three internal servers. The intelligence was right. The pipeline was the problem.&lt;/p&gt;

&lt;p&gt;Most organizations already pay for threat intelligence feeds. The gap is not data: it is operationalization. IOCs arrive in dashboards and PDFs that never automatically translate into detection rules, blocklists, or playbooks. The speed advantage that intelligence should deliver gets destroyed by manual correlation that no team can sustain at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Operationalization Gap
&lt;/h2&gt;

&lt;p&gt;Organizations that suffered breaches in 2024 and 2025 were not operating without intelligence. They had feed subscriptions. The pattern documented in security research is consistent: intelligence arrives, operations cannot absorb it within the relevant time window, the moment passes, and the lesson disappears without a record.&lt;/p&gt;

&lt;p&gt;An IOC that fires without an associated playbook is a documented detection gap. CISA demonstrated with the AIS (Automated Indicator Sharing) program that machine-speed sharing is a solved problem: STIX 2.1 for structure, TAXII 2.1 for transport. What AIS does not solve is the last mile: mapping indicators to SIEM rules and EDR actions, a step that most teams still handle manually.&lt;/p&gt;

&lt;p&gt;Automating the conversion of CTI reports into detection rules exists as an active research field, but adoption in SOC teams has not kept pace with the volume of intelligence produced. Organizations continue paying for intelligence that never reaches the detector.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Threat Intelligence Actually Is
&lt;/h2&gt;

&lt;p&gt;Intelligence is not the same as data. STIX 2.1, ratified by OASIS in June 2021, defines structured objects with 18 domain objects and 18 cyber-observable objects to represent context, relationships, and relevance. A CSV list of IPs has none of that.&lt;/p&gt;

&lt;p&gt;The practical distinction splits intelligence into three layers: strategic informs long-term decisions, operational supports campaign planning, and tactical provides IOCs for immediate detection. Most commercial feeds deliver exclusively the tactical layer, which also has the shortest shelf life.&lt;/p&gt;

&lt;p&gt;MITRE ATT&amp;amp;CK v18 (October 2025) catalogs 14 tactics, 216 techniques, and 475 sub-techniques for the Enterprise environment. Adversary techniques change on a quarterly timescale. IOCs change on an hourly timescale. Building detections based on ATT&amp;amp;CK TTPs produces rules that survive attacker infrastructure rotation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Commercial Feeds Solve the Wrong Problem
&lt;/h2&gt;

&lt;p&gt;Commercial feeds deliver what is easy to deliver: IP addresses, file hashes, and domains. The problem is that attackers rotate IPs within hours, and half of fresh feed indicators generate false positives from legitimate business traffic, requiring manual triage before any application. With each new batch of indicators, analysts must filter before applying, destroying the automation gain.&lt;/p&gt;

&lt;p&gt;The result is context loss at delivery: the SOC receives values, not intelligence. An IP address without information about which campaign, which actor, and which technique it represents is noise dressed as signal.&lt;/p&gt;

&lt;p&gt;IP blocklists have a shelf life of 24 to 72 hours under normal conditions, and shorter when the attacker notices they are being blocked. Relying exclusively on tactical feeds anchors detection in infrastructure-centric thinking, while the approach that survives is behavior-centric. ATT&amp;amp;CK was built precisely for that paradigm shift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Underused Passive Sources: What You Already Have
&lt;/h2&gt;

&lt;p&gt;Operationalizing threat intelligence does not require depending exclusively on external feeds: the organization's own attack surface already generates primary intelligence that no vendor can see. Certificate Transparency logs are public and free. When an attacker registers a typosquat domain for a phishing campaign, they need an SSL certificate before launching the infrastructure. That certificate gets logged in CT logs and is available for query before the attack starts. IOC feeds only capture the domain after the campaign is active and observed by third parties.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://secybers.com/blog-details/advanced-certificate-transparency-hunting-uncovering-hidden-infrastructure-through-ct-log-analysis" rel="noopener noreferrer"&gt;SecYbers&lt;/a&gt; documents that CT logs combined with DNS and WHOIS data enable full adversary infrastructure mapping before the active phase of the campaign. Subdomain drift, the creation of unmanaged subdomains on the organization's own domains, exposes attack surface that no external feed can see because it is internal. Technology fingerprinting reveals exposed services before attackers discover them through their own scans.&lt;/p&gt;

&lt;p&gt;These sources are continuous intelligence generated by the organization's own attack surface. Tools like &lt;a href="https://intel.mago.team/spells" rel="noopener noreferrer"&gt;intel.mago.team/spells&lt;/a&gt; monitor CT logs and infrastructure detection automatically as intelligence sources; for point-in-time queries, &lt;a href="https://crt.sh" rel="noopener noreferrer"&gt;crt.sh&lt;/a&gt; offers free search and SSLMate's &lt;a href="https://sslmate.com/certspotter/" rel="noopener noreferrer"&gt;Cert Spotter&lt;/a&gt; provides continuous monitoring with a free tier. Most teams outsource intelligence generation to external feeds while already operating high-quality primary sources without processing them.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Automation Closes the Gap
&lt;/h2&gt;

&lt;p&gt;The minimum functional pipeline has four stages: ingestion of structured sources, enrichment with context, mapping to ATT&amp;amp;CK TTPs, and automatic delivery to SIEM or EDR. The gap lives between mapping and delivery, which is exactly where manual work intervenes and destroys the speed that automation should provide.&lt;/p&gt;

&lt;p&gt;TAXII 2.1 solves the transport layer: feeds that publish via TAXII allow automated ingestion without manual CSV parsing. STIX 2.1 solves structure: typed objects with explicit relationships let downstream systems process indicators without human intervention. CISA provides AIS access via TAXII to any organization that wants to start with government-sourced intelligence.&lt;/p&gt;

&lt;p&gt;Research published on arXiv in 2026 (From IOCs to Regex, &lt;a href="https://arxiv.org/abs/2604.12228" rel="noopener noreferrer"&gt;arXiv:2604.12228&lt;/a&gt;) demonstrates that LLMs can convert CTI reports into regex expressions for detection with a significant degree of automation. The problem is not technological: teams that do not define what they need to detect before buying feeds have no way to evaluate what to automate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Minimum Pipeline
&lt;/h2&gt;

&lt;p&gt;The most common mistake is starting with the feed and working back to requirements. The functional pipeline reverses that order.&lt;/p&gt;

&lt;p&gt;Step one is defining intelligence requirements based on realistic threats for the organization's sector, size, and technology stack. Without that, any feed generates noise. Step two is an attack surface inventory, because you cannot prioritize intelligence without knowing what to protect. Step three is structured ingestion: feeds in STIX format via TAXII enable automation; raw CSVs do not. Step four is TTP mapping, building detections based on adversary behavior rather than point-in-time indicators that expire. Step five is the feedback loop: measure how many alerts generated by last quarter's intelligence produced an actual security decision change.&lt;/p&gt;

&lt;p&gt;The 2025 arXiv research on MITRE ATT&amp;amp;CK applications (&lt;a href="https://arxiv.org/abs/2502.10825" rel="noopener noreferrer"&gt;arXiv:2502.10825&lt;/a&gt;) identifies automated TTP extraction from reports as a critical gap. Most organizations perform that mapping manually, making the most time-consuming pipeline stage also the most error-prone.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Operationalize First
&lt;/h2&gt;

&lt;p&gt;Operationalization priority follows immediate impact with minimal configuration friction.&lt;/p&gt;

&lt;p&gt;Credential exposure monitoring detects when organizational accounts appear in public dumps before attackers use them. Certificate issuance monitoring via CT logs detects phishing infrastructure before campaign launch. Subdomain drift detection exposes attack surface that was never inventoried.&lt;/p&gt;

&lt;p&gt;All three share one property: they produce actionable alerts, not reports. The output is a suspect domain to block, a credential to rotate, a subdomain to disable. Define your requirements before buying the next feed. Set up CT log monitoring for your domains. Map your detections to ATT&amp;amp;CK TTPs, not IP lists. Intelligence that never reaches the detector is indistinguishable from having no intelligence.&lt;/p&gt;

</description>
      <category>osint</category>
      <category>security</category>
    </item>
    <item>
      <title>Web Application Reconnaissance: Mapping the Attack Surface</title>
      <dc:creator>Rxkov</dc:creator>
      <pubDate>Sun, 06 Sep 2026 05:57:25 +0000</pubDate>
      <link>https://dev.to/rxkov/web-application-reconnaissance-mapping-the-attack-surface-573a</link>
      <guid>https://dev.to/rxkov/web-application-reconnaissance-mapping-the-attack-surface-573a</guid>
      <description>&lt;p&gt;Subfinder returns 1,247 subdomains in 19 minutes. Amass adds 340 more from passive sources. The pentest report covers 12 of them.&lt;/p&gt;

&lt;p&gt;That gap is where most critical bugs survive. Not because the team missed the assets, but because there was no layer to decide which ones to test first.&lt;/p&gt;

&lt;p&gt;Recon produces lists. Practitioners who find critical vulnerabilities first treat those lists as input to a risk scoring layer, not as a final deliverable. The difference between an attack surface report and an actual attack map lives in that intermediate step: internet exposure, known CVE surface, and business criticality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recon Data Is Not Intelligence: The Volume Problem
&lt;/h2&gt;

&lt;p&gt;Modern subdomain enumeration tools deliver impressive scale in minutes. Subfinder queries 103 passive sources simultaneously; Amass adds active brute-force enumeration over wordlists of one million entries. The problem that volume creates is a filtering problem.&lt;/p&gt;

&lt;p&gt;The OWASP Attack Surface Management Top 10 places "unknown and unmanaged external assets" at the top of the risk list. Organizations frequently don't know the full extent of their own exposed surface. An annual pentest scoped to the main domain leaves the rest untouched.&lt;/p&gt;

&lt;p&gt;The distinction that matters is between attack surface and exploitable surface. A list of 1,247 subdomains is an attack surface. Exploitable surface is the subset with active DNS, a stack with a public CVE and CVSS above 7.0, and a path that touches authentication or sensitive data. The gap between those two numbers is where tools stop and judgment begins.&lt;/p&gt;

&lt;p&gt;Technical reports without business context are not actionable. A subdomain running Apache 2.4.49 with public exposure is priority zero: CVE-2021-41773 carries CVSS 9.8 and widely documented public exploits. A subdomain running Apache 2.4.53 on staging with no active DNS is a candidate for deprioritization. Risk scoring makes that distinction; the raw list does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passive Discovery: Mapping Without Touching
&lt;/h2&gt;

&lt;p&gt;Certificate Transparency logs are the most underrated resource for passive discovery. Every TLS certificate issued by a trusted CA appears in a public, immutable log within minutes of issuance. Before a new subdomain even has full DNS propagation, it is already visible in the CT history.&lt;/p&gt;

&lt;p&gt;crt.sh exposes that history through a direct SQL API. A query for &lt;code&gt;%.empresa.com&lt;/code&gt; returns every subdomain that has ever received a certificate, including admin panels provisioned once and never formally decommissioned. A certificate issued in 2019 for &lt;code&gt;dev-api.empresa.com&lt;/code&gt; stays in the log even if the subdomain was later disabled.&lt;/p&gt;

&lt;p&gt;Google and Bing surface accidentally indexed content through search operators. &lt;code&gt;site:empresa.com filetype:env&lt;/code&gt; finds published &lt;code&gt;.env&lt;/code&gt; files. &lt;code&gt;inurl:empresa.com admin login&lt;/code&gt; finds panels exposed to crawlers. The OWASP WSTG operator collection covers 40 distinct search patterns for sensitive data indexed without intent.&lt;/p&gt;

&lt;p&gt;Passive DNS databases like SecurityTrails and VirusTotal store resolution history for years. SecurityTrails &lt;code&gt;/v1/domain/{domain}/subdomains&lt;/code&gt; returns up to 10,000 historical subdomains via API; VirusTotal &lt;code&gt;/domains/{domain}/subdomains&lt;/code&gt; supplements that with resolution data from threat intelligence feeds. A corporate acquisition that brought in domains from a purchased company shows up in that history before any public announcement. WHOIS and RDAP expose ownership records that link seemingly independent domains to the same registrant.&lt;/p&gt;

&lt;p&gt;Public repositories on GitHub and GitLab routinely expose internal hostnames, API tokens, and environment keys in commit history — the dork &lt;code&gt;site:github.com 'company.com' 'password'&lt;/code&gt; is standard practice in passive reconnaissance.&lt;/p&gt;

&lt;p&gt;The operational advantage of all these methods is zero interaction with the target infrastructure. No packets sent means no IDS alerts, no access logs, no anomalous monitoring window. The passive phase is where information asymmetry exists before any contact is made.&lt;/p&gt;

&lt;p&gt;All of that data arrives as raw lists — the resulting volume is precisely what makes the scoring layer non-optional.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack Fingerprinting: Disclosed Versions as a CVE Shortlist
&lt;/h2&gt;

&lt;p&gt;Every version string a server leaks is a direct lookup against CVE databases. Fingerprinting does not find vulnerabilities; it narrows the search space to confirmed candidates with documented exploits.&lt;/p&gt;

&lt;p&gt;HTTP headers are the first signal layer. &lt;code&gt;Server: Apache/2.4.49&lt;/code&gt; identifies the exact version. &lt;code&gt;X-Powered-By: PHP/8.0.1&lt;/code&gt; does the same for the runtime. &lt;code&gt;X-AspNet-Version: 4.0.30319&lt;/code&gt; and &lt;code&gt;X-AspNetMvc-Version: 5.2&lt;/code&gt; reveal the full stack in two response lines. These headers are enabled by default in standard configurations and rarely disabled in production.&lt;/p&gt;

&lt;p&gt;Session cookie names identify frameworks without any response body analysis. &lt;code&gt;PHPSESSID&lt;/code&gt; indicates PHP; &lt;code&gt;JSESSIONID&lt;/code&gt; indicates Java EE or Spring; &lt;code&gt;ASP.NET_SessionId&lt;/code&gt; indicates .NET. Combined with the &lt;code&gt;Set-Cookie&lt;/code&gt; header value, these names build a partial stack map without any vulnerability scanner.&lt;/p&gt;

&lt;p&gt;Error pages are immediate visual signatures. Apache Tomcat's default 404 has specific HTML with the Jakarta logo. Spring Boot's 500 exposes "Whitelabel Error Page" by default through version 2.x. IIS 7.5 has an error page with a proprietary HTML structure. Any of these patterns connects the asset to a specific CVE history.&lt;/p&gt;

&lt;p&gt;Default framework files frequently remain accessible. &lt;code&gt;changelog.txt&lt;/code&gt; in WordPress exposes the exact version without authentication. &lt;code&gt;composer.json&lt;/code&gt; in PHP applications lists dependencies with versions. &lt;code&gt;package.json&lt;/code&gt; in Node.js applications does the same. These files are found through direct path guessing, no scanner required.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://intel.mago.team" rel="noopener noreferrer"&gt;intel.mago.team&lt;/a&gt; tech_detector automates the collection of these signals from a URL, without a browser extension, making the process scalable for mass enumeration pipelines. The output maps each detected signal to a stack profile that feeds the NVD query by version.&lt;/p&gt;

&lt;p&gt;The operational chain is straightforward: disclosed version, NVD lookup by exact CPE, filter by CVSS greater than or equal to 7.0, filter by available public exploit. The asset that passes that filter is a priority test candidate before any manual analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subdomain Enumeration: The Perimeter Extends Beyond the Main Domain
&lt;/h2&gt;

&lt;p&gt;Staging and development subdomains represent the most common class of forgotten assets. OWASP ASM Top 10 #7 catalogs "exposed debug and test environments" as a distinct risk because these instances exist on subdomains that never go through security review. &lt;code&gt;dev-api.empresa.com&lt;/code&gt;, &lt;code&gt;staging-admin.empresa.com&lt;/code&gt;, &lt;code&gt;test.empresa.com&lt;/code&gt;: each tends to have weaker authentication, real data for environment validation, and no WAF in front.&lt;/p&gt;

&lt;p&gt;CT logs cover historical subdomains that passive DNS databases may not have. The combination of crt.sh, SecurityTrails, and Subfinder maximizes coverage before any active contact with the target. OWASP Amass adds integration with threat intelligence APIs and pastebins to capture accidental references to internal subdomains.&lt;/p&gt;

&lt;p&gt;Subdomain takeover is the direct risk from dangling CNAME records. A subdomain &lt;code&gt;cdn.empresa.com&lt;/code&gt; with a CNAME pointing to a decommissioned cloud service can be claimed by an attacker who provisions the same URL on the target platform. The result is a legitimate company subdomain serving attacker-controlled content, with valid cookie scope on the parent domain. OWASP ASM Top 10 #8 documents this scenario as insecure DNS configuration.&lt;/p&gt;

&lt;p&gt;Active brute-force enumeration over wordlists captures subdomains that never received a TLS certificate and do not appear in passive databases. Wordlists like those from SecLists cover common patterns: &lt;code&gt;api&lt;/code&gt;, &lt;code&gt;admin&lt;/code&gt;, &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;staging&lt;/code&gt;, &lt;code&gt;beta&lt;/code&gt;, &lt;code&gt;internal&lt;/code&gt;. The critical distinction is authorization: active enumeration sends DNS packets to the target's authoritative server and is only legal with an explicit pentest scope.&lt;/p&gt;

&lt;p&gt;Each subdomain added to the list through this process strengthens the case for scoring before testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Synthesis Layer: Scoring Findings Before Testing Them
&lt;/h2&gt;

&lt;p&gt;Raw recon without scoring is output, not intelligence. The risk matrix operates on three factors: internet exposure, known CVE surface, and business criticality. Applying that matrix converts a subdomain list into an attack map with a defined test sequence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Factor 1: Internet exposure.&lt;/strong&gt; An asset counts as exposed when three conditions hold: it appears in CT logs or passive DNS, it resolves DNS at the time of assessment, and it has port 443 or 80 open. A historical subdomain in crt.sh with no active DNS resolution has low exposure; monitoring is worthwhile, but immediate test priority is not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Factor 2: CVE surface.&lt;/strong&gt; Stack fingerprinting produces a version tuple. That tuple goes to the NVD as a CPE query. The relevant filter is CVSS greater than or equal to 7.0 with a documented public exploit. EPSS (Exploit Prediction Scoring System) complements CVSS by estimating the actual probability of active exploitation within 30 days. A CVE with CVSS 9.8 and EPSS 0.02 is less urgent than one with CVSS 7.5 and EPSS 0.87.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Factor 3: Business criticality.&lt;/strong&gt; Path analysis and form inspection identify what the asset processes. A login form indicates authentication. Credit card fields indicate a payment flow. &lt;code&gt;user_id&lt;/code&gt; or &lt;code&gt;account_id&lt;/code&gt; parameters in responses indicate personal data. An admin panel with HTTP Basic Auth on public exposure is maximum priority regardless of how uninteresting it appears.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Concrete example.&lt;/strong&gt; Assess &lt;code&gt;status.empresa.com&lt;/code&gt; discovered in CT logs. The subdomain resolves DNS; port 443 responds. The &lt;code&gt;Server: nginx/1.14.0&lt;/code&gt; header appears in the response. The NVD lists CVE-2019-9511 (HTTP/2 DoS, CVSS 7.5) and CVE-2019-9516 (CVSS 7.5) for that version. The &lt;code&gt;/admin&lt;/code&gt; path returns HTTP 200 without authentication, with a visible login form. EPSS for CVE-2019-9511 is 0.94 in 30 days. Composite score: high exposure, CVE with elevated EPSS, exposed admin path. Result: immediate testing, ahead of any host in the main scope.&lt;/p&gt;

&lt;p&gt;What gets deprioritized: historical subdomains in CT with no active DNS resolution; updated stacks with no public CVE with relevant EPSS; services with no confirmed internet exposure. The OWASP Relative Attack Surface Quotient weights attack points by access level, damage potential, and reproducibility — the result is a numerical metric comparable across assets (&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Attack_Surface_Analysis_Cheat_Sheet.html" rel="noopener noreferrer"&gt;OWASP ASM&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Operationalizing: The Attack Surface Changes Every Day
&lt;/h2&gt;

&lt;p&gt;A recon audit done today is a snapshot of yesterday's risk. TLS certificates are issued continuously. A new subdomain provisioned at 2:00 PM has its certificate in the CT log by 2:05 PM, before any human security review. Annual or monthly audit cadences cannot keep pace with that speed.&lt;/p&gt;

&lt;p&gt;CT log streaming is the operational solution. Tools like certstream subscribe to logs from multiple CAs and deliver real-time events for monitored domains. A new certificate for &lt;code&gt;*.empresa.com&lt;/code&gt; triggers an event; the pipeline runs fingerprinting, DNS check, and port scan automatically; the alert arrives with context before the subdomain is in full production.&lt;/p&gt;

&lt;p&gt;Delta checking formalizes the process: for each newly discovered asset, the pipeline compares against the existing inventory, runs the full fingerprinting and CVE lookup chain, and ranks by risk score. Alerts with a configurable threshold ensure the team receives actionable context, not another raw log to interpret manually.&lt;/p&gt;

&lt;p&gt;intel.mago.team provides continuous stack and subdomain monitoring, closing the gap between audit cycles. New assets enter the pipeline with automated fingerprinting and scoring, without requiring manual re-execution of every tool each week.&lt;/p&gt;

&lt;p&gt;The attack surface is not a list to generate and file away. It is a living map that requires a scoring layer between raw discovery and actual testing. Built once, that layer makes any tool output immediately actionable.&lt;/p&gt;

</description>
      <category>osint</category>
      <category>security</category>
    </item>
    <item>
      <title>JWT Security: How Misconfigured Tokens Expose Your APIs</title>
      <dc:creator>Rxkov</dc:creator>
      <pubDate>Sun, 06 Sep 2026 05:56:49 +0000</pubDate>
      <link>https://dev.to/rxkov/jwt-security-how-misconfigured-tokens-expose-your-apis-he5</link>
      <guid>https://dev.to/rxkov/jwt-security-how-misconfigured-tokens-expose-your-apis-he5</guid>
      <description>&lt;h1&gt;
  
  
  JWT Security: How Misconfigured Tokens Expose Your APIs
&lt;/h1&gt;

&lt;p&gt;A JWT with a forged signature. No password needed, no exploit chain, just a change to the base64-encoded header and a stolen session. Auth0 shipped this bug to production. Attackers exploited it at scale. The token looked valid. The API accepted it.&lt;/p&gt;

&lt;p&gt;The problem is not the JWT format itself. The only real protection the token has, signature verification, can be disabled by misconfiguration. And popular libraries have made it trivially easy to get wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  JWTs Are Trusted by Design. That Is the Problem
&lt;/h2&gt;

&lt;p&gt;A JWT has three parts: header, payload, and signature. The signature is the only security control. Without it, anyone who decodes the token sees the claims in plaintext and can modify them freely.&lt;/p&gt;

&lt;p&gt;The design is stateless by nature. The server holds no session state and cannot revoke individual tokens without additional infrastructure. Who signed the token and when are the only verifiable facts.&lt;/p&gt;

&lt;p&gt;Decoding a JWT is trivial. Verifying the signature requires an explicit library call. The distinction between those two operations is where most bugs live.&lt;/p&gt;

&lt;p&gt;Historically, &lt;code&gt;jsonwebtoken&lt;/code&gt; and &lt;code&gt;jose&lt;/code&gt; accepted &lt;code&gt;alg:none&lt;/code&gt; by default. An unsigned token was valid. The library decoded the payload and returned it as authenticated without verifying anything. That was not an obscure bug. It was the default behavior of libraries used in millions of applications.&lt;/p&gt;

&lt;p&gt;The second structural problem: many languages separate &lt;code&gt;decode&lt;/code&gt; from &lt;code&gt;verify&lt;/code&gt; into distinct functions. Python has &lt;code&gt;jwt.decode()&lt;/code&gt; and &lt;code&gt;jwt.decode(options={"verify_signature": False})&lt;/code&gt;. PHP has &lt;code&gt;Firebase\JWT\JWT::decode()&lt;/code&gt;, which accepts an array of allowed algorithms. When documentation shows incomplete examples or a developer is rushing, the verification step disappears silently. The application works. Tests pass. The vulnerability persists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Misconfigurations That Break Authentication Completely
&lt;/h2&gt;

&lt;h3&gt;
  
  
  alg:none
&lt;/h3&gt;

&lt;p&gt;Change the &lt;code&gt;alg&lt;/code&gt; field in the header to &lt;code&gt;none&lt;/code&gt;, remove the signature, and send the token. In vulnerable implementations, the server accepts it. No key required. No access to the private key. Just base64 editing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Decode the original header&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9"&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;span class="c"&gt;# {"alg":"RS256","typ":"JWT"}&lt;/span&gt;

&lt;span class="c"&gt;# Build a new header with alg:none&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'{"alg":"none","typ":"JWT"}'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'='&lt;/span&gt;
&lt;span class="c"&gt;# eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0&lt;/span&gt;

&lt;span class="c"&gt;# Assemble the token without a signature (trailing empty dot)&lt;/span&gt;
&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0IiwicGF5bG9hZCI6ImFkbWluIn0."&lt;/span&gt;
curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://api.alvo.com/admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Auth0 had this problem in production. The variant &lt;code&gt;alg:nonE&lt;/code&gt; (uppercase E) bypassed the simple string check that would normally block &lt;code&gt;none&lt;/code&gt;. A case-sensitive comparison broke the entire protection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Algorithm Confusion: RS256 to HS256
&lt;/h3&gt;

&lt;p&gt;RS256 uses a private key to sign and a public key to verify. The public key is, by definition, public and frequently exposed at the application's JWKS endpoint.&lt;/p&gt;

&lt;p&gt;HS256 uses a symmetric key. The same secret signs and verifies.&lt;/p&gt;

&lt;p&gt;When an API configured for RS256 accepts HS256 tokens, the attacker uses the server's public key as the HMAC secret. The signature is valid because the server uses that same public key to verify. CVE-2024-54150 documents exactly this pattern in Comcast's xmidt platform, published in 2024. Not in 2010. In 2024. The vector has existed for over a decade and keeps getting introduced into production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Weak Secrets in HS256
&lt;/h3&gt;

&lt;p&gt;Hashcat in mode 16500 attacks HS256 tokens directly. A captured token with a 12-character dictionary-based secret falls in minutes on modern hardware.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hashcat &lt;span class="nt"&gt;-a&lt;/span&gt; 0 &lt;span class="nt"&gt;-m&lt;/span&gt; 16500 captured.jwt /usr/share/wordlists/rockyou.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Red Sentry documented that 16-character secrets are crackable in under 24 hours with current hardware. That includes secrets that look strong but follow predictable patterns like &lt;code&gt;MyAppSecret2024&lt;/code&gt; or &lt;code&gt;jwt-secret-prod&lt;/code&gt;. Secrets derived from product names, dates, or any human-readable string fall into that category.&lt;/p&gt;

&lt;h3&gt;
  
  
  Missing Claims
&lt;/h3&gt;

&lt;p&gt;A missing &lt;code&gt;exp&lt;/code&gt; claim means the token never expires. Compromised credentials cannot be rotated. An account compromised six months ago still holds a valid token today.&lt;/p&gt;

&lt;p&gt;A missing &lt;code&gt;aud&lt;/code&gt; claim means a token issued for service A is accepted by service B. Lateral movement with no additional exploit. Just token reuse across microservices in the same organization.&lt;/p&gt;

&lt;p&gt;Each of these four vectors alone enables complete authentication bypass. Production APIs frequently present more than one at the same time. The team that got &lt;code&gt;alg:none&lt;/code&gt; wrong probably also skipped &lt;code&gt;aud&lt;/code&gt; validation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not Theoretical: CVEs With Real Production Impact
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CVE-2022-21449&lt;/strong&gt; affected Java 15, 16, 17, and 18 before the April 2022 patch. The bug was in the JVM's ECDSA implementation. A signature with &lt;code&gt;r=0, s=0&lt;/code&gt; passed mathematical validation. Any JWT signed with ES256, ES384, or ES512 on a vulnerable JVM was forgeable with those two values. CVSS 7.5, remotely exploitable, no authentication required. &lt;code&gt;com.nimbusds.jose&lt;/code&gt; fixed it in version 9.22. &lt;code&gt;com.auth0:java-jwt&lt;/code&gt; fixed it in 3.19.2. Applications running earlier versions stayed vulnerable for months after the public patch because security dependency updates are not automatic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CVE-2018-6873&lt;/strong&gt; in Auth0. The &lt;code&gt;aud&lt;/code&gt; parameter was not validated. An attacker with any Auth0 account used their own token to access any account on the platform. All they needed was the target's email address. (CVE-2018-6874, from the same API, is a distinct CSRF vulnerability — not to be confused with the audience bypass.) Complete admin takeover without knowing any victim credentials. It affected every application protected by Auth0 during that period. The impact was not limited to a bug in one specific application. It was systemic across the platform's entire customer base.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CVE-2024-54150&lt;/strong&gt; in Comcast's &lt;code&gt;xmidt-org/cjwt&lt;/code&gt;. Classic algorithm confusion, the same RS256/HS256 pattern. Published in 2024, confirming that this vector keeps getting introduced into production. Comcast's code likely went through review. It likely has tests. The vulnerability existed anyway because functional tests do not detect algorithm confusion.&lt;/p&gt;

&lt;p&gt;Red Sentry counted multiple new critical JWT-related CVEs in 2025 alone (Red Sentry, 2025), affecting cloud platforms and enterprise systems. This vulnerability class is not going away. It keeps reproducing because every new JWT implementation reinvents the same mistakes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Detect Before Attackers Do
&lt;/h2&gt;

&lt;p&gt;Manual testing requires base64 and curl. Nothing else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test alg:none:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Capture a valid JWT from any authenticated response&lt;/li&gt;
&lt;li&gt;Decode the header: &lt;code&gt;echo "&amp;lt;header_part&amp;gt;" | base64 -d&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Build a new header with &lt;code&gt;"alg":"none"&lt;/code&gt; and encode it back&lt;/li&gt;
&lt;li&gt;Assemble the token with the original payload but without a signature (end with an empty dot)&lt;/li&gt;
&lt;li&gt;Send it to the protected endpoint. If it accepts, the endpoint is vulnerable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Test algorithm confusion:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Obtain the server's public key (JWKS endpoint, &lt;code&gt;.well-known/jwks.json&lt;/code&gt;, or documentation)&lt;/li&gt;
&lt;li&gt;Re-sign the token with HS256 using the public key as the HMAC secret&lt;/li&gt;
&lt;li&gt;Send it to the endpoint&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For automation, &lt;code&gt;jwt_tool&lt;/code&gt; covers these vectors and more in batch mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 jwt_tool.py &amp;lt;token&amp;gt; &lt;span class="nt"&gt;-X&lt;/span&gt; a               &lt;span class="c"&gt;# algorithm confusion&lt;/span&gt;
python3 jwt_tool.py &amp;lt;token&amp;gt; &lt;span class="nt"&gt;-X&lt;/span&gt; n               &lt;span class="c"&gt;# none attack&lt;/span&gt;
python3 jwt_tool.py &amp;lt;token&amp;gt; &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; wordlist.txt  &lt;span class="c"&gt;# crack HS256 secret&lt;/span&gt;
python3 jwt_tool.py &amp;lt;token&amp;gt; &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="nt"&gt;-hc&lt;/span&gt; alg &lt;span class="nt"&gt;-hv&lt;/span&gt; none &lt;span class="c"&gt;# manual header injection&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Burp JWT Scanner detects CVE-2022-21449 passively. It intercepts tokens in responses and tests attack variants in active mode. In an API assessment, running the active scanner against every endpoint that returns a JWT takes minutes and covers known classes without manual configuration.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;kid&lt;/code&gt; (Key ID) field deserves separate attention. It is frequently used as a parameter in database queries without sanitization. SQLi via the &lt;code&gt;kid&lt;/code&gt; header is a documented vector with public PoCs. Variants include JKU and X5U header injection for key substitution, where the attacker points the token at an external JWKS under their control. TrustedSec has a complete methodology covering these advanced attacks.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;jwt_scanner&lt;/code&gt; (intel.mago.team/spells) automates detection of those three vectors in a single scan: it crawls exposed endpoints, detects JWTs in the Authorization header and Set-Cookie, applies algorithm confusion and &lt;code&gt;alg:none&lt;/code&gt; tests, and correlates with the CVE database to identify affected libraries by version.&lt;/p&gt;

&lt;h2&gt;
  
  
  JWT Is Not the Problem. Implementation Is.
&lt;/h2&gt;

&lt;p&gt;JWT is not inherently insecure. The strongest argument against this analysis is that the same blind-trust vector exists in session tokens: a compromised Redis instance exposes sessions the same way a weak secret exposes tokens. Misconfiguration, not the format, is the attack surface in both cases. The difference is that JWT externalizes state. Any service that accepts the token without verifying the algorithm becomes a failure point independent of the issuer. Centralized sessions fail at one point. Poorly verified tokens fail at every service that consumes them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remediation: The Checklist That Closes the Surface
&lt;/h2&gt;

&lt;p&gt;Four controls. All mandatory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Algorithm pinning on the server&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Never read the &lt;code&gt;alg&lt;/code&gt; field from the token to decide which algorithm to use for verification. The server hardcodes the algorithm. The token has no say in that decision.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// WRONG: client dictates the algorithm&lt;/span&gt;
&lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// CORRECT: server decides&lt;/span&gt;
&lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;algorithms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RS256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# PyJWT
&lt;/span&gt;&lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;public_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;algorithms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RS256&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="c1"&gt;# Never:
# jwt.decode(token, public_key, algorithms=jwt.get_unverified_header(token)["alg"])
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Real entropy for HS256 secrets&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Minimum 256 bits generated cryptographically. No passwords, phrases, or human-readable strings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl rand &lt;span class="nt"&gt;-base64&lt;/span&gt; 32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Store it in a secrets manager (AWS Secrets Manager, HashiCorp Vault, or equivalent). Rotate every 90 days. Never commit it to a repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Explicit validation of all claims&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;algorithms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RS256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;issuer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://auth.sua-api.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;audience&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.sua-api.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;clockTolerance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reject tokens without &lt;code&gt;exp&lt;/code&gt;. Reject tokens with &lt;code&gt;exp&lt;/code&gt; in the past. Reject tokens with an &lt;code&gt;aud&lt;/code&gt; that does not match your service. Reject tokens with an unknown &lt;code&gt;iss&lt;/code&gt;. The library does not do this automatically. You configure it explicitly or you skip it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Short lifetime and revocation for critical cases&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Access token: maximum 15 minutes. Refresh token: maximum 24 hours. Both require a mandatory &lt;code&gt;exp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For security events (logout, password change, privilege de-escalation), maintain a &lt;code&gt;jti&lt;/code&gt; (JWT ID) blocklist with TTL equal to the token lifetime. Stateless is convenient, not dogma. Redis with TTL handles selective revocation without complex architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Asymmetric over symmetric in multi-service architectures&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If more than one service consumes your tokens, use RS256 or ES256. The private key stays only in the issuer service. Consumer services verify with the public key. RS256/HS256 algorithm confusion is not possible in this architecture when each service hardcodes the expected algorithm.&lt;/p&gt;




&lt;p&gt;JWT security is not a library decision. It is operational discipline. The attack surface closes when algorithm pinning, secret hygiene, and claims validation are verified continuously. The same vulnerabilities reappear in 2024 because teams do not scan what they have implemented. Scanning is the missing step between implementing and trusting.&lt;/p&gt;

</description>
      <category>jwt</category>
      <category>apisecurity</category>
      <category>authentication</category>
      <category>securitytech</category>
    </item>
    <item>
      <title>GitHub OSINT: What Developers Expose Without Realizing It</title>
      <dc:creator>Rxkov</dc:creator>
      <pubDate>Sun, 06 Sep 2026 05:56:13 +0000</pubDate>
      <link>https://dev.to/rxkov/github-osint-what-developers-expose-without-realizing-it-39pc</link>
      <guid>https://dev.to/rxkov/github-osint-what-developers-expose-without-realizing-it-39pc</guid>
      <description>&lt;h1&gt;
  
  
  GitHub OSINT: What Developers Expose Without Realizing It
&lt;/h1&gt;

&lt;p&gt;In September 2022, Toyota discovered that an access key to the database of 296,019 customers had been public on GitHub since December 2017. Five years. Anyone could have used it at any point during its public existence.&lt;/p&gt;

&lt;p&gt;Attackers collect exposed credentials on GitHub in under 5 minutes. The median remediation time for teams is 94 days. The 89-day gap between those two numbers is the window where any attacker already has what they need. Any security policy that ignores this gap is theater.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 5-Minute Window That Invalidates Your Security Policy
&lt;/h2&gt;

&lt;p&gt;Reactive controls are ineffective by design. The gap between a credential exposure and automated collection by attackers is under 5 minutes; the median remediation time for teams is 94 days.&lt;/p&gt;

&lt;p&gt;Unit 42 (Palo Alto Networks) documented automated collection of IAM credentials in under 5 minutes after exposure on GitHub. The Verizon DBIR 2025 reported that the median time to remediate a leaked secret is 94 days. The real exposure window before any internal action is 89 days.&lt;/p&gt;

&lt;p&gt;GitHub found 39 million leaked secrets in 2024. GitGuardian reported 28.65 million new leaks in 2025, up 34% from the previous year. These numbers describe a system where the rate of production of new attack vectors consistently outpaces detection and response capacity.&lt;/p&gt;

&lt;p&gt;A security policy that doesn't address the gap between exposure and detection isn't a security policy. It's compliance documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You're Exposing Right Now (and Probably Don't Know)
&lt;/h2&gt;

&lt;p&gt;The exposure isn't limited to carelessly committed &lt;code&gt;.env&lt;/code&gt; files. The real surface includes complete database connection strings (DATABASE_URL, MONGO_URI), API keys for critical services (AWS, GCP, OpenAI, Stripe, Twilio) committed in configuration files, SSH private keys and TLS certificates in infrastructure repositories, and JWT secrets hardcoded to make local development easier.&lt;/p&gt;

&lt;p&gt;The 2025 data records one rapidly growing vector: AI service credentials increased 81% from the prior year. GitGuardian detected 113,000 DeepSeek API keys exposed in a single year. Each new AI service a team adopts adds a potential credential vector with no monitoring history configured.&lt;/p&gt;

&lt;p&gt;The most underrated vector is git history. A secret deleted in the current commit exists in every prior revision of the repository. The GitHub API exposes this history for public repositories without authentication.&lt;/p&gt;

&lt;p&gt;Reverting a commit doesn't remove the credential from history. The exposure window starts at the moment of the first commit, not at the moment of the attempted fix. Attackers monitoring the event stream collect the credential at the time of the original push; the subsequent revert is irrelevant to them.&lt;/p&gt;

&lt;p&gt;Internal URLs and staging endpoints in configuration files complete the surface. They reveal the internal infrastructure architecture to anyone indexing the repository, including internal services that were never designed to be publicly accessible.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Attackers Find Your Keys in Minutes Using the GitHub API
&lt;/h2&gt;

&lt;p&gt;The GitHub Search API turns the platform into a credential surveillance system accessible to anyone. Queries like &lt;code&gt;filename:.env DB_PASSWORD&lt;/code&gt;, &lt;code&gt;extension:sql mysql dump&lt;/code&gt;, and &lt;code&gt;api_key language:python&lt;/code&gt; return results in seconds, without authentication.&lt;/p&gt;

&lt;p&gt;The GitHub Events API (&lt;code&gt;api.github.com/events&lt;/code&gt;) broadcasts a public stream of every commit made on the platform. Automated tools consume this stream and scan each diff for known credential patterns. The time between a push and detection by an external monitoring system is less than the average code review.&lt;/p&gt;

&lt;p&gt;In October 2025, the OffSeq Threat Radar documented an active mass reconnaissance campaign. Ghost accounts abuse the GitHub API to map entire organizations using user agents that mimic legitimate tools. The requests return HTTP 200 without triggering alerts in conventional monitoring systems.&lt;/p&gt;

&lt;p&gt;Tools like gitrob automate complete surface mapping of an organization: repositories, members, configuration files, and commit history. The combination of GitHub Search with event stream monitoring and history scanning covers the entire public surface of an organization without any credentials on the attacker's side. Exposed credentials are consumed within a window shorter than a status meeting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Incidents That Cost More Than Any Monitoring Tool
&lt;/h2&gt;

&lt;p&gt;Toyota, Uber, and Twitch demonstrate that exposure via public repositories isn't an anomaly: it's the default mode of operation for teams without continuous monitoring. In none of these cases was detection internal.&lt;/p&gt;

&lt;p&gt;Toyota is the most documented case. In December 2017, a subcontractor committed a symmetric key to a customer database in a public repository. The key remained exposed for five years. In September 2022, GitGuardian detected the exposure and notified the company; Toyota had not identified the issue internally.&lt;/p&gt;

&lt;p&gt;The Uber breach in 2022 followed the same pattern. Credentials found in a public repository were part of the attack chain that exposed data from 57 million riders and drivers. The settlement reached USD 148 million. The original commit wasn't malicious: it was a developer who considered the repository secure.&lt;/p&gt;

&lt;p&gt;Twitch and Samsung suffered similar exposures of source code and internal credentials via public repositories. The pattern is consistent: unintentional commit, detection always external. The root cause isn't isolated human error. It's the absence of continuous monitoring that would have made the error immediately visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detection Tools Exist, But Arrive Too Late When Used Only in CI/CD
&lt;/h2&gt;

&lt;p&gt;Gitleaks and TruffleHog are effective, but when run only in the CI/CD pipeline, they detect exposures that are already indexed, collected, and potentially exploited.&lt;/p&gt;

&lt;p&gt;Gitleaks uses regex patterns with TOML configuration, operates without a network connection, and runs as a pre-commit hook in milliseconds. It blocks the commit before the secret reaches the repository. TruffleHog adds entropy analysis and makes real API calls to verify whether the detected credential is still active.&lt;/p&gt;

&lt;p&gt;The configuration that provides layered coverage combines Gitleaks at pre-commit (prevention) with TruffleHog in CI/CD (detection of secrets that passed the first layer). AWS Labs' git-secrets configures local hooks to block specific AWS service patterns before the commit. The three tools combined have over 51,000 stars on GitHub and cover the most common credential patterns in production.&lt;/p&gt;

&lt;p&gt;The critical gap in all three: none monitors what's already public in forks, gists, or third-party code that copied the repository. GitHub Secret Scanning (Advanced Security), even when enabled for the organization, covers only the repository where it's configured. When a developer creates a public fork of a repository that contains credentials in its history, those credentials fall outside the scope of any CI/CD scanner configured on the original repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Continuous Monitoring Is the Only Response That Closes the 89-Day Gap
&lt;/h2&gt;

&lt;p&gt;The difference between proactive and reactive detection determines whether a leaked secret becomes a contained incident or a breach requiring regulatory notification. Continuous monitoring means active scanning of GitHub search, event streams, and paste sites in real time, not a weekly scan of the main repository.&lt;/p&gt;

&lt;p&gt;The surface to monitor includes the organization name, corporate domains, proprietary API key patterns, and corporate email addresses. Any of these identifiers can appear in third-party repositories, gists, Pastebin, or leak forums. An operationally useful alert arrives in minutes, not days.&lt;/p&gt;

&lt;p&gt;intel.mago.team automatically monitors the exposure surface: GitHub, GitLab, paste sites, and leak forums, with alerts that allow credential rotation before the exploitation window closes. Knowing about an exposure the same day is the difference between a contained incident and a breach report that goes to the regulator.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Checklist That Closes the Immediate Gaps
&lt;/h2&gt;

&lt;p&gt;Durable remediation doesn't depend on consistent human discipline. It depends on automation that blocks the vector before the commit and on visibility into what's already outside your control.&lt;/p&gt;

&lt;p&gt;The audit starts with the existing history: &lt;code&gt;git log --all -p | grep -iE 'api_key|password|secret|token'&lt;/code&gt; on the current repository identifies past exposures. Every credential with suspected exposure needs to be rotated before any other action, even if the commit has already been reverted.&lt;/p&gt;

&lt;p&gt;For history rewriting, &lt;code&gt;git-filter-repo&lt;/code&gt; removes committed secrets permanently and replaces the deprecated &lt;code&gt;git filter-branch&lt;/code&gt;. Installing Gitleaks as a pre-commit hook via the pre-commit framework takes two commands and blocks commits matching the configured patterns.&lt;/p&gt;

&lt;p&gt;Environment variables in a local &lt;code&gt;.env&lt;/code&gt; with a global &lt;code&gt;.gitignore&lt;/code&gt; configured eliminate the most common vector for accidental exposure. GitHub Secret Scanning enabled at the organization level is free for public repositories. External continuous monitoring covers the gap that none of these internal measures can reach: what has already been copied, forked, or indexed before any remediation action.&lt;/p&gt;

&lt;p&gt;The commit has been made. The question is: who found it before you did?&lt;/p&gt;

</description>
      <category>osint</category>
      <category>github</category>
      <category>credenciais</category>
      <category>recon</category>
    </item>
  </channel>
</rss>
