Seratus

Entry 15 · · 9 min read

Personal Data in Databases: Oracle, SQL Server and PostgreSQL

Column-level sampling, unmasked TCKN and IBAN columns, the role-to-table permission graph, and why not found in the sample is never a risk decision.

  • databases
  • Oracle
  • SQL Server
  • PostgreSQL

Databases are where personal data sits in its densest and most structured form; they are also where discovery tools look least. A payroll spreadsheet on a file server is one document. A “CUSTOMER” table in an Oracle schema is eight million rows, sixty columns and, usually, out-of-date documentation about which of those columns hold a national ID number (TCKN) and which hold an IBAN. Discovering personal data in a database is not document scanning; it is asking, column by column: what is here, who can read it, and what do we say when we do not find anything.

Why databases are a separate problem

File-based discovery opens a document, extracts its content and searches for patterns. In a database that approach fails for two reasons. The first is scale: a single table can hold more records than an entire file share, and reading it in full loads the production system. The second is structure: personal data does not sit inside a text but in a specific column with a specific data type. A column named “TCKN” announces itself; a column named “REF_NO” or “EXT_ID” can hold TCKNs too, and its name says nothing.

A third difference is the access model. On a file server, permissions are granted at file or folder level; in a database they are granted at role and table level and rarely restricted per column. When a role is granted “read the CUSTOMER table”, it reads all sixty columns; no separate decision was ever made about the TCKN column.

These three differences mean database discovery needs its own method: column-level sampling, pattern detection combined with data type and name clues, and role-to-table permission mapping. The outcome bears directly on the VERBİS inventory: the Data Controllers Registry page published by KVKK, Türkiye’s data protection authority, describes an inventory that asks which data category is held for which purpose, and the finding “the CUSTOMER table holds TCKNs” is that question’s answer from real discovery. This article is not legal advice.

How column-level sampling works

Seratus models a database as tables (containers) and columns (scanned units). For each table the column list and data types are read from metadata. A sample is then taken from each column: not every row, but the values of a set number of rows in that column. Turkish-specific detection rules run on the sample: TCKN, tax ID number, TR IBAN, card number, email, TR phone, vehicle plate, ICD code and others.

Sampling achieves three things. It does not load the production database; instead of reading all eight million rows, a limited number is read. Scan time becomes largely independent of table size, so large schemas are scanned in reasonable time. And because the connection stays read-only, the scan changes nothing.

The result of sampling is a finding per column: which pattern was seen, in what share of the sampled values, at what confidence percentage. The finding is presented with masked sample values, so the reviewer confirms what the column holds without seeing a full TCKN. Every rule has its own accuracy and false-positive reduction program: an eleven-digit number may be a TCKN, but one that fails the check digit is not, and that validation reduces false positives. Details are on the Turkish detection accuracy page.

Column name and data type are context clues that support detection. A DATE column named “BIRTH_DATE” is a personal data candidate even without content sampling; a CLOB column named “NOTES” holds unstructured text and can contain anything. Seratus shows these clues next to the finding; the decision is confirmed by a human.

Unmasked TCKN and IBAN columns

The most frequent database finding is unmasked identity and financial columns. Three patterns are typical.

Plain values in the primary table. The TCKN sits in clear text in the customer or employee table. That may be an application requirement; what usually goes unnoticed is that the same column travels, equally exposed, to the reporting role, the test environment and the analytics copy.

Values repeated in derived tables. Integration tables, log tables and temporary tables copy the TCKN from the primary table. A staging table called “STG_CUSTOMER_2021” is left over from a migration four years ago and still carries eight million TCKNs; no application reads it, but every reporting role can.

Misnamed columns. TCKNs sit in columns called “REF_NO”, “EXT_ID” or “DESCRIPTION”, sometimes inside a text. A name-based inventory never sees them; only content sampling finds them.

The pattern for IBANs is similar: TR IBANs sit in clear text in payroll, supplier payment and refund tables, and copies of those tables multiply in accounting integrations. At an insurance company, if the monthly exports of the claims payment table have accumulated in a staging schema for years, the number of IBANs is several times that of the primary table.

Remediation for these findings may be masking, access narrowing or disposal. Which one depends on the purpose and period for which the column is held, and that decision belongs with the retention calendar in the KVKK compliance module. For the four-year-old TCKN copy in the staging table, the right action is not masking but disposal.

The role-to-table permission graph

Finding a TCKN in a column does not, on its own, produce a risk ranking. The ranking comes from who can read that table. Seratus extracts a role-to-table permission graph for every database: which user holds which role, which role can read which table, and which columns in those tables hold personal data.

The model differs across platforms, but the question is the same. In Oracle, roles, system privileges and object privileges are evaluated together; a broad system privilege such as “SELECT ANY TABLE” means access to every table even if no object privilege was ever granted. In SQL Server, the db_datareader fixed role reads every table in the database; schema-level SELECT grants and server-level sysadmin membership are computed separately. In PostgreSQL, roles nest and GRANT statements can be made at schema, table and column level; a grant to the “PUBLIC” role is a grant to everyone. In MySQL and MariaDB there are user-host pairs and global, database and table-level privileges.

The findings that stand out in the graph are familiar: the application service account that reads the whole schema, the reporting role granted to everyone, the still fully privileged account of a manager who left years ago, SELECT granted to PUBLIC. Joined with the columns holding personal data, these become statements such as “140 users can reach the TCKN column”, and they sit in the same access and exposure graph as file server ACLs and Microsoft 365 sharing links.

The “not found in the sample” nuance

Sampling-based detection has a limit that must be stated plainly: nothing definitive can be said about a pattern that did not appear in the sample. If the first thousand rows of a column hold no TCKN, that does not mean the column holds no TCKN; it means the pattern was not seen in that sample. The column may be mostly empty and populated only for certain records; older records may use a different format; the TCKN may be kept only for a particular customer segment.

Seratus shows this as a separate result type, “not found in the sample”, and states explicitly inside the product that this is a technical finding, not a risk decision. Deciding that a column holds no personal data is a human decision made with the sample alongside column name, data type, application documentation and the data owner’s knowledge. The product does not make that decision; it shows the supporting context and records the decision taken with who, when and why.

The consequence for audits matters. The sentence “we scanned our databases and found no personal data” is misleading when the method is not stated. The accurate statement is: “these columns of these tables were scanned at this sampling rate; these patterns were found in these columns; in these columns nothing was found in the sample and they were judged not to hold personal data for this reason.” The Seratus source-level detail report produces that statement.

Read-only connection and operational impact

The first question a database administrator asks of a discovery tool is “what does it do to production”. The answer has three parts. The connection is read-only: a thin client on Oracle with no extra driver, the db_datareader fixed role on SQL Server, an account with SELECT only on PostgreSQL and MySQL/MariaDB. No write, DDL or configuration change is made; scan queries read only metadata and a limited sample.

Load is bounded: sampling replaces full table scans, and scans can be scheduled into maintenance windows. An interrupted scan resumes where it stopped; a dropped connection or a restart does not cause the schema to be rescanned from the beginning.

There is no agent. Nothing is installed on the database server; Seratus connects over the network from its own server inside the organization. Data never leaves the Seratus server for any external service; the deployment is on-premises and air-gap compatible. Platform details are on the Oracle, SQL Server and PostgreSQL/MySQL pages.

How Seratus closes these gaps

Gap: knowledge of which column holds TCKN, IBAN or health data rests on stale documentation. Seratus runs Turkish-specific detection rules on column-level samples; each finding comes with a confidence percentage and a masked sample and goes to human confirmation together with column name and data type clues.

Gap: forgotten copies in staging, log and integration tables. The whole schema is scanned; not only the tables the application knows, but derived tables left behind years ago produce findings and are tied to the retention calendar.

Gap: table access not joined with column content. The role-to-table permission graph joins the tables each role can reach with the columns holding personal data; broad system privileges, PUBLIC grants and departed-user accounts are flagged as separate findings.

Gap: “not found” treated as “clean”. “Not found in the sample” is a separate result type, and the product states explicitly that it is not a risk decision; the decision is taken by a human and recorded.

Gap: write permissions requested for discovery. Connections are read-only (thin client, db_datareader, SELECT); actions such as masking, access narrowing and disposal run under a separate right, assigned to an owner, with approval, and are written to the audit log.

Gap: the VERBİS inventory does not reflect database reality. Database findings enter the VERBİS inventory from real discovery alongside file and Microsoft 365 findings; the “data not in the inventory” gap appears in the KVKK readiness score.

Takeaway

Discovering personal data in a database is a column-by-column inquiry: what is here, who can read it, what do we say when we do not find anything. Sampling makes that inquiry possible without loading the production system; the role-to-table permission graph turns a finding into a risk; the “not found in the sample” nuance keeps the organization’s statement in an audit truthful. A read-only connection guarantees that all of it happens without touching the database.

Questions on this entry

1

How does Seratus scan databases?

Read-only: through a thin client on Oracle, at db_datareader level on SQL Server, with a read-only account on PostgreSQL and MySQL/MariaDB. It treats the table as the container and the column as the scanned unit, and runs Turkish-specific detection rules on a sample taken from each column.

2

Does sampling-based detection read every row?

No. A sample is taken from each column and patterns are searched within it. That allows tables with billions of rows to be scanned without loading the production database. The trade-off is that nothing definitive can be said about a pattern that did not appear in the sample, and the product states that explicitly.

3

What does not found in the sample mean?

Only that the pattern was not seen in that sample; not that the column holds no personal data. It is a technical finding, not a risk decision. Seratus shows it as a separate result and leaves it to human judgement, together with context clues such as column name and data type.

4

What is the role-to-table permission graph?

A map of which tables each database role and user can read, joined with the columns where personal data was found. An application role that reads the whole schema, or a reporting role granted to everyone, appears in this graph as broad access to TCKN or IBAN columns.

5

Does Seratus mask data inside the database?

Masking is a remediation action: it answers a finding, is assigned to an owner, runs under a separate approval right and is written to the audit log. The discovery connection is read-only and separate from the action permission.

DOSSIER 001

See this on your own data

Request a demonstration. We will walk through discovery, risk ranking, remediation and the KVKK screens with your scenarios.