Home  ›  Insights  ›  Denmark’s 123456 breach for Salesforce admins
Field Note

Denmark’s 123456 breach for Salesforce admins: the call

One small supplier, one former employee’s password, and 8.75 million national register records gone in three weeks. The bill caught it, not the security team. Every Salesforce org has suppliers with the same kind of key. Here is the arithmetic, and the audit to run this week.

The call

PILOT: run a one-week audit of every integration user and connected app, and a records-returned alert in shadow mode. The Salesforce admin owns the audit; the CISO owns the decision on what gets switched off.

Denmark’s Central Person Register (CPR) announced on 5 October that a private company’s legitimate access had been misused to extract data on about 8.8 million people (independent, BleepingComputer). On 9 October the attacker told Politiken he got in with the password 123456 (independent, The Local; Ingeniøren). The story topped Hacker News on 10 October with 382 points (independent, Hacker News).

What it is, and is not

It is a supplier-access breach. The company, reported as Pays ApS, was legally allowed to query the register. At least three of its accounts, including the administrator, used 123456 (independent, DataBreaches.net, citing Politiken and Ritzau). The password came from a former employee and had appeared in an earlier leak (independent, The Local). Access ran from 10 September for 21 days and 17 hours, preliminary (independent, DataBreaches.net). Authorities reportedly found it through an unusually large invoice (independent, Ground News). The minister said security “has not been good enough” (independent, The Local).

It is not a break-in to the register itself, a zero-day, or AI. Nobody beat the system. A supplier with a valid login asked it for everything, one lookup at a time, and the system answered. That is why it matters to a Salesforce buyer: your org answers integration users the same way.

The number nobody is quoting

Coverage leads with 123456. The better question is how loud this theft would be inside a Salesforce org. Answer: barely audible.

FigureValueLabel
Records taken8,749,975independent, citing Politiken
Access window21 days 17 hours = 521 hoursindependent
Extraction rate8,749,975 ÷ 521 = 16,795 an hour, 280 a minute, 403,070 a dayour maths
Salesforce REST query batchDefault and maximum 2,000 records per callvendor
Calls to pull the same volume403,070 ÷ 2,000 = 202 a day; 8,749,975 ÷ 2,000 = 4,375 in totalour maths
Enterprise Edition daily API allowance100,000 + 1,000 per Salesforce licencevendor
Share of the minimum allowance202 ÷ 100,000 = 0.2% a day; the whole 4,375 is 4.4% of one dayour maths
If compressed into 10 days, as reported437 calls a day, 0.44%our maths

Copy this theft into Salesforce and no API limit alert fires. A 500-licence org has 600,000 calls a day; 202 is 0.03% (our maths). And Salesforce sends no per-lookup invoice, so the control that caught Denmark’s breach does not exist for you. The only signal is volume of records returned by one user, and you only see it if you log it and keep the logs. With Event Monitoring or Shield, log files are kept for one year by default (vendor, Salesforce).

Missing:

  • The supplier’s confirmed identity, from the authorities rather than the press.
  • Whether the register enforced MFA, IP restrictions, or rate limits on supplier logins.
  • Why a former employee’s password still worked.
  • Why no alert fired on volume in 22 days.
  • How many other companies hold the same kind of access, and when their access was last reviewed.
  • The full list of exposed fields beyond name, address, and CPR number.

Who owns what

The supplier’s password hygiene is their job. Who holds a key to your org is ours.

In Salesforce, every integration should run as its own integration user with the API Only User permission, a profile or permission set that reads only the objects it needs, and Login IP Ranges set to the supplier’s addresses. Prefer OAuth through a connected app or External Client App over a stored password, so revoking a supplier is one switch. Outbound calls from your org belong in Named Credentials, not in code. Put the alert on rows returned per user per day, not on API call counts, and route it to someone who will read it.

Monday morning

  • Step 1. Export every active user with API access and every connected app, with owner and last login. Owner: Salesforce admin.
  • Step 2. Deactivate anything with no named business owner or no login in 90 days. Owner: CISO signs off; admin executes.
  • Step 3. For what remains, confirm OAuth, Login IP Ranges, and least-privilege object access. Owner: Salesforce architect.
  • Step 4. Set a daily rows-returned baseline per integration user and alert at three times it, in shadow for one week. Owner: security operations.
  • Step 5. Ask each supplier, in writing, how they offboard staff who hold your credentials. Owner: vendor management.

What would change our call: we move this to WATCH if the Danish investigation shows a register-specific failure with no parallel in a CRM.

Living page. Last updated 12 Oct 2026. What would change our call: the official investigation findings.

Frequently asked questions

What happened in the Danish CPR breach?

An attacker used the login of a small IT company with legal access to Denmark’s Central Person Register to extract names, addresses, and CPR numbers for about 8.75 million people, living, emigrated, and deceased. Access ran from 10 September for about 21 days and 17 hours. Danish media report that at least three of the company’s accounts, including its administrator, used the password 123456.

Could the same thing happen to a Salesforce org?

The pattern can. Any third party with an integration user and read access to Contacts or Accounts holds a key to your customer records. Pulling 8.75 million records through the REST API takes about 4,375 query calls at 2,000 records each, which is 4.4% of one day’s minimum Enterprise Edition allowance (our maths). API limit alerts will not fire.

What should a Salesforce admin check first?

List every integration user and connected app, who owns each one, and when it last logged in. Remove the ones nobody owns. Then confirm each remaining integration logs in through OAuth from known IP ranges with the narrowest object access it needs, and put an alert on records returned per day, not on API calls.