Пользуйтесь ключами RSA, у каждого пользователя своя пара ключей. Один (публичный) передаётся на сервер, другой (приватный) остаётся у пользователя. Когда делается транзакция, например покупка, то на сервер должна поступать информация:
- идентификатор покупателя
- номер счёта покупателя
- идентификатор продавца
- номер счёта продавца
- идентификатор покупки
- валюта
- точная сумма покупки
- точное время транзации в таймзоне UTC
К каждой транзакции должен прилагаться отпечаток от всех этих значений, подписанный с использованием приватного ключа покупателя. На сервере этот отпечаток должен проверяться с использованием публичного ключа того же пользователя. Публичный ключ можно хранить в базе же. Он только для сверки.
Чтобы выполнить транзакцию (покупку), нужно проверить, хватает ли средств на балансе пользователя-покупателя. Для этого нужно изъять из базы все транзакции пользователя-покупателя, по каждой транзакции провести проверку отпечатка и суммировать те, у которых отпечаток корректный.
Проверяйте время транзакций. У каждого покупателя все транзакции должны быть строго хронологическими. Не должно быть чтобы покупка N+1 была ДО покупки N.
Это вычисляется долго. Чтобы ускорить вычисление остатка, можно ввести специальный тип транзакции "состояние на начало месяца". Его должны выполнять сторонний доверенный сервер, имеющий свою специальную пару ключей. Тогда текущий баланс = последний остаток на начало месяца + все поступления - все оплаты. Вычисляется значительно быстрее.
Если злоумышленник взломает базу данных, то внесёние записей будет чрезывычайно затруднительным, потому что подделать отпечаток транзакции будет невозможно. Слабое звено - можно просто выгрузить базу или таблицу в файл и затем дропнуть таблицу или базу и шантажировать вас потерянными данными. Чтобы исключить такое, запретите пользователям (не покупателям, а тем, которые подключаются к базе) делать DROP TABLE/DATABASE. Ещё делайте резервные копии. Ещё держите зеркало базы данных.
Всё что я описал, будет бесполезно, если злоумышленник заменит исходный код платёжного сервера, так как он сможет просто вырубить все проверки на отпечатки. Поэтому платёжный сервер не должен быть скриптовым. То есть не PHP, node, Python, Ruby. Это должен быть компилируемый код. С цифровой подписью. Сервер не должен исполнять приложения с отсутствующей или неправильной цифровой подписью.
Но это не мешает подменить список доверенных центров сертификации на сервере, чтобы запустить поддельное приложение вместо платёжного сервера. Поэтому на стороне СУБД нужно реализовать механизм, который не даёт подключиться к базе любому приложению. Это приложение должно иметь специальный механизм доступа. Ограничение по IP, специальные заголовки, особенная сессия. Значит это не MySQL и, скорее всего, не PostgreSQL.
Ещё о каком-нибудь головняке рассказать или уже достаточно?
Специалисты из WebMoney, PayPal, Yandex.Деньги и онлайн-банков сейчас не скрывая улыбки смотрят на мой алгоритм. Привет вам всем, друзья!
Относительно реализации на PHP. Два сервера с запросами через AJAX не сделают сервер надёжнее, потому что всё можно подделать в браузере.
Нужно предотвратить доступ пользователя к серверу. В Интернет огромное количество статей на эту тему. Я боюсь, что полный список средств предотвращения доступа я просто не вспомню. В любом случае, способов взлома больше, чем методов защиты в PHP.
Вот списочек способов
проникнуть на сервер:
- Через уязвимости серверных компонентов;
- Через уязвимости в веб-окружении сервера;
- Через удаленное выполнение произвольного кода;
- Через наличие переполнений;
- Через инъекции (внедрение кода) (например, SQL Injection);
- Через обход системы аутентификации веб-ресурса;
- Через XSS / CSRF;
- Через перехват привилегированных аккаунтов (или сессии таких аккаунтов);
- Через Remote File Inclusion / Local File Inclusion;
- Через компоненты CMS, CMF и фреймворков с известными уязвимостями;
- Через перенаправление на другие сайты c помощью открытых редиректов;
- Через сканирование директорий и файлов (поиск дампов, данных про git, svn);
- Через подбор паролей.
И вот списочек способов
снижения вероятности проникновения и нанесения непоправимого ущерба:
Состояние серверов
- Держи сервера в актуальном состоянии, следи за найденными уязвимостями, обновляй серверные приложения
- Делай бакапы файлов и баз данных, держи зеркальную базу данных; в случае беды - используй копию
- Используй виртуальные машины, делай периодически снимки, а в случае взлома восстанавливай машины из снимков
- Больше не вспомню. Держи админа под рукой.
Доступ к серверам
- Не подключайся от имени пользователя root, заведи другого.
- Установи на всех пользователей длинный пароль.
- Запрети всем пользователям делать sudo. Можно разрешить делать некоторые отдельные операции, например, nginx reload.
- Для подключения к серверам пользуйтя SSH или SFTP, авторизация по ключам
- Смени порты сервисов SSH и SFTP
- Установи ограничение на список IP, с которых можно подключиться к серверу. Сделай себе белый IP адрес.
Внутренние сервисы
- Если всё приложение на одном сервисе, то все внутренние службы (mysql, memcached, raddis, rabbit) должны слушать только интерфейс 127.0.0.1. ( habrahabr.ru/post/212265 )
- Если приложение включает несколько серверов (отдельно база, отдельно PHP), то есть представляет собой кластер, то службы должны слушать только те IP, которые относятся к кластеру.
- Смени стандартные порты всех сервисов
- Пользователю UNIX от которого работает PHP-FPM / NGINX/ APACHE, должны быть открыты для записи только несколько директорий (upload, логи, временные файлы), а для исполнения - вообще ничего нельзя.
- Пользователю базы данных не должны быть доступны деструктивные и небезопасные операции (DROP/ALTER/CREATE PROCEDURE), может быть даже для INSERT/UPDATE/DELETE в данных по финансам использовать другой аккант в базе данных
Приложение PHP
- Защищай сайт с помощью SSL
- Используй дополнительный платёжный пароль, ограничивай количество попыток оплаты с неверным паролем
- Используй server side http cookie, короткие сессии с привязкой ключа сесии к IP и UserAgent клиента
- Все запросы к базу должны быть только с использованием связываемых параметров, никаких конкатенаций запроса с параметрами (Внедрение SQL-кода, SQL injection, php.net/manual/ru/security.database.sql-injection.php и другое по фразе SQL injection )
- Не доверяй проверкам данных на стороне клиента. Да, они ускоряют работу, но их можно легко подделать. Поэтому все проверки дублируй в коде на сервере.
- Не доверяй данным пользователя: фильтруй строки, отрезай теги, укорачивай до допустимой длины, приводи к числам все числа (wikipedia: CSS, Cross Site Scripting — «межсайтовы..., XSS для новичков, Яндекс: XSS-атаки и другое по фразе "XSS")
- Все запросы на сервер должны сопровождаться токеном безопасности ( https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D0%B6%D... https://learn.javascript.ru/csrf habrahabr.ru/post/235247 и другое по фразе "CSRF" )
- Нигде не делай авто-редирект на страницу, указанную в параметрах запроса - так обычно делают, чтобы после логина вернуться туда, откуда была вызвана форма логина
- Про содержимое транзакций уже писал, не знаю что из этого тебе пригодится