Postgres closed 28 security holes in one release, six of them found by AI, and two of the fixes change how your database behaves
PostgreSQL 18.6, 17.11, 16.15, 15.19 and 14.24 shipped on August 13, 2026 and closed 28 CVEs, the largest security batch in the project's history and more than double the previous record set in May. Fourteen are rated CVSS 8.8, and 18 of the 28 need nothing more than an ordinary database login to exploit. Six of them, all rated 8.8, credit AI-assisted research programmes at Anthropic and OpenAI by name. Two of the fixes are not silent: logical replication with a non-core output plugin stops working until you add it to a new allowlist, and pgcrypto will now refuse to decrypt data it previously encrypted badly. Updated August 30: Amazon RDS and Azure have now shipped the patched minors, Google Cloud SQL still has not, seventeen days after the release.
We have written that Postgres is our default database and that its extension ecosystem is the main reason. That position has not changed. But on August 13 the project shipped a maintenance release across every supported branch that closes 28 security vulnerabilities, and it is worth being precise about what that number means, because most of the coverage has treated it as a routine patch note.
It is not routine. Twenty-eight CVEs is the largest security batch PostgreSQL has ever shipped. The previous record was 11, set three months earlier in the May 14 release. For scale, the project’s own security page lists exactly seven CVEs for each of 2023, 2024 and 2025, and 44 so far in 2026.
Three things about this release matter more than the headline count, and only one of them is fixed by updating.
1. Eighteen of the 28 need nothing but a login
This is the part that changes the risk calculation, and it is buried in the CVSS vectors rather than the descriptions.
We pulled all 28 entries from the project’s own security page and sorted them by attack vector. Here is the distribution of the privilege required:
| Privileges required | Count | What it means in practice |
|---|---|---|
Low (PR:L) |
18 | Any ordinary database user with a normal SQL connection |
None (PR:N) |
6 | No account at all, though four of these need a user to be tricked into something |
High (PR:H) |
4 | Superuser or database owner |
Fourteen of the 28 carry the batch’s top score of CVSS 8.8, and twelve of those fourteen are PR:L. The project’s own wording for several of them is worth reading literally: the flaw “allows the query author to execute arbitrary code as the operating system user running the database.”
The query author. Not an administrator, not someone who has already broken in. Anyone who can run a query.
That matters because of an assumption almost every small application makes. The database sits behind a firewall, only the application server can reach it, so a database vulnerability is a second-order problem. That reasoning holds for bugs requiring superuser. It does not hold here. The relevant question is now: who or what can get a query to your database? Which usually means:
- Your application’s own connection, if there is a SQL injection anywhere in the codebase. A bug that previously leaked a table now becomes code execution on the database host.
- A read-only analytics or reporting login handed to a BI tool, a contractor, or a dashboard.
- A per-tenant or per-customer database role, if you built multi-tenancy that way.
- On shared or low-cost managed hosting, any other tenant on the same instance.
The bugs are in unglamorous, heavily-used functions: to_char(), regular expression matching, tsvector and tsquery full-text search, the ascii() function, the query planner’s selectivity estimator. These are not exotic code paths. They are what ordinary applications call thousands of times a minute.
2. Six of them were found by AI research programmes, and the project says so out loud
Read the release notes rather than the CVE list and the reason for the spike stops being a mystery. PostgreSQL credits reporters by name, and six of the 28 credits name an AI-assisted security research effort:
| CVE | CVSS | Credited to |
|---|---|---|
| CVE-2026-15741 | 8.8 | Ben Morris, in collaboration with Claude and Anthropic Research |
| CVE-2026-15742 | 8.8 | Ben Morris, in collaboration with Claude and Anthropic Research |
| CVE-2026-16239 | 8.8 | Ben Morris, in collaboration with Claude and Anthropic Research, and Peter Geoghegan |
| CVE-2026-14680 | 8.8 | Amy Burnett, OpenAI Codex Security |
| CVE-2026-16238 | 8.8 | Amy Burnett, OpenAI Codex Security |
| CVE-2026-14669 | 8.8 | eleven reporters including Amy Burnett of OpenAI Codex Security and Tan Zhen of AntAISecurityLab |
All six are rated 8.8. All six are arbitrary code execution.
The last row is the one to sit with. CVE-2026-14669, a buffer overrun triggered by a long time zone abbreviation in to_char(), was reported independently by eleven separate people and teams. That function has been in Postgres for over twenty years. Nobody found this bug for two decades, and then eleven parties found the same one at roughly the same time.
That is what a step change in tooling looks like from the inside. The honest reading is not that Postgres got worse; the code did not change. It is that the cost of finding this class of bug in a large C codebase fell sharply, and a backlog that had been accumulating quietly since the 1990s is now being drained in public. The release note for that same CVE adds a line the project does not write casually: “exploits leading to arbitrary code execution have been reported.”
We think this pattern generalises, and it is the strategic point of this post. Every mature open-source C project in your stack, and there are more of them under your application than you probably realise, is about to go through the same thing. Expect CVE counts in projects like this to stay elevated for a while. That is a sign the process is working, not a sign the software is rotting, but it does mean the operational cost of running self-hosted infrastructure just went up, because the patch cadence goes up with it.
3. Two fixes change behaviour, and updating without reading will break things
This is the part almost nobody has written about, and it is where a routine apt upgrade turns into an incident.
Minor Postgres releases are famously boring: stop the server, swap the binaries, start it again, no dump and restore. That is still true here for the data. It is not true for two configuration behaviours.
Logical replication will stop, if you use a third-party output plugin
The fix for CVE-2026-6471 introduces a new server parameter, output_plugin_libraries, which is an allowlist. Previously a replication user could load any library on disk as a logical decoding output plugin, which is a straightforward path to running arbitrary code. Now only plugins on the list are permitted, and the default list contains exactly two entries: pgoutput and test_decoding.
If you use anything else, and change-data-capture pipelines routinely do, replication stops working the moment you update. You have to add it explicitly:
output_plugin_libraries = 'pgoutput, test_decoding, my_trusted_decoder'
There is a second trap attached. pg_upgrade --check will now fail if the new cluster’s output_plugin_libraries does not permit the plugins used by logical replication slots on the old cluster, when migrating from version 17 or later. Set it on the new cluster before you run the upgrade, not after it fails.
pgcrypto will refuse to decrypt data it never really encrypted
CVE-2026-14663 is the most uncomfortable item in the release, and it is the one worth reading twice if you have ever used pgp_sym_encrypt() on a column.
If OpenSSL rejected the cipher you asked for, because it was running in FIPS mode or the legacy provider was not loaded, pgcrypto did not notice the failure. It XOR’d the unencrypted block with the plaintext and returned it as ciphertext. The project’s own description is blunt: this rendered the “encryption” trivially breakable.
This affects the deprecated and non-FIPS algorithms specifically: blowfish (bf), twofish, cast5 and 3des.
Two consequences, in order of importance. First, if any of your data was encrypted this way, it was never protected, and it needs to be treated as though it were stored in the clear. If that data is personal health information under PHIPA or personal information under PIPEDA, that is a determination to make with your privacy officer, not something to quietly re-encrypt and move on from. Second, after updating, pgcrypto will by default refuse to decrypt affected messages. To recover the data you need the new option:
pgp_sym_decrypt(encrypted_column, 'your key', 'ignore-cipher-failure=1')
Then re-encrypt with a modern algorithm. There is a catch the release notes flag explicitly: OpenSSL has to behave the same way it did when the faulty messages were written. If the set of unsupported algorithms has shifted in the meantime, this recovery path does not work.
The third behavioural change is smaller. The fix for CVE-2026-6464 teaches psql to skip in-line data after a COPY ... FROM STDIN that failed at startup, rather than executing that data as SQL commands. If you have test scripts that deliberately exercise a failing COPY ... FROM STDIN, each one now needs a \. terminator line.
The extension question, honestly
Six of the 28 CVEs are in contrib modules rather than the core server: pgcrypto, pg_stat_statements, pg_trgm, fuzzystrmatch, refint and amcheck. Two more of the bug fixes in the same release, in btree_gist and ltree, require a manual REINDEX afterwards or you are left with a corrupt index.
We argued a fortnight ago that the extension ecosystem is Postgres’s best feature, because it collapses five pieces of infrastructure into one. We still believe that, and this release does not undo the argument. Consolidating a vector store, a job scheduler and a geospatial engine into your existing database is still fewer things to secure, back up and patch than running four services.
But the honest version of that argument has a second half we underweighted. Every CREATE EXTENSION adds C code that runs inside your database process, with the privileges of the database process. A buffer overflow in fuzzystrmatch is not sandboxed away from your customer table; it is arbitrary code execution as the OS user running Postgres. The consolidation is real and it is still worth it, but it consolidates attack surface along with everything else. The practical implication is small and specific: audit what is actually installed. Run this on each database:
SELECT extname, extversion FROM pg_extension ORDER BY extname;
Anything on that list you cannot explain the business reason for should come off. pg_stat_statements earns its place on almost any production system. refint, a compatibility shim from the era before Postgres had real foreign keys, almost certainly does not, and it carries an 8.8 in this release.
Whether you are actually patched, and the version numbering trap
Two things make this harder to check than it should be.
First: 18.5 does not exist. The security pages on postgresql.org list the fixed version for the 18 branch as 18.5. No such release ever shipped. A regression was found after the release was wrapped, so the project skipped straight from 18.4 to 18.6. The release notes say so plainly: “18.5 was never released, due to a regression discovered post-wrap.” If you are checking a CVE page and going looking for 18.5, you will not find it. The versions that carry the fixes are 18.6, 17.11, 16.15, 15.19 and 14.24.
Second: your managed provider probably has not shipped it yet. We checked all three major managed platforms this morning, ten days after the release. Every one of them still advertises the pre-patch minor version:
| Platform | Latest version offered, checked August 23, 2026 | Patched version |
|---|---|---|
| Amazon RDS for PostgreSQL | 18.4, 17.10, 16.14, 15.18 | 18.6, 17.11, 16.15, 15.19 |
| Google Cloud SQL | 18.4, 17.10, 16.14, 15.18, 14.23 | 18.6, 17.11, 16.15, 15.19, 14.24 |
| Azure Database for PostgreSQL flexible server | 18.4, 17.10, 16.14, 15.18, 14.23 | 18.6, 17.11, 16.15, 15.19, 14.24 |
One caveat, and it matters: this does not prove those platforms are vulnerable. All three maintain their own builds and can and sometimes do backport individual security fixes without moving the advertised minor version. What the table shows is that the version string your console reports is not evidence either way, so if you are on managed Postgres the answer has to come from the provider rather than from SELECT version().
Cloud SQL’s documented policy is to support new minor versions within 30 days of general availability, which puts its deadline around September 12. Azure patches during its monthly maintenance deployments. RDS ships minors on its own cadence and applies them in your configured maintenance window, which means even after Amazon publishes 18.6 you may be waiting weeks longer for it to reach your instance unless you apply it manually.
Update, August 28, 2026. Two of the three have now shipped it. We re-checked the same three provider pages this morning, fifteen days after the release:
| Platform | Latest version offered, checked August 28, 2026 | Status |
|---|---|---|
| Amazon RDS for PostgreSQL | 18.6, 17.11, 16.15, 15.19 | Available. AWS announced support on August 25, 2026 |
| Azure Database for PostgreSQL flexible server | 18.6, 17.11, 16.15, 15.19, 14.24 | Available. New servers are created on these versions |
| Google Cloud SQL | 18.4, 17.10, 16.14, 15.18, 14.23 | Still pre-patch |
Two things follow from that. If you are on RDS, the version being available is not the version being applied: Amazon publishes the minor and then rolls it into your instance during your configured maintenance window, so if you want it now you apply it now rather than waiting. Azure applies minors itself during its monthly deployment, so check which deployment yours falls in. And if you are on Cloud SQL, you are the one still waiting, with Google’s own 30-day policy putting the deadline around September 12. The email in the section below is still the right move there, and the question to ask is whether Google has backported the fixes into the 18.4 build it is serving.
Re-checked August 30, 2026. Cloud SQL’s supported-versions page still lists 18.4, 17.10, 16.14, 15.18 and 14.23, seventeen days after the release. Nothing has changed since the August 28 check. If you run Cloud SQL, you are now inside the last two weeks of Google’s own 30-day window, and the sensible read is to plan for the deadline rather than the announcement.
What to actually do
If you self-host Postgres, this is this week’s work, not next quarter’s:
- Check your version:
SELECT version(); - Before updating, check whether you use logical replication with a non-core output plugin.
SELECT slot_name, plugin FROM pg_replication_slots;will tell you. Anything other thanpgoutputortest_decodingneeds adding tooutput_plugin_librariesinpostgresql.conf. - Check whether pgcrypto is installed and whether anything was encrypted with blowfish, twofish, cast5 or 3des. If so, read the section above before you update, not after.
- Update to 18.6, 17.11, 16.15, 15.19 or 14.24.
- If you have GIN indexes, check for the corrupt
reltuplesvalues a separate bug in this release could leave behind, which silently stops autovacuum and autoanalyze from processing the table:
SELECT DISTINCT t.oid::regclass, t.reltuples
FROM pg_class t
JOIN pg_index i ON t.oid = i.indrelid
JOIN pg_class ic ON i.indexrelid = ic.oid
WHERE t.relhasindex AND ic.relam = 2742;
Run ANALYZE on anything that comes back with a nonsensical value, including Infinity or NaN.
- If you use
btree_giston float or bit columns, orltree, reindex those indexes.
If you are on managed Postgres, you cannot do any of the above, and the whole job is one email. Ask your provider two specific questions rather than one general one: has the August 13 security release been applied to my instance, and if not, on what date will it be. A provider that has backported the fixes without bumping the version number will say so. A provider that cannot answer has told you something useful.
Either way, one thing worth doing today regardless of version. Look at who holds a database login. Given that 18 of these 28 flaws need only an ordinary account, every unused role, every credential in an old contractor’s .env file, and every over-privileged reporting user is now worth more to an attacker than it was two weeks ago.
SELECT rolname, rolsuper, rolcreaterole, rolcreatedb, rolcanlogin
FROM pg_roles WHERE rolcanlogin ORDER BY rolsuper DESC, rolname;
One more date
The same announcement confirms that PostgreSQL 14 reaches end of life on November 12, 2026. After that there are no more security fixes for the 14 branch, ever, and given the rate at which this class of bug is now being found, an unsupported Postgres in 2027 is going to age badly and quickly. If you are on 14.x, 14.24 is close to the last patch you will get. A major version upgrade needs planning now rather than in October.
If you are not sure what your application is running or who has a login to it, send us a note. Working out which of these apply to a specific setup takes about half an hour and we are happy to do it.
Sources
- PostgreSQL 18.6, 17.11, 16.15, 15.19, 14.24 and 19 Beta 3 Released, PostgreSQL Global Development Group, August 13, 2026
- PostgreSQL 18.6 release notes, including the reporter credits and the migration notes quoted above
- PostgreSQL security information, the full CVE table with CVSS vectors, retrieved August 23, 2026
- CVE-2026-6471, CVE-2026-14663, CVE-2026-14669 and CVE-2026-14676, PostgreSQL project advisories
- Amazon RDS for PostgreSQL supported versions, retrieved August 23, 2026 and re-checked August 28, 2026
- Amazon RDS for PostgreSQL supports minor versions 18.6, 17.11, 16.15, 15.19, and 14.24, AWS, August 25, 2026
- Cloud SQL for PostgreSQL database versions, retrieved August 23, 2026
- Supported versions of PostgreSQL in Azure Database for PostgreSQL flexible server, retrieved August 23, 2026