All guidesSupabase RLS and keys
On this page

Supabase Security Advisor

Supabase

What does sensitive_columns_exposed mean, and how do I fix it?

By Ohad Krispin, founder of VibeRaven · LinkedIn

sensitive_columns_exposed is the Supabase Security Advisor error (lint 0023) for a table the Data API exposes, with row level security off, that has a column named like a secret or personal identifier, such as password, token, api_key or ssn. Anyone with your project URL and public key can read that column. The fix is the one for rls_disabled_in_public: enable RLS on the table and add a policy for each read your app makes, or move the column to a table with RLS.

psql output from a Supabase Postgres 17.11 container: lint 0023 flags the ssn column of public.profiles, and the anon role reads it. After row level security is enabled, the lint returns nothing and anon reads no rows.
psql output from a Supabase Postgres 17.11 container: lint 0023 flags the ssn column of public.profiles, and the anon role reads it. After row level security is enabled, the lint returns nothing and anon reads no rows.

Checklist

How to fix sensitive_columns_exposed, then check the fix

  1. 01
    Enable row level security on the table and add a policy for each read, in a migration:
    alter table public.profiles enable row level security;
    
    create policy "Users read their own profile"
    on public.profiles for select
    to authenticated
    using ( (select auth.uid()) = id );
  2. 02
    If the browser never needs the column, move it to its own table with RLS on and no policy, so only your server's secret or service role key reads it. Copy the data across before you drop the column:
    create table public.profile_secrets (
      id uuid primary key references public.profiles (id) on delete cascade,
      ssn text
    );
    alter table public.profile_secrets enable row level security;
    
    alter table public.profiles drop column ssn;
  3. 03
    Check the columns the lint does not catch. It matches whole column names only: on a Supabase Postgres 17.11 container on 22 October 2026 it flagged ssn but not password_hash in the same table. This lists every column of every public table with RLS still off:
    select c.relname as table_name, a.attname as column_name
    from pg_catalog.pg_class c
    join pg_catalog.pg_namespace n on n.oid = c.relnamespace
    join pg_catalog.pg_attribute a on a.attrelid = c.oid and a.attnum > 0 and not a.attisdropped
    where n.nspname = 'public'
      and c.relkind = 'r'
      and not c.relrowsecurity
    order by 1, 2;
  4. 04
    Run the Advisor again. The table should be gone from both this lint and rls_disabled_in_public:
    supabase db advisors --linked --type security
  5. 05Read the column as a signed-out visitor. On the container, the anon role read another user's ssn before the fix and 0 rows after it.

Exceptions

When sensitive_columns_exposed is safe to ignore

The lint's source flags a table only when RLS is off, the schema is exposed to the Data API and anon or authenticated can select from it. Those are the conditions of rls_disabled_in_public, so every table this lint reports is reported there too.

A column named like a secret that holds nothing private

A course certificate title or a public share token matches the name list. The table still has RLS off, so fix that anyway; renaming the column alone only hides the warning.

A table only your server reads

Enable RLS and add no policy, or revoke the browser roles' grants. On the container, revoking select from anon and authenticated cleared the lint. The lint checks only select, though, and a select-only revoke leaves inserts open, so revoke every grant.

revoke all on table public.profiles from anon, authenticated;

Related

Sources

Official docs behind this checklist

FAQ

FAQ for builders before launch

Which column names does it match?

About 70 names in six groups: credentials such as password, secret, api_key and token; identifiers such as ssn, passport_number and tax_id; financial ones such as credit_card, iban and cvv; health, device and key names. A dash counts as an underscore, and case is ignored.

Should user passwords be in a public table at all?

No. Supabase Auth stores password hashes in auth.users, which the Data API does not expose. Keep your own tables to profile data and let Auth handle sign-in.