от Веселин Марков(31-08-2006)

рейтинг (21)   [ добре ]  [ зле ]

Printer Friendly Вариант за отпечатване

Записване, съхранение и прочит на поверителна информация от БД


Веселин Марков 08.2006


Тази статия има за цел да представи сравнително сигурен подход при боравене с конфиденциална информация, в т.ч. персонални данни, банкова информация, осигурителни номера и т.н.


.:Въведение


Срещал съм какви ли не безотговорни изпълнения, на които смело се разчита и биват определяни като изключително надеждни. Няма да се спирам на тях, но всеки си дава сметка, че голяма част от тази информация е потребна и предмет на злоупотреби от трети лица.

Написаното не претендира за цялост по въпроса, несъмнено има други сполучливи варианти, които могат да бъдат реализирани и тук е мястото да бъдат разисквани.

Използваният софтуер включва актуални версии на Apache, PHP, PostgreSQL, OpenSSL и Linux.

Идеята е съвсем простичка - цялото движение на данните от браузъра на клиента до служителят, който ще чете и обработва информацията, да се случва по защитен начин.

Как става това може да бъде онагледено чрез следната схема:

клиентска заявка за изпращане на данни към https сървър -> криптиране на специалната информация -> запис в база данни <- четене и обработка от служител

Условно https сървърът се намира в зона DMZ, която няма достъп до LAN.

Добре е да се спомене, че БД която ползвам в случая има поддръжка на SSL. Скриптът, който се закача към нея го прави посредством SSL връзка. PostgreSQL е конфигуриран така, че да не приема други методи на свързване. По този начин трафикът между уеб сървъра и БД е безполезен за неоторизирани лица, дори да бъде прихванат от пакетен снифър, примерно. Въпреки това, един път получило административен достъп до съответната машина, такова лице би могло да се възползва от новопостъпващите данни, но съществуващата информация ще остане непрочитаема за него.

Малко по-късно ще се спра на това как се извършва самото криптиране и декриптиране на данните, което е основна част от решението на проблема.

.:Конфигуриране на софтуера

При Apache нещата стоят така

# ./configure --enable-ssl [...]

При стартиране из лог файловете ни интересува този ред, който потвърждава, че сървърът е компилиран със съответната поддръжка.

[Sun Aug 13 04:40:51 2006] [info] Server: Apache/2.2.3, Interface: mod_ssl/2.2.3, Library: OpenSSL/0.9.8b

Ако все още нямате сертификат и планирате да си купите такъв от Certificate Authority организация, по този начин ще си създадете ключ и Certificate Signing Request, който след това ще им изпратите за подпис. Добре е ключът да е защитен с парола, в противен случай не ползвайте '-des3'.

# openssl genrsa -des3 -out secure.example.net.key 1024
# openssl req -new -key secure.example.net.key -out secure.example.net.csr

След като сте се сдобили вече със сертификат за сървъра, остава да си довършите конфигурацията.

[extra/httpd-ssl.conf]

<VirtualHost W.X.Y.Z:443>

DocumentRoot "/opt/apache2/htdocs/secure.example.net"
ServerName secure.example.net:443
ServerAdmin adm@example.net
ErrorLog /opt/apache2/logs/secure.example.net-error_ssl_log
TransferLog /opt/apache2/logs/secure.example.net-access_ssl_log
SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL

SSLCertificateFile /opt/apache2/conf/secure.example.net.crt
SSLCertificateKeyFile /opt/apache2/conf/secure.example.net.key


<FilesMatch "\.(cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>
<Directory "/opt/apache2/cgi-bin">
SSLOptions +StdEnvVars
</Directory>

BrowserMatch ".*MSIE.*" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0

CustomLog /opt/apache2/logs/ssl_request_log %t %h %{SSL_PROTOCOL}x \
%{SSL_CIPHER}x \"%r\" %b \"%{Referer}i\" \"%{User-Agent}i\""


</VirtualHost>


За PHP -

# ./configure --with-openssl[=DIR] [...]

Проверявате дали всичко изглежда наред

# php -i | grep -i ssl

От интерес са следните редове

Registered Stream Socket Transports => tcp, udp, unix, udg, ssl, sslv3, sslv2, tls
SSL Support => enabled
OpenSSL Version => OpenSSL 0.9.8b 04 May 2006

Малко по-надолу е видно какво ще правим с php.


При PostgreSQL също разрешавате поддръжка на SSL


# ./configure --with-openssl [...]

Тук само е желателно да ползвате подписан от оторизиран CA сертификат, може да бъде и self-signed. По този начин създавате и ползвате вторият:

# openssl req -new -text -out server.req
# openssl rsa -in privkey.pem -out server.key

Можете да махнете паролата на ключа ако искате да не си играете с expect скриптиране -

# openssl rsa -in privkey.pem -out server.key ; rm privkey.pem

Така ще си подпишете сами сертификата

# openssl req -x509 -in server.req -text -key server.key -out server.crt
# chmod og-rwx server.key

PostgreSQL ще търси създадените файлове в data директорията. Остава да се укаже, че сървърът ще ползва ssl комуникация в postgresql.conf - ssl = on

В pg_hba.conf може да изисквате единствено такъв начин за свързване на клиентско приложение с базата:

hostssl all all 0.0.0.0/0 md5

.:Симетрично/Асиметрично Криптиране


След като трафикът е подсигурен, сега е време да се помисли как ще се криптират самите данни. При симетрично криптиране обикновено се ползва един и същ ключ за криптиране и декриптиране на данните. След като в нашия случай трябва да имаме ключ, който ще е върху сървъра, този вариант се елиминира по простата причина, че всеки с достъп до него би могъл евентуално да прочете какво има в БД.

Удачно е да се използва RSA асиметрично криптиране, при което ще имаме публичен и частен ключ. Асиметричното криптиране не се свежда само до Public Key криптиране, тъй като може да се ползва система от 2 частни ключа.

Можем да създадем и ползваме 8192 битови ключове, но декриптирането на данните ще отнема значително повече процесорно време.
Частният ключ трябва да защитим с парола:

# openssl genrsa -des3 -out private.pem 2048

В случая PEM (privacy enhanced mail) файлът садържа и публичния ключ, който ще извлечем така:

# openssl rsa -in private.pem -out public.pem -outform PEM -pubout

Той изглежда така

 
 -----BEGIN PUBLIC KEY-----
 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuFSjgsIjIlyEo9qbjn+8
 86TyFI+78vVM5PJjKyFqpmWeJMPrmDK86ng1uPwIjEbSJKxhrYAhhR8+za8Q8DVU
 9ksTCdiH6jVwDm0VHoQuVaXuHTI7U5oCZFFrqkkN7x8GiulF8gQtbv8k5Z/P0JCQ
 43eEujiGdaB/xI4zoZe7RSyHur3ILGV32q7cS+/kiMe/SgCPGYDuYmr7cO1q/8QW
 kHHKHaJm29BUY2rXFGXXDeKEdXnNRYlQNJGKf5m8tg9lHm1IQq2g9S+aWedXN5Qz
 o6lZP4B34H0nA1eULWEO+gocXcUgBhlY2HO/T8nYn6VmNDmJnvv2j8gSpjMajcVH
 1QIDAQAX
 -----END PUBLIC KEY-----
 

Проверка дали всичко е направено правилно

# echo test > cleartext.txt
# openssl rsautl -encrypt -inkey public.pem -pubin -in cleartext.txt -out enctext.dat
# openssl rsautl -decrypt -inkey private.pem -in enctext.dat -out cleartext1.txt
# cat cleartext1.txt
test


private.pem преместваме на система, до която няма достъп от зоната в която се намира нашият уеб сървър. Остава да се напише приложение, което теоритично се свежда до логване на оператор на въпросната машина (#2) по https, стартиране на приложение, закачащо се по ssl към БД на уеб сървъра и четене от нея. С частният ключ и въведената към него парола информацията се декриптира.


.:PHP, Примери

След като сме компилирали PHP с поддръжка на OpenSSL вече имаме достъп до такива функции и по-нататък нещата са повече от тривиални. Референции за работа с тях - http://php.net/openssl.

...

// криптиране при сървъра

$text = somefilter($_POST['data']);

$fp = fopen('public.pem','r');
$pub_key = fread($fp,1024);
fclose($fp);

$res = openssl_get_publickey($pub_key);
openssl_public_encrypt($text,$crypttext,$pub_key);
openssl_free_key($res);

$data = base64_encode($crypttext);

// в по-'четлив' вид

echo 'string crypted: ' . $data . "\n";

...

...

// декриптиране на друг хост

$priv_key_pass = somefilter($_POST['priv_key_pass']);

pg_connect(“host=W.X.Y.Z port=5432 dbname= user= password= sslmode=require”);

// вързка към БД и четене от нея, методът на закачане се налага и от самата БД
...

$enctext = base64_decode($data);

$fp = fopen('private.pem','r');
$priv_key = fread($fp,1024);
fclose($fp);

$res = openssl_get_privatekey($priv_key,$priv_key_pass);
openssl_private_decrypt($enctext,$text,$res);
openssl_pkey_free($res);

echo 'string decrypt : ' . $text . "\n";

...

Алтернативно е използването на PGP (GnuPG) с PHP, но за целта gpg трябва да се вика като външна програма, което в някои случаи не е особено удобно.


.:Връзки

[1] http://www.php.net/openssl
[2] http://www.postgresql.org/docs/
[3] http://www.openssl.org/docs/





<< Как да инсталираме, използваме и тестваме с quemu seamlessrd | Смяна на ИП блок >>