Posts with tag Databáze

Šifrované připojení k MySQL serveru s vlastnoručně podepsaným certifikátem

Feb|24 2012

On-line manuál MySQL nám celkem dobře vysvětlí, jaké argumenty použít pro šifrované připojení klienta k serveru. Pojďme se ale podívat na veškeré kroky, které musím jako "správce" serveru provést, tedy včetně generování klíčů a certifikátu, včetně elektronického podpisu.

V první řadě bych chtěl zdůraznit, že následující řádky by neměl brát do úvahy administrátor produkčního serveru. Ten by měl používat výhradně certifikáty podepsané důvěryhodnou certifikační autoritou. Následující způsob lze využít pro testování nebo v případě vzdálené komunikace se serverem, která z nějakého důvodu musí být šifrována.

Generování veřejného a soukromého klíče pro server

V prvním kroku vygenerujeme soukromý klíč pro server:

openssl genrsa -out server.key 1024

V případě potřeby veřejný klíč vygenerujeme ze soukromého následujícím příkazem, nicméně pro účely zabezpečení spojení ho nebudeme potřebovat.

rsa -in server.key -pubout > server.pub

Jak vytvořit a na co slouží Certificate Signing Request

Jedná se o zprávu, která obsahuje informace o majiteli budoucího certifikátu. Ten se odesílá certifikační autoritě, my ho ale použijeme pro vytvoření vlastnoručně podepsaného certifikátu.

openssl req -new -key server.key -out server.csr

Generování certifikátu

Certifikát by měl mít rozumnou délku platnosti, v produkci běžně rok, pro testování můžete zvolit jinou rozumnou dobu. Pro vygenerování samotné použijeme soubor vygenerovaný v minulém kroku a openssl utilita se nás zeptá na několik informací o majiteli certifikátu:

openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

Certifikát můžeme vytvořit i přímo z privátního klíče, bez generování souboru server.csr:

openssl req -new -x509 -key example.com.key -out example.com.cert -days 3650 -subj /CN=example.com

Nyní potřebujeme umístit certifikát do adresáře a to tak, aby byl čitelný uživatelem, pod kterým běží náš MySQL server, typicky uživatel mysql. Pozor si musíme dát i na SELinux kontext (pokud je aktivní), který může zabránit přečtení souborů démonem. Následující sekvenci příkazů můžete brát jako inspiraci:

# mkdir /usr/share/mysql/testcerts
# cp server.{crt,key} /usr/share/mysql/testcerts
# chown -R mysql:mysql /usr/share/mysql/testcerts
# restorecon -r /usr/share/mysql/testcerts

Vygenerovaný certifikát spolu se soukromým klíčem použijeme v konfiguračním souboru démona MySQL:

[mysqld]
ssl-ca=/usr/share/mysql/testcerts/server.crt
ssl-cert=/usr/share/mysql/testcerts/server.crt
ssl-key=/usr/share/mysql/testcerts/server.key

Rychlá a snadná cesta, jak vytvořit certifikát pro testování:

Jednoduchá stránka, jednoduché ovládání. Pouze uvedete doménu (můžete bez problému použít example.com) a aplikace vám vygeneruje dvojici soukromého klíče s vlastnoručně podepsaným certifikátem.

http://www.selfsignedcertificate.com

Zdroje:

http://dev.mysql.com/doc/refman/5.0/en/secure-using-ssl.html
http://www.akadia.com/services/ssh_test_certificate.html
http://www.chriscalender.com/?p=448

Tags: Internet | Programování | Databáze | Bezpečnost | Linux | Fedora



Hacker Igigi opět a znova

Jan| 3 2010

Při dnešní návštěvě portálu zive.cz jsem narazil na pozdrav od Igigiho, hackera, který se dlouhodobě nabourává zejména do databází webových serverů, a na kontě má již pěknou řádku známých portálů. Přesto, že to zprvu vypadá tak, že napadl i zive.cz, není tomu tak a jedná se jen o upoutávku na rozhovor, který s ním proběhl.

Reakce na jeho konání se různí - jedni ho chválí za odhalování chyb, což má za následek zvýšení zájmu správců serverů o bezpečnost, která je často žalostně slabá, druzí ho kritizují, že svým nelegálním chováním jen získává pozornost veřejnosti.

Igigi na Zive.czOsobně z něho mám smíšené pocity. Někteří správci si opravdu dají říct, až když je jejich web napaden, nicméně kdybych dostal do ruky hajzla, který nám ukradl rádio a poškodil zámek u auta, asi bych mu nebyl příliš vděčný za to, že mě naučil vyndávat přední panel rádia.

Takže osvěta je určitě dobrá a donutit provozovatele webových serverů, aby více dbali na bezpečnost a dodržovali alespoň základní pravidla bezpečnosti, to by bylo super. Ovšem ne tímto způsobem. Jakým ale, to je otázka.

Některé další názory si můžete přečíst naříklad tu:

Tags: Internet | Sítě | Počítače | Databáze | Bezpečnost



Hledání optimálního dotazu pro filtraci a řazení produktů s atributy

Nov|27 2009

Následující příspěvek se zabývá rychlostí databáze MySQL a hledáním optimálního SQL dotazu pro zobrazení doplňkových atributů u produktů.

Máme dvě tabulky, jednu s produkty, který obsahuje základní atributy a druhou s dodatečnými atributy, jejichž typ je definován sloupcem type. Tabulka atributy obsahuje cizí klíč produkt do tabulky produkty a položky s dvojicí klíčů (produkt, type) nemusí být unikátní. Znamená to, že můžeme mít více atributů stejného typu u jednoho produktu. Tabulky jsou v MySQL definovány následovně:


CREATE TABLE IF NOT EXISTS `produkty` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(200) COLLATE utf8_czech_ci NOT NULL,
  `price` decimal(8,2) NOT NULL,
  `active` int(11) NOT NULL,
  `text` text COLLATE utf8_czech_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS `atributy` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product` int(11) NOT NULL,
  `type` int(11) NOT NULL,
  `value_number` int(11) DEFAULT NULL,
  `value_text` text COLLATE utf8_czech_ci,
  `value_datetime` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `product` (`product`)
) ENGINE=InnoDB;

Tabulka pro produkty obsahuje 10 000 záznamů a pro atributy 100 000. Problémem, kterým jsem se zabýval, je vypsání produktů spolu s jejich atributy, přičemž produkty mají být omezeny podle hodnot atributů a seřazeny podle jednoho z atributů.

Konkrétně chceme například vypsat prvních 10 produktů, řazených podle datumu, starších jak červen, aktivní, cena větší než 100 a s textem obsahující řetězec 'sd'.

Dotaz pomocí spojování tabulek - JOIN


SELECT 
	main . * , 
	attr1.value_number AS p_price, 
	attr2.value_text AS p_text, 
	attr3.value_datetime AS p_datetime
FROM `produkty` AS main
LEFT JOIN atributy AS attr1 ON main.id = attr1.produkt
	AND attr1.type =2
LEFT JOIN atributy AS attr2 ON main.id = attr2.produkt
	AND attr2.type =3
LEFT JOIN atributy AS attr3 ON main.id = attr3.produkt
	AND attr3.type =4
WHERE 
	attr3.value_datetime < '2009-06-01 00:00:00'
	AND main.active =1
	AND attr1.value_number >100
	AND attr2.value_text LIKE '%sd%'
GROUP BY main.id
ORDER BY attr3.value_datetime
LIMIT 10

Dotaz trval 4s a mě zajímalo, která část dotazu je nejpomalejší. Zkusil jsem odebraz příkaz ORDER, dotaz trval pouze 2,5s, což je samozřejmě stále špatné. Další změnou bylo úplné vyhození omezujících podmíne, tedy celý příkaz WHERE.

Pokud jsem použil řazení, trval opět 4s, ovšem bez použití podmínek i řazení se rychlost změnila o 2 řády, na použitelných 0,04s. Důvod je zřejmý, při použití řazení musí databáze vypočítat všechny řádky a potom teprve seřadit, zatímco bez použití ORDER příkazu stačí připravit několik řádků, aby byl splněn LIMIT, a tím to hasne.

Bohužel se nemůžeme smířit s tím, že na stránkách budeme zobrazovat produkty náhodně seřazené a nenabídneme uživateli jejich filtrování. Pojďme tedy zkusit jiný typ dotazu, který nám vrátí stejná data.

Dotaz pomocí vloženého dotazu


SELECT 
	main.* 
FROM produkty AS main WHERE
(
	SELECT count(*) 
	FROM atributy AS attr1 
	WHERE attr1.product = main.id 
	AND attr1.type = 2 
	AND attr1.value_number > 700
) AND (
	SELECT count(*) 
	FROM atributy AS attr2 
	WHERE attr2.product = main.id 
	AND attr2.type = 3 
	AND attr2.value_text like '%sd%'
) AND (
	SELECT count(*) 
	FROM atributy AS attr3 
	WHERE attr3.product = main.id 
	AND attr3.type = 4 
	AND attr3.value_datetime > '2009-08-07 23:11:22'
)

Výsledek? Rychlost načítání je srovnatelná s předešlým, ale nelze použít řazení. Atributy se musejí načítat dodatečně. Další možnost, jak načítat podobná data je tu:

Dotaz pomocí obyčejného spojení dvou tabulek


SELECT main . *
FROM produkty AS main, atributy AS a
WHERE main.id = a.product 
AND (
	(a.type =2 AND a.value_number >500)
	OR 
	(a.type =3 AND a.value_text LIKE '%sd%')
	OR 
	(a.type =4 AND a.value_datetime > '2009-06-01 00:00:00')
)
GROUP BY main.id

Rychlost opět srovnatelná, při použití ORDER BY příkazu pomalejší o dva až tři řády (v rozsahu uvedeném na začátku článku řádově minuty).

Závěr:

Ať se snažím, jak se snažím, vymyslet dotaz, který by dokázal řadit i filtrovat produkty s atributy takto rozsáhlých tabulek, se mi nedaří. Na druhou stranu, jak rád říkám, "život je kompromis". Jde tedy o to zvolit ten správný kompromis, na výběr máme následující:

  • využít JOIN tabulek a omezit filtrování produktů na jeden, maximálně dva atributy, s rostoucím počtem filtrovaných atributů je dotaz složitější
  • nepoužívat řazení podle atributů, ale pouze podle sloupců tabulky produkty
  • atributy, podle kterých se nefiltruje, nenačítat pomocí spojování tabulek (nebo obecně při samotném výběru produktů), ale načíst je dodatečně

K poslední možnosti ještě poznámka: Často omezujeme výběr pomocí příkazu LIMIT a tím pádem je daleko výhodnější načíst data u všech takto vybraných produktů najednou (maximálně je jich zpravidla desítky) a přiřadit je k produktům až následně v aplikaci. Načtení atributů samotných je celkem rychlé například takto:


SELECT * FROM atributy WHERE produkt IN (...)

Pokud máte někdo nápady, jak sestavit zmíněné dotazy efektivněji, určitě se o ně podělte v diskuzi.

Tags: Programování | Počítače | Databáze



Testování rychlosti dotazů v MySQL 5.1

Nov|26 2009

V příspěvku jsou popsány základní možnosti, jak zjišťovat rychlost a efektivitu různých SQL dotazů, za účelem výběru nejoptimálnějšího řešení.

Nejdříve něco málo o testování rychlosti dotazů v MySQL. Rychlost dotazů testujeme nejčastěji u sestavování složitějších dotazů, často doprovázené spojováním více tabulek. Pomocí PhpMyAdminu nebo konzole můžeme vytvářet libovolné dotazy a rychlost jejich zpracování je po provedení uvedena.

Musím hned zpočátku upozornit, abyste si předem velmi rozmysleli, jaký dotaz provádíte, pokud to není na vašem stroji, můžete nešikovným dotazem SQL server pěkně potrápit. V případě pochybností nebo i preventivně doporučuji použít na dotaz omezení pomocí LIMIT. To většinou zabrzdí vykonávání dotazu včas.

Při testování rychlosti dotazů často narážíme na první problém - využívání cache. Pokud podobný dotaz provádíme vícekrát nebo jsou data v paměti SQL serveru, výsledky jsou k dispozici rychleji a o složitosti dotazu se mnoho nedozvíme. Zakázat využití cache paměti lze doplněním klíčového slova SQL_NO_CACHE ihned za první SELECT. Tím bychom měli přemluvit server, aby nepoužíval cache.


SELECT SQL_NO_CACHE *
FROM `produkty`
WHERE price > 1000
ORDER BY `name` ASC
limit 10;

Pro pochopení, jak zpracovává složitější dotaz SQL server, lze využívat příkaz EXPLAIN, který se jednoduše přidá před zkoumaný dotaz.


EXPLAIN SELECT *
FROM `produkty`
WHERE price > 1000
ORDER BY `name` ASC
limit 10;

Pokud ale hledáme ideální řešení a vybíráme mezi více dotazy, které se liší strukturou, ale vracejí stejná data, příkaz EXPLAIN nám toho moc neřekne, místo něho je lepší použít proměnou last_query_cost. Optimalizátor tuto hodnotu nastaví vždy při vykonání nějakého dotazu v daném sezení a obsahuje nějaké číslo. Čím větší číslo, tím složitější dotaz.


SELECT *
FROM `produkty`
WHERE price > 1000
ORDER BY `name` ASC
limit 10;
SHOW SESSION STATUS WHERE Variable_name LIKE 'last_query_cost';

Samozřejmě mi nedá abych nepřipomenul, že velkou práci databázovému serveru ušetříte správným používáním indexů. Nakonec přikládám malý prográmek v Pythonu 3, který generuje náhodné hodnoty do tabulek produkty a atributy. Definice tabulek je následující:


CREATE TABLE IF NOT EXISTS `atributy` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`produkt` int(11) NOT NULL,
`type` int(11) NOT NULL,
`value_number` int(11) DEFAULT NULL,
`value_text` text COLLATE utf8_czech_ci,
`value_datetime` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `produkt` (`produkt`,`type`)
) ENGINE=InnoDB;


CREATE TABLE IF NOT EXISTS `produkty` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(200) COLLATE utf8_czech_ci NOT NULL,
`price` decimal(8,2) NOT NULL,
`active` int(11) NOT NULL,
`text` text COLLATE utf8_czech_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;

A slíbený program je zde:


import random

alphabet = 'abcdefghijklmnopqrstuvwxz '

def getString(mn, mx):
"""
Generate random string with the length between mn and mx.
"""
s = ''
for i in range(random.randint(mn, mx)):
s += alphabet[random.randint(0, len(alphabet) -1)]
return s

# generates data for table produkty
for i in range(1000):
print('INSERT INTO `produkty` (`id`, `name`, `price`, `active`, `text`) '
+ 'VALUES (NULL, \'%s\', \'%s\', %s, \'%s\');'
% (getString(3, 20), str(random.randint(10, 1000)),
str(random.randint(0, 1)), getString(50, 500)))

# generates data for table atributy
for i in range(1000):
for j in range(5):
print('INSERT INTO `atributy` '
+ '(`id`, `produkt`, `type`, `value_number`, `value_text`, `value_datetime`) '
+ 'VALUES (NULL, \'%s\', \'%s\', %s, \'NULL\', \'NULL\');'
% (str(i), '2', str(random.randint(10, 1000))))
print('INSERT INTO `atributy` '
+ '(`id`, `produkt`, `type`, `value_number`, `value_text`, `value_datetime`) '
+ 'VALUES (NULL, \'%s\', \'%s\', \'NULL\', \'%s\', \'NULL\');'
% (str(i), '3', getString(10, 1000)))
print('INSERT INTO `atributy` '
+ '(`id`, `produkt`, `type`, `value_number`, `value_text`, `value_datetime`) '
+ 'VALUES (NULL, \'%s\', \'%s\', \'NULL\', \'NULL\', \'%s\');'
% (str(i), '4', ('2009-' + str(random.randint(1, 12)) + '-'
+ str(random.randint(1, 28)) + ' 19:08:23')))
Tags: Internet | Programování | Python | Počítače | Databáze