1. Обзор
В спешке при создании приложений GenAI мы часто забываем о самом важном компоненте: безопасности.
Представьте, что вы создаете чат-бота для отдела кадров. Вы хотите, чтобы он отвечал на такие вопросы, как «Какова моя зарплата?» или «Как работает моя команда?»
- Если Алиса (штатная сотрудница) попросит, ей следует показать только свои данные.
- Если Боб (менеджер) попросит, он должен увидеть данные своей команды.
Проблема
Большинство архитектур RAG (Retrieval Augmented Generation) пытаются обрабатывать это на уровне приложения . Они фильтруют фрагменты после их извлечения или полагаются на LLM (Low Life Model — логическое поведение). Это ненадежный подход. Если логика приложения дает сбой, происходит утечка данных.
Решение
Перенесите безопасность на уровень базы данных . Используя безопасность на уровне строк PostgreSQL (RLS) в AlloyDB , мы гарантируем, что база данных физически откажется возвращать данные, которые пользователь не имеет доступа, — независимо от того, что запрашивает ИИ.
В этом руководстве мы создадим «Личное хранилище» : защищенного HR-помощника, который динамически меняет свои ответы в зависимости от того, кто вошел в систему.

Архитектура
Мы не разрабатываем сложную логику управления правами доступа на Python. Мы используем сам механизм базы данных.
- Интерфейс: Простое приложение Streamlit, имитирующее вход в систему.
- Мозг: AlloyDB AI (совместим с PostgreSQL).
- Механизм: В начале каждой транзакции мы устанавливаем переменную сессии (
app.active_user). Политики базы данных автоматически проверяют таблицуuser_roles(выступающую в качестве нашего поставщика идентификации) для фильтрации строк.
Что вы построите
Безопасное приложение для управления персоналом. Вместо того чтобы полагаться на логику приложения для фильтрации конфиденциальных данных, вы реализуете защиту на уровне строк (RLS) непосредственно в механизме базы данных AlloyDB. Это гарантирует, что даже если ваша модель ИИ «галлюцинирует» или попытается получить доступ к несанкционированным данным, база данных физически откажется их возвращать.
Что вы узнаете
Вы узнаете:
- Как разработать схему для RLS (разделение данных и идентификации).
- Как писать политики PostgreSQL (
CREATE POLICY). - Как обойти исключение для "Владельца таблицы" с помощью
FORCE ROW LEVEL SECURITY. - Как создать приложение на Python, которое выполняет "переключение контекста" для пользователей.
Требования
2. Прежде чем начать
Создать проект
- В консоли Google Cloud на странице выбора проекта выберите или создайте проект Google Cloud.
- Убедитесь, что для вашего облачного проекта включена функция выставления счетов. Узнайте, как проверить, включена ли функция выставления счетов для проекта .
- Вы будете использовать Cloud Shell — среду командной строки, работающую в Google Cloud. Нажмите «Активировать Cloud Shell» в верхней части консоли Google Cloud.

- После подключения к Cloud Shell необходимо проверить, прошли ли вы аутентификацию и установлен ли идентификатор вашего проекта, используя следующую команду:
gcloud auth list
- Выполните следующую команду в Cloud Shell, чтобы убедиться, что команда gcloud знает о вашем проекте.
gcloud config list project
- Если ваш проект не задан, используйте следующую команду для его установки:
gcloud config set project <YOUR_PROJECT_ID>
- Включите необходимые API: перейдите по ссылке и включите API.
В качестве альтернативы можно использовать команду gcloud. Для получения информации о командах gcloud и их использовании обратитесь к документации .
gcloud services enable \
alloydb.googleapis.com \
compute.googleapis.com \
cloudresourcemanager.googleapis.com \
servicenetworking.googleapis.com \
aiplatform.googleapis.com
Подводные камни и устранение неполадок
Синдром «Проекта-призрака» | Вы выполнили команду |
Баррикада Биллинга | Вы активировали проект, но забыли указать платежный аккаунт. AlloyDB — высокопроизводительный движок; он не запустится, если «топливо» (платежный бак) пуст. |
Задержка распространения API | Вы нажали «Включить API», но в командной строке по-прежнему отображается сообщение |
Квота Квагс | Если вы используете совершенно новую пробную учетную запись, вы можете столкнуться с региональной квотой на экземпляры AlloyDB. Если |
3. Настройка базы данных
В этой лабораторной работе мы будем использовать AlloyDB в качестве базы данных для тестовых данных. Она использует кластеры для хранения всех ресурсов, таких как базы данных и журналы. Каждый кластер имеет основной экземпляр , который обеспечивает точку доступа к данным. Таблицы будут содержать сами данные.
Давайте создадим кластер AlloyDB, экземпляр и таблицу, куда будет загружен тестовый набор данных.
- Нажмите на кнопку или скопируйте ссылку ниже в браузер, где вы авторизованы в Google Cloud Console.
- После завершения этого шага репозиторий будет клонирован в ваш локальный редактор CloudShell, и вы сможете запустить приведенную ниже команду, указав папку проекта (важно убедиться, что вы находитесь в каталоге проекта):
sh run.sh
- Теперь воспользуйтесь пользовательским интерфейсом (щелкните ссылку в терминале или щелкните ссылку «предварительный просмотр в веб-браузере» в терминале).
- Введите данные для идентификатора проекта, названия кластера и экземпляра, чтобы начать работу.
- Пока прокручиваются логи, выпейте кофе, а здесь вы сможете почитать о том, как это происходит за кулисами. Это может занять около 10-15 минут.
Подводные камни и устранение неполадок
Проблема «терпения» | Кластеры баз данных — это ресурсоемкая инфраструктура. Если вы обновите страницу или завершите сессию Cloud Shell, потому что она «зависла», вы можете получить «фантомный» экземпляр, который будет частично выделен и его невозможно будет удалить без ручного вмешательства. |
Региональное несоответствие | Если вы включили API в регионе |
Скопления зомби | Если вы ранее использовали это же имя для кластера и не удалили его, скрипт может сообщить, что имя кластера уже существует. Имена кластеров должны быть уникальными в рамках одного проекта. |
Таймаут облачной оболочки | Если ваш перерыв на кофе длится 30 минут, Cloud Shell может перейти в спящий режим и отключить процесс |
4. Предоставление схемы
На этом этапе мы рассмотрим следующее:

Вот подробная пошаговая инструкция:
После запуска кластера и экземпляра AlloyDB перейдите в редактор SQL AlloyDB Studio, чтобы включить расширения AI и настроить схему.

Возможно, вам потребуется дождаться завершения создания экземпляра. После этого войдите в AlloyDB, используя учетные данные, которые вы создали при создании кластера. Для аутентификации в PostgreSQL используйте следующие данные:
- Имя пользователя: "
postgres" - База данных: "
postgres" - Пароль: "
alloydb" (или тот, который вы указали при создании учетной записи)
После успешной аутентификации в AlloyDB Studio команды SQL вводятся в редакторе. Вы можете добавить несколько окон редактора, используя значок плюса справа от последнего окна.

Команды для AlloyDB будут вводиться в окнах редактора, используя при необходимости параметры «Выполнить», «Форматировать» и «Очистить».
Создайте таблицу
Нам нужны две таблицы: одна для конфиденциальных данных (сотрудники), а другая для правил идентификации (роли пользователей). Разделение таблиц крайне важно для предотвращения ошибок «бесконечной рекурсии» в политиках.
В AlloyDB Studio можно создать таблицу, используя приведенный ниже оператор DDL:
-- 1. Create User Roles (The Identity Provider)
CREATE TABLE user_roles (
username TEXT PRIMARY KEY,
role TEXT -- 'employee', 'manager', 'admin'
);
INSERT INTO user_roles (username, role) VALUES
('Alice', 'employee'),
('Bob', 'manager'),
('Charlie', 'employee');
-- 2. Create the Data Table
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
name TEXT,
salary INTEGER,
performance_review TEXT
);
INSERT INTO employees (name, salary, performance_review) VALUES
('Alice', 80000, 'Alice meets expectations but needs to improve punctuality.'),
('Bob', 120000, 'Bob is a strong leader. Team morale is high.'),
('Charlie', 85000, 'Charlie exceeds expectations. Ready for promotion.');
Подводные камни и устранение неполадок
Обнаружена бесконечная рекурсия при определении ролей в таблице сотрудников. | Причина сбоя: Если ваша политика гласит «Проверьте таблицу сотрудников, чтобы узнать, являюсь ли я менеджером», база данных должна выполнить запрос к таблице для проверки политики, что приводит к повторному запуску политики. Результат: Обнаружена бесконечная рекурсия. Решение: Всегда используйте отдельную таблицу поиска (user_roles) или используйте реальных пользователей базы данных для ролей. |
Проверьте данные:
SELECT count(*) FROM employees;
-- Output: 3
5. Обеспечение и контроль безопасности
Теперь включим экраны. Также создадим универсального "пользователя приложения", которого наш код на Python будет использовать для подключения.
Выполните следующий SQL-запрос в редакторе запросов AlloyDB:
-- 1. Activate RLS
ALTER TABLE employees ENABLE ROW LEVEL SECURITY;
-- 2. CRITICAL: Force RLS for Table Owners
ALTER TABLE employees FORCE ROW LEVEL SECURITY;
-- 3. Create the Application User
DO
$do$
BEGIN
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'app_user') THEN
CREATE ROLE app_user LOGIN PASSWORD 'password';
END IF;
END
$do$;
-- 4. Grant Access
GRANT SELECT ON employees TO app_user;
GRANT SELECT ON user_roles TO app_user;
Подводные камни и устранение неполадок
Тестирование от имени пользователя PostgreSQL (суперпользователь) и просмотр всех данных. | Причина сбоя: По умолчанию RLS не применяется к владельцу таблицы или суперпользователям. Они обходят все политики. Устранение неполадок: Если ваши политики кажутся «неработоспособными» (разрешают все), проверьте, вошли ли вы в систему как |
6. Создайте политики доступа.
Мы определим два правила, используя переменную сессии (app.active_user), которую позже установим из кода нашего приложения.
Выполните следующий SQL-запрос в редакторе запросов AlloyDB:
-- Policy 1: Self-View
-- Users can see rows where their name matches the session variable
CREATE POLICY "view_own_data" ON employees
FOR SELECT
USING (name = current_setting('app.active_user', true));
-- Policy 2: Manager-View
-- Managers can see ALL rows.
CREATE POLICY "manager_view_all" ON employees
FOR SELECT
USING (
EXISTS (
SELECT 1 FROM user_roles
WHERE username = current_setting('app.active_user', true)
AND role = 'manager'
)
);
Подводные камни и устранение неполадок
Используется current_user вместо app.active_user. | Проблема: Current_user — зарезервированное ключевое слово SQL, возвращающее роль базы данных (например, app_user). Нам нужен пользователь приложения (например, Alice). Решение: Всегда используйте пользовательское пространство имен, например, app.variable_name . |
Забыт параметр | Проблема: Если переменная не задана, запрос завершается с ошибкой. Решение: Функция current_setting('...', true) возвращает NULL вместо сбоя, что безопасно приводит к возврату 0 строк. |
7. Создайте приложение «Хамелеон».
Для моделирования логики приложения мы будем использовать Python и Streamlit.

Откройте терминал Cloud Shell в режиме редактора, перейдите в корневую папку или в каталог, где вы хотите создать это приложение. Создайте новую папку.
1. Установите зависимости:
Выполните следующую команду в терминале Cloud Shell из каталога вашего нового проекта :
pip install streamlit psycopg2-binary
2. Создайте файл app.py:
Создайте новый файл с именем app.py и скопируйте в него содержимое из файла репозитория .
import streamlit as st
import psycopg2
# CONFIGURATION (Replace with your IP)
DB_HOST = "10.x.x.x"
DB_NAME = "postgres"
DB_USER = "postgres"
DB_PASS = "alloydb"
def get_db_connection():
return psycopg2.connect(
host=DB_HOST, database=DB_NAME, user=DB_USER, password=DB_PASS
)
def query_database(user_name):
conn = get_db_connection()
try:
with conn.cursor() as cur:
# THE SECURITY HANDSHAKE
# We tell the database: "For this transaction, I am acting as..."
cur.execute(f"SET app.active_user = '{user_name}';")
# THE BLIND QUERY
# We ask for EVERYTHING. The database silently filters it.
cur.execute("SELECT name, role, salary, performance_review FROM employees;")
return cur.fetchall()
finally:
conn.close()
# UI
st.title("🛡️ The Private Vault")
user = st.sidebar.radio("Act as User:", ["Alice", "Bob", "Charlie", "Eve"])
if st.button("Access Data"):
results = query_database(user)
if not results:
st.error("🚫 Access Denied.")
else:
st.success(f"Viewing data as {user}")
for row in results:
st.write(row)
3. Запустите приложение:
Выполните следующую команду в терминале Cloud Shell из каталога вашего нового проекта :
streamlit run app.py --server.port 8080 --server.enableCORS false
Подводные камни и устранение неполадок
Объединение соединений. | Риск: При использовании пула соединений переменная сессии SET app.active_user может сохраняться в соединении и «утекать» к следующему пользователю, который получит это соединение. Решение: В производственной среде всегда используйте RESET app.active_user или DISCARD ALL при возврате соединения в пул. |
Пустой экран в Cloud Shell. | Исправление: Используйте кнопку "Предварительный просмотр веб-страниц" на порту 8080. Не нажимайте на ссылку localhost в терминале. |
8. Проверка концепции «нулевого доверия»
Попробуйте приложение, чтобы убедиться в реализации принципа «нулевого доверия»:
Выберите "Алиса" : она должна увидеть 1 строку (себя).

Выберите "Боба" : он должен увидеть 3 строки (для всех).

Почему это важно для агентов искусственного интеллекта
Представьте, что вы подключаете свою модель к этой базе данных. Если пользователь попросит вашу модель: «Подвести итоги всех оценок эффективности», она сгенерирует запрос SELECT performance_review FROM employees.
- Без RLS: Ваша модель получает личные отзывы всех пользователей и передает их Алисе.
- При использовании RLS: ваша модель выполняет тот же самый запрос, но база данных возвращает только отзыв Алисы.
Это ИИ с нулевым доверием. Вы не доверяете модели фильтрацию данных; вы заставляете базу данных скрывать их.
Переносим это в производство.
Представленная здесь архитектура является готовой к использованию в производственной среде, но конкретная реализация упрощена для удобства обучения. Для безопасного развертывания в реальной корпоративной среде следует внедрить следующие улучшения:
- Реальная аутентификация: замените выпадающее меню «Переключатель идентификации» надежным поставщиком идентификации (IDP), таким как Google Identity Platform, Okta или Auth0. Ваше приложение должно проверять токен пользователя и безопасно извлекать его идентификационные данные, прежде чем устанавливать переменную сессии в базе данных, гарантируя, что пользователи не смогут подделать свою личность.
- Безопасность пула соединений: При использовании пулов соединений переменные сессии могут сохраняться между запросами разных пользователей, если они обрабатываются неправильно. Убедитесь, что ваше приложение сбрасывает переменную сессии (например, RESET app.active_user) или очищает состояние соединения при возврате соединения в пул, чтобы предотвратить утечку данных между пользователями.
- Управление секретами: Жесткое кодирование учетных данных базы данных представляет собой риск для безопасности. Используйте специализированный сервис управления секретами, например Google Secret Manager, для безопасного хранения и извлечения паролей к базе данных и строк подключения во время выполнения.
9. Уборка
После завершения этой лабораторной работы не забудьте удалить кластер и экземпляр AlloyDB.
Это должно привести к очистке кластера вместе с его экземплярами.
10. Поздравляем!
Поздравляем! Вы успешно перенесли безопасность на уровень данных. Даже если бы в вашем коде на Python была ошибка, из-за которой он пытался бы print(all_salaries) , база данных ничего бы не вернула Алисе.
Следующие шаги
- Попробуйте это со своим собственным набором данных.
- Изучите документацию AlloyDB AI .
- Посетите веб-сайт Code Vipassana , чтобы узнать о других мастер-классах.