Ochrana, obrana proti SQL injection v PHP

Za řešení odpovídající úrovni dnešní doby se považuje oddělené předávání dotazu a jeho parametrů databázovému serveru. Nejmodernější obrana proti SQL injection (injekci) jsou tedy parametrizované dotazy v PHP. Jde to v PHP s (již) objektovým rozhraním MySQLi i PDO. Někdy se pro to používá termín „svazování proměnných“ nebo SQL injekce, injektáž. Zde jsou příklady ochrany proti SQL injection (injekci) v rozhraní MySQLi.

phpmyadmin

O napadení webu a zjištění hesel útočníky už toho bylo napsáno hodně. Dříve se doporučovalo escapovat nebo používat mysqli->real_escape_string() a na čísla intval(n) nebo floatval(n.nn). Navíc hesla do databáze ukládat jako hashe SHA512 a mít sloupec sůl. Před uložením hesla do databáze toto heslo ještě „osolit“ jedinečnou solí pro každý záznam.
To je sice zatím bezpečné, ale má to háček. Tím je lidský faktor a zapomenutí escapovat proměnné nebo ošetřit čísla vstupující do SQL dotazu z formuláře. Pak se stane webová stránka zranitelná na SQL injekci (SQL injection).

Proč používat parametrizovaný dotaz? Nejdůležitějším důvodem pro použití parametrizovaných dotazů je vyhnout se útokům s vložením SQL injekce (injection). Je to ve své podstatě změna SQL dotazu odeslanými daty (většinou z formuláře). Lidský faktor je chybující prvek, zapomenutí escapovat nějakou proměnnou může mít fatální následky. Lidé prostě dělají chyby, stroje ne. Za druhé, parametrizovaný dotaz se postará o scénář, kde by sql dotaz mohl selhat, např. vložení O'Baily do SQL dotazu.

Proto se nyní (rok 2022) doporučuje používat v PHP parametrizované SQL dotazy a ukládat hesla do databáze pomocí PHP funkce password_hash(). Ověřovat heslo pomocí sesterské funkce password_verify(). Více informací je v článku „bezpečné ukládání hesel do MariaDB“. Jako PHP rozhraní pro práci s MariaDB lze používat objektové PDO nebo již taky objektové MYSQLI.

Nejdříve si vytvoříme účet testik, databázi testik a tabulku uživatele v utf8 pomocí phpmysqladmin
(https://localhost/phpmyadmin/).

MariaDB klient, monitor, konzola - psaní SQL dotazů v příkazovém řádku

virhundo@hirunduv:~$ mariadb -u testik -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 151
Server version: 10.3.34-MariaDB-0ubuntu0.20.04.1 Ubuntu 20.04

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> \C utf8
Charset changed
MariaDB [(none)]> use testik;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [testik]> show tables;
+------------------+
| Tables_in_testik |
+------------------+
| uzivatele        |
+------------------+
1 row in set (0.001 sec)

MariaDB [testik]> describe uzivatele;
+-------+------------------+------+-----+---------+----------------+
| Field | Type             | Null | Key | Default | Extra          |
+-------+------------------+------+-----+---------+----------------+
| uid   | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| ucet  | varchar(15)      | NO   | UNI | NULL    |                |
| heslo | varchar(128)     | NO   |     | NULL    |                |
| prijm | varchar(25)      | NO   |     | NULL    |                |
| jmeno | varchar(25)      | NO   |     | NULL    |                |
+-------+------------------+------+-----+---------+----------------+
5 rows in set (0.020 sec)

MariaDB [testik]> select * from uzivatele;
+-----+---------------+--------------------------------------------------------------+---------------+-----------+
| uid | ucet          | heslo                                                        | prijm         | jmeno     |
+-----+---------------+--------------------------------------------------------------+---------------+-----------+
|   1 | admin         | $2y$10$Zus3PC71xQit/2Bbu/esRO9nUGHHiUCyUASnPkO6mX3Ml0IngGtEm | Světlá        | Karolína  |
|   2 | šéfredaktor   | $2y$10$/mL7eGXPDTVHc9l5x/iVZeIowmOaDJg4th8UkcG21.DhxxZrdudyW | Pan           | Tau       |
|   3 | redaktor      | $2y$10$jiIi1oAy6Q4BQVuCZy2k/OWIEa66/xkqh450T.4kE78DQ7smViN4y | Novák         | Petr      |
|   4 | přispěvatel   | $2y$10$QmCpFjBgoV93mu9IeT0UKOoGt6NMMt5OZVJh4sMunmwwsRihNJvJu | Soros         | Georg     |
|   5 | poloosa       | $2y$10$Yi4tJ8KkFX6QOTKJR2HOVehSxbrNq6PVreiIf7Htwd.8SLPDCTynC | Kudeříková    | Marie     |
|   9 | traktor       | $2y$10$Jux8JT5NWoi7O5MNxb8dNe9PXo.qr7.FIgjUPjyr4jz9rmD94mxUC | Náhlovský     | Josef     |
|  11 | vizidlo       | $2y$10$D6vJtFZWv3MtrQhiVESGR.WVSrtrN6EXMl49jk/oMadZ1mVO0RulG | Pokorný       | Jan       |
+-----+---------------+--------------------------------------------------------------+---------------+-----------+
7 rows in set (0.022 sec)

Nyní se přihlásíme do konzolového klienta mariadb, viz výše. Vše by mělo fungovat tak, jak je to popsané. Máme připravenu databázi a tabulku „uzivatele“ k ukázce SQL injekce (SQL injection).
V následující ukázce je již přihlašovací jméno, účet přednastavené. Je to ukázkový příklad neošetřeného PHP kódu proti SQL injektáži.

<?php
echo "<!DOCTYPE html>\n";
echo "<html lang=\"cs\">\n";
echo "<head>\n";
echo "<meta charset=\"utf-8\" />\n";
echo "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n";
echo "<meta name=\"robots\" content=\"all\" />\n";
echo "<meta name=\"keywords\" content=\"Parametrizované,SQL,dotazy,PHP,mysqli\" />\n";
echo "<meta name=\"description\" content=\"Parametrizované SQL dotazy v PHP rozhraní mysqli\" />\n";
echo "<meta name=\"autor\" content=\"Kocour\" />\n";
echo "<title>Parametrizované SQL dotazy v PHP rozhraní mysqli</title>\n";
echo "</head>\n";
echo "<body>\n";
echo "<h1>Parametrizované SQL dotazy v PHP rozhraní mysqli</h1>\n";

$mysqli = new mysqli("localhost", "testik", "testik", "testik");
if ($mysqli->character_set_name()!="utf8mb4") { $mysqli->set_charset("utf8mb4"); }

$submit="poslat";
// ============== přihlášení uživatele (admina) =============
if (!empty($_POST[$submit]) and !empty($_POST['ucet']) and !empty($_POST['heslo'])) {
// tady načteme heslo z databáze a dáme do proměnné $hash_heslo
$ucet = $_POST['ucet'];
//$ucet = $mysqli->real_escape_string($ucet); // starší obrana proti SQL injection
$dotaz = "select * from uzivatele where ucet=\"".$ucet."\"";
//echo $dotaz."<br />";
$vysledek = $mysqli->query($dotaz);
$poczaznamu = $vysledek->num_rows;
// ********** SQL injection na Zadej účet: " OR "1"="1 ************ !!!!!!!!!!!!!!!!!!!!!!    
// vytvoří SQL dotaz: select * from uzivatele where ucet="" OR "1"="1"     !!! To vrátí všechny záznamy
if ($poczaznamu > 0) {
$zaznam = $vysledek->fetch_array(MYSQLI_ASSOC);
$hash_heslo = $zaznam['heslo'];
$heslo2 = $_POST['heslo'];
// porovnání hesel password_hash()-> password_verify()
if (password_verify($heslo2, $hash_heslo)) {
echo 'Správné heslo!';
} else {
echo 'Špatné jméno nebo heslo.<br />';
echo "<pre>";
echo "**********************************************************<br />";
echo " ----- H A C K N U T O   !!! -------                      <br />";
echo " nezapnutá obrana proti SQL injection !!!                 <br />";
echo "**********************************************************<br />";
echo "SQL dotaz: ".$dotaz."<br />";
echo "+-----+---------------+--------------------------------------------------------------+---------------+-----------+<br>";
echo "| uid | ucet          | heslo                                                        | prijm         | jmeno     |<br>";
echo "+-----+---------------+--------------------------------------------------------------+---------------+-----------+<br>";
echo "|   1 | admin         | &#36;2y&#36;10&#36;Zus3PC71xQit/2Bbu/esRO9nUGHHiUCyUASnPkO6mX3Ml0IngGtEm | Světlá        | Karolína  |<br>";
echo "</pre>";
}
} else {
echo 'Špatné jméno nebo heslo.<br />';
}   
}
// ==========================================================

// ============================= přihlašovací formulář ================================================
// účet:admin
// heslo:tajne-heslo-milanku
echo "<form method=\"post\">\n";
echo "<fieldset><br />\n";
echo "<legend>Zadání autorizačních údajů</legend>\n";
echo "Zadej přihlašovací účet<br />";
// SQL injection na Zadej přihlašovací účet: " OR "1"="1
echo "<input type=\"text\" name=\"ucet\" value=\"&quot; OR &quot;1&quot;=&quot;1\" size=\"\" /><br /><br />\n";
echo "Zadej heslo<br />";
echo "<input type=\"password\" name=\"heslo\" value=\"123456\" size=\"\" /><br />\n";
echo "<br /><input type=\"submit\" name=\"".$submit."\" value=\"odeslat\" />\n";
echo "</fieldset>\n";
echo "</form>\n";
// ======================================================================================================

$mysqli->close();  // není nutné
echo "</body>\n";
echo "</html>\n";
?>

Zobrazí se nám přihlašovací formulář s předvyplněným hackerským jménem. Přitom heslo může být libovolné a přesto je obejit přihlašovací formulář. Přesto, že jsme měli heslo správně uloženo jako password_hash(), přesto že jsme použili správně password_verify(), hacker se bez ošetřeného kódu proti SQL injektáži k přihlašovacím údajům dostal.

SQL injection

Takovýto neošetřený dotaz z formuláře je skvělé místo pro SQL injekci (injection). Místo očekávaného SQL dotazu: „select * from uzivatele where ucet="admin";“ byl dotaz pozměněn na:

select * from uzivatele where ucet="" OR "1"="1";

To hackerovi umožní obejít autorizační logiku a dodá všechny záznamy z tabulky uživatelé. Pokud ještě byly hesla uložena pomocí zastaralých MD5, SHA1 hashovacích funkcí, tak zná i jejich otevřený tvar a získá plnou kontrolu nad webovou aplikací.

hacknuto

Nyní už známe jak to nedělat. A níže je popsáno jak to dělat s parametrizovanými dotazy.
Ve zdrojovém kódu by stačilo odremovat (zrušit // ) na řádku 24 zapnout obranu proti SQL injekci (injection):
$ucet = $mysqli->real_escape_string($ucet);

sql injection obrana

Pak by byla aplikace zabezpečená starším a již nedoporučovaným způsobem.

Parametrizované dotazy v MySQLi

Ale existuje modernější a doporučovaný způsob obrany (nebo ochrany) proti SQL injekci (injection) - parametrizované dotazy v PHP. V parametrizovaných dotazech nevkládáme proměnné do dotazu ale dáváme zástupné znaky, obvykle otazníky. Proměnné předáme později najednou v poli. Bohužel oficiální dokumentace k parametrizovaným dotazům v PHP s rozhraním MySQLi je značně chaotickým mixem zastaralých řešení.

Používáme objektové:

  1. $mysqli->prepare($dotaz)
  2. $stmt->bind_param($typy, ...$parametry)
  3. $stmt->execute()
  4. $vysledek = $stmt->get_result() , viz např. Použití „LIKE“ v parametrizovaném dotazu

U bodu 2. je důležité a málo zdokumentované $stmt->bind_param($typy, ...$parametry), kdy jsou třeba už jen dvě proměnné, první typu string ($typy="ssssi"), druhá typu pole ($parametry=array($ucet,$heslo,$prijmeni,$jmeno,$uid)).
Už žádné zastaralé $stmt->bind_param('sssd', $code, $language, $official, $percent);bind_result(), call_user_func_array()... ale

$typy = "ssss";
$parametry = array($ucet,$heslo,$prijmeni,$jmeno);
...
$stmt->bind_param($typy, ...$parametry);

Jako typy parametrů (zde proměnná $typy) se používá:

Escapování proměnných u parametrizovaných dotazů již není potřeba ($mysqli->real_escape_string). To ale neznamená, že bychom je neměli vůbec ošetřovat. Minimálně u proměnných omezit délku, oříznutí počátečních a koncových mezer, strip_tags(), zbavit se znaků ", ', <, >.

SELECT

Příklad výběrového parametrizovaného dotazu v PHP a rozhraní MySQLi (objektová verze). Zde je důležité hlavně pole $parametry = array($ucet,$heslo,$prijmeni,$jmeno); $stmt->bind_param($typy, ...$parametry); To umožňuje snadno používat více parametrů v poli, nikoliv další proměnné, viz INSERT - přídání záznamu parametrizovaně.

$mysqli = new mysqli("localhost", "testik", "testik", "testik");
if ($mysqli->character_set_name()!="utf8mb4") { $mysqli->set_charset("utf8mb4"); }
$ucet = $_POST['ucet'];
$delka=15; $ucet = mb_substr(trim($_POST['ucet']),0,$delka,"utf-8"); $ucet=strip_tags($ucet);$ucet = str_replace(array("'",">","<",'"'), array("","","",""), $ucet);
// =================== parametrizovaný dotaz ===============  
$dotaz = "select * from uzivatele where ucet=?";
$typy = "s";
$parametry = array($ucet);
if ($stmt = $mysqli->prepare($dotaz)) {
$stmt->bind_param($typy, ...$parametry);
$stmt->execute();
$vysledek = $stmt->get_result();
$stmt->close();
$poczaznamu = $vysledek->num_rows;
while($zaznam = $vysledek->fetch_assoc()) {
echo $zaznam['jmeno']." ".$zaznam['prijm']."<br />";
}
}  
$mysqli->close();

parametrizovaný dotaz v MYSQLi

Pokud používáme výhradně parametrizované SQL dotazy a chceme vložit dotaz bez proměnných, stačí vynechat ->bind_param().

$mysqli = new mysqli("localhost", "testik", "testik", "testik");
if ($mysqli->character_set_name()!="utf8mb4") { $mysqli->set_charset("utf8mb4"); }
// =================== parametrizovaný dotaz ===============  
$dotaz = "select jmeno,prijm from uzivatele";
if ($stmt = $mysqli->prepare($dotaz)) {
  $stmt->execute();
   $vysledek = $stmt->get_result();
   $stmt->close();
   $poczaznamu = $vysledek->num_rows;
   while($zaznam = $vysledek->fetch_assoc()) {
     echo $zaznam['jmeno']." ".$zaznam['prijm']."<br />";
   }
}
$mysqli->close();

INSERT

Příklad pro parametrizovaný dotaz pro vkládání nových záznamů v PHP a s MySQLi jako obrana proti SQL injection (injekci).

$mysqli = new mysqli("localhost", "testik", "testik", "testik");
if ($mysqli->character_set_name()!="utf8mb4") { $mysqli->set_charset("utf8mb4"); }
$ucet = mb_substr(trim($_POST['ucet']),0,15,"utf-8");
$heslo = mb_substr(trim($_POST['heslo']),0,50,"utf-8"); $heslo = password_hash($heslo, PASSWORD_DEFAULT);
$prijmeni = mb_substr(trim($_POST['prijmeni']),0,25,"utf-8");
$jmeno = mb_substr(trim($_POST['jmeno']),0,25,"utf-8"); $jmeno = str_replace(array("'",">","<",'"'), array("","","",""), $jmeno);
$dotaz = "insert into uzivatele(ucet,heslo,prijm,jmeno) values(?,?,?,?)";
$typy = "ssss";
$parametry = array($ucet,$heslo,$prijmeni,$jmeno);
if ($stmt = $mysqli->prepare($dotaz)) {
$stmt->bind_param($typy, ...$parametry);
$stmt->execute();
echo $stmt->affected_rows;
$stmt->close();
}
$mysqli->close();

UPDATE

Bezpečná úprava záznamů (editace, update) odolná proti SQL injection (injekci), tedy výhradně s parametrizovanými dotazy v PHP a MySQLi rozhraní.

$mysqli = new mysqli("localhost", "testik", "testik", "testik");
if ($mysqli->character_set_name()!="utf8mb4") { $mysqli->set_charset("utf8mb4"); }
$uid = intval($_POST['uid']);
$delka=15; $ucet = mb_substr(trim($_POST['ucet']),0,$delka,"utf-8"); $ucet=strip_tags($ucet);
$delka=50; $heslo = mb_substr(trim($_POST['heslo']),0,$delka,"utf-8"); $heslo = password_hash($heslo, PASSWORD_DEFAULT);
$delka=25; $prijmeni = mb_substr(trim($_POST['prijmeni']),0,$delka,"utf-8"); $prijmeni=strip_tags($prijmeni);
$delka=25; $jmeno = mb_substr(trim($_POST['jmeno']),0,$delka,"utf-8"); $jmeno=strip_tags($jmeno); $jmeno = str_replace(array("'",">","<",'"'), array("","","",""), $jmeno);
$dotaz = "UPDATE uzivatele SET ucet = ?, heslo = ?, prijm = ?, jmeno = ? WHERE uid = ?";
$typy = "ssssi";
$parametry = array($ucet,$heslo,$prijmeni,$jmeno,$uid);
if ($stmt = $mysqli->prepare($dotaz)) {
$stmt->bind_param($typy, ...$parametry);
$stmt->execute();
echo $stmt->affected_rows;
$stmt->close();
}
$mysqli->close();

DELETE

Mazání záznamů v PHP a rozhraní MySQLi pomocí parametrizovaného dotazu, tedy bezpečné a odolné proti SQL injekci.

$mysqli = new mysqli("localhost", "testik", "testik", "testik");
if ($mysqli->character_set_name()!="utf8mb4") { $mysqli->set_charset("utf8mb4"); }
$uid = intval($_POST['uid']);
$dotaz = "DELETE FROM uzivatele WHERE uid = ?";
$typy = "i";
$parametry = array($uid);
if ($stmt = $mysqli->prepare($dotaz)) {
$stmt->bind_param($typy, ...$parametry);
$stmt->execute();
echo $stmt->affected_rows;
$stmt->close();
}
$mysqli->close();

Získání id primárního klíče

Zjištění ID primárního klíče v parametrizovaném dotazu v PHP a MySQLi.

$mysqli = new mysqli("localhost", "testik", "testik", "testik");
if ($mysqli->character_set_name()!="utf8mb4") { $mysqli->set_charset("utf8mb4"); }
$dotaz = "insert into uzivatele(ucet,heslo,prijm,jmeno) values(?,?,?,?)";
$typy = "ssss";
$parametry = array($ucet,$heslo,$prijmeni,$jmeno);
if ($stmt = $mysqli->prepare($dotaz)) {
$stmt->bind_param($typy, ...$parametry);
$stmt->execute();
echo $mysqli->insert_id;
$stmt->close();
}
$mysqli->close();


LIKE v parametrizovaném dotazu

Použití „LIKE“ v parametrizovaném dotazu je snadné, občas se dělají chyby nebo někdo dokonce říká, že to nejde.

Toto je špatně!
$stmt = $mysqli->prepare("SELECT id, name, age FROM myTable WHERE Name LIKE %?%");

Celý trik je v tom, že v případě použití LIKE v parametrizovaném dotazu znak % zadáváme jako součást proměnné! ($prijm = "Ma%";)
Ukázka, návod na použití LIKE v parametrizovaném dotazu s objektovým rozhraním MySQLi v PHP nad databází MariaDB (dříve MySQL).

$mysqli = new mysqli("localhost", "testik", "testik", "testik");
if ($mysqli->character_set_name()!="utf8mb4") { $mysqli->set_charset("utf8mb4"); }
// =================== parametrizovaný dotaz ===============  
$prijm = "Ma%";
$typy = "s";
$parametry = array($prijm);
$dotaz = "select * from uzivatele where prijm like ?";
//echo $dotaz."<br />";
if ($stmt = $mysqli->prepare($dotaz)) {
$stmt->bind_param($typy, ...$parametry);
$stmt->execute();
$vysledek = $stmt->get_result();
$stmt->close();
$poczaznamu = $vysledek->num_rows;
while($zaznam = $vysledek->fetch_assoc()) {
echo $zaznam['jmeno']." ".$zaznam['prijm']."<br />";
}
}
$mysqli->close();

Od PHP 8.1.0 je možné volitelně přidat do $stmt->execute(array(1,2,3)) parametr pole, podobně jako v PDO rozhraní. Tím je možno vynechat $stmt->bind_param($typy, ...$parametry) a používat jen prepare() a execute().

<?php
$mysqli = new mysqli("localhost", "redakcni_system", "moje tajne heslo", "redakcni_system");
if ($mysqli->character_set_name()!="utf8mb4") { $mysqli->set_charset("utf8mb4"); }
$jmeno = "Jan";
$dotaz = "select * from uzivatele where jmeno = ? limit ?";
$parametry = array($jmeno, 2);
$stmt = $mysqli->prepare($dotaz);
$stmt->execute($parametry);
$vysledek = $stmt->get_result();
$stmt->close();
$poczaznamu = $vysledek->num_rows;
while ($zaznam = $vysledek->fetch_assoc()) {
echo $zaznam['Prijmeni']." ".$zaznam['jmeno']."<br />";
}
$mysqli->close();
?>

Odkazy

PHP MySQLi Prepared Statements Tutorial to Prevent SQL Injection


Komentáře

2. Přezdívka: zden, email: , 22.10.2023 10:43:37
Především se musí ošetřit všechny vstupy z formulářů (POST) nebo z adresního řádku (GET),nejdříve třeba $delka=15; $ucet = mb_substr(trim($_POST['ucet']),0,$delka,"utf-8"); $ucet=strip_tags($ucet);$ucet = str_replace(array("'",">","<",'"'), array("","","",""), $ucet); pak postaru svatá trojice intval(), floatval(), real_escape_string(), po novu místo real_escape_string parametrizovaný dotaz.
Obecně se ukládat do databáze (tabulky) hesla v šifrovaném tvaru nedoporučuje, SQL injekci to nezabrání. Nyní je doporučeno používání password_hash("moje tajné heslo", PASSWORD_DEFAULT) již bez sloupce sůl (narozeninové útoky [birthday paradox] a tabulky předhašovaných hesel [rainbow tables]).
Webové aplikace z bezpečnostních důvodů nepřistupují k databázi přes veřejný internet, ale přes rozhraní localhost. Šifrovat všechna pole lze, ale má to vliv na výkon.

Viz:

https://zmsoft.cz/?str=bezpecne-ukladani-hesel-do-mariadb&hid=5&idmh=5

https://www.itnetwork.cz/java/spring-boot/blog/bezpecnostni-hrozby-jak-spravne-ukladat-hesla
1. Přezdívka: ben, email: , 20.10.2023 11:16:22
Děkuji za velmi kvalitní a srozumitelně napsaný článek. Jak se díváte na použití PHP funkce openssl_encrypt/decrypt? Pokud bych všechna vyplňovaná pole nejdříve zašifroval a teprve pak ukládal sql dotazem, byla by přece možnost napadení minimální. A navíc by citlivé údaje byly hezky zašifrované v databázi. Stejně jako heslo, které ukládám stejně jako vy funkcí password_hash().

Jak používat messenger Signál


SSL pro weby od 11/2015 zdarma


MS WINDOWS 10, 11 - sběr informací o uživateli


DEBIAN 12 (bookworm) - OS zdarma debian vyšel 10.6.2023

debian

debian - stáhnout nejnovější DEBIAN pro PC
debian edu - debian pro školy a školní prostředí, stažení DEBedu (torrent)


Zranitelnost „ROM-0“ routerů


Předali data tajným službám
Americké bezpečnostní agentuře (NSA) předali data Microsoft, Yahoo, Google, Facebook...


Itálie preferuje open source
Italský parlament schválil zákon, který nařizuje státním institucím pořizovat otevřený software před komerčním. To znamená LINUX místo MS-WINDOWS, LIBRE OFFICE místo MS OFFICE atd.

01.10. 2023 18:09:01
  • Redakční systém MRS
  • 10 nečastějších zranitelností WEBU
  • Esperantoesperanto - univerzální mezinárodní jazyk
  • Kryptografie okolo nás - kniha popisuje využití kryptografie v běžném životě
  • SMS zdarma - posílání SMS zdarma
  • proč LINUX
  • základy LINUXU
  • Software na úřadech - jeho otevřené alternativy
  • Řekněte sbohem Microsoftu
  • Rychlost připojení - změřte si svoji rychlost
  • SEO servis
  • Jak psát web
  • Zákony - občanský, autorský, obchodní zákoník atd.
  • Infosoud - nalezení stání a průběhu jednání
  • Soudní rozhodnutí - nalezení rozsudků
  • ARES - registr ekonomických subjektů
  • Katastr nemovitostí
  • Broďan - brodské nezávislé zpravodajství

vydělávejte
na burze kryptoměn

26.09. 2023 19:56:31
Návštěvy
Celkem: 286517
Týden: 819
Dnes: 122
  přihlásit poslední změna: 27.10. 2023 08:43:43