- Joined
- Jan 24, 2009
- Messages
- 785
- Reaction score
- 484
Здравейте, съфорумници,
Реших да напиша един урок за това как да защитите максимално кода си от външни фактори (aka пишлегари, научили 1-2 неща).
Отсега казвам, че това не са най-правилните начини, но вършат работа на 100%.
Филтрация
* Нещо, което не трябва да се използва според мен.
Най-често срещата грешка (поне в сайтовете тук) е, че всички разчитате на филтрацията, най-често с str_replace, което може да изиграе лоша шега. Имате по един масив (array) със забранените символи и ги махате от дадения стринг.
Вярвате или не, в по-горния пример всеки от "забранените" стрингове може да бъде изписан. Няма да казвам как, за да не науча някой на още нещо.
Решението на този проблем е много прост - не казвайте какво забранявате, а какво позволявате.
[^A-Za-z0-9\_] - този regex ще позволи само малки и големи латински букви (A-Z & a-z), числа (0-9) и долна черта (\_). Някой сеща ли се за начин за минаване на този филтър?
Валидация
Винаги, винаги, винаги, валидирайте дали стойността взета от протребителя отговаря на вашите очаквания.
Пример:
Очаквам потребителското име да е между 4 и 10 символа и да съдържа само букви, цифри и долна черта:
^[A-Za-z0-9\_]{4,10}$
^ - търсенето да започне от самото начало на $username
$ - търсенето да е до самия край на $username
A-Za-z - големи и малки латински букви
\_ - долна черна
{4,10} - между 4 и 10 символа
^ и $ са важна част от regex-a, винаги ги използвайте, иначе тази валидация може да бъде лесно преодоляна.
Повече:
RegexOne - Learn Regular Expressions - Lesson 1: An Introduction, and the ABCs
Типове данни
PHP е нетипизиран език - потребителя може да въведе всичко във всяка заявка и PHP няма да има нищо против да присвои стойността към някой глобален масив.
Но за сметка на това PHP ни дава функциии, с които да преобразуване всяка променлива към някой тип.
Когато потребителя е длъжен да въведе число, използвате int cast или intval
Повече информация:
PHP: Type Juggling - Manual
Prepared Statements
Prepared statements спират 99% от SQL инжекциите, но все пак трябва да е последното ниво на защита, не трябва да предавате никакви стойности без да са минали през валидация и типизиране (типове данни). Филтрацията не е задължителна, ако имате валидация и типизиране.
Ако трябва да ги обяснявам тук... поста ще стане километричен. Има доста материали по темата и можете да ги погледнете:
PHP Prepared Statements
�5.4.MySQLi - Базово използване - част 2 - Prepared Statements (Gatakka �7.1�.2�13) - ��.34.32h - YouTube - на български!!
Често използвани Regex-и
Това са regex-и, които аз използвам често и смятам, че биха били от полза и за вас:
^[A-Za-z0-9\_\.\-]{1,20}@[A-Za-z0-9\_\-]{1,20}\.[A-Za-z]{2,7}$ - Валидация на email (максимална дължина 49 символа)
^[A-Za-z0-9\_]{4,10}$ - потребителско име
^([a-zA-Z\-\']{2,20}\s?){1,3}$ - лично име (максимална дължина 62 символа, преди това трябва да мине през trim()). Ако сайтът ви е интернационален препоръчавм добавянето и на единична кавичка (') за имена като O'Sullivan. После трябва да бъде преобразувана в "
Надявам се поста да е от полза, ако имате допълнителни въпроси пишете в темата!
Поздрави,
RaFa
Реших да напиша един урок за това как да защитите максимално кода си от външни фактори (aka пишлегари, научили 1-2 неща).
Отсега казвам, че това не са най-правилните начини, но вършат работа на 100%.
Филтрация
* Нещо, което не трябва да се използва според мен.
Най-често срещата грешка (поне в сайтовете тук) е, че всички разчитате на филтрацията, най-често с str_replace, което може да изиграе лоша шега. Имате по един масив (array) със забранените символи и ги махате от дадения стринг.
PHP:
<?php
$forbidden = array('shutdown', 'drop', 'update');
$username = str_replace($forbidden, '', $_POST['username']);
Вярвате или не, в по-горния пример всеки от "забранените" стрингове може да бъде изписан. Няма да казвам как, за да не науча някой на още нещо.
Решението на този проблем е много прост - не казвайте какво забранявате, а какво позволявате.
PHP:
<?php
$username = preg_replace('/[^A-Za-z0-9\_]/', '', $_POST['username']);
[^A-Za-z0-9\_] - този regex ще позволи само малки и големи латински букви (A-Z & a-z), числа (0-9) и долна черта (\_). Някой сеща ли се за начин за минаване на този филтър?
Валидация
Винаги, винаги, винаги, валидирайте дали стойността взета от протребителя отговаря на вашите очаквания.
Пример:
Очаквам потребителското име да е между 4 и 10 символа и да съдържа само букви, цифри и долна черта:
PHP:
<?php
$username = $_POST['username'];
if(!preg_match('/^[A-Za-z0-9\_]{4,10}$/', $username)) {
// Потребителското име на отговаря на горните критерии.
exit; // Ако нещо не е наред, не продължавайте изпълнението на скрипта!
}
^[A-Za-z0-9\_]{4,10}$
^ - търсенето да започне от самото начало на $username
$ - търсенето да е до самия край на $username
A-Za-z - големи и малки латински букви
\_ - долна черна
{4,10} - между 4 и 10 символа
^ и $ са важна част от regex-a, винаги ги използвайте, иначе тази валидация може да бъде лесно преодоляна.
Повече:
RegexOne - Learn Regular Expressions - Lesson 1: An Introduction, and the ABCs
Типове данни
PHP е нетипизиран език - потребителя може да въведе всичко във всяка заявка и PHP няма да има нищо против да присвои стойността към някой глобален масив.
Но за сметка на това PHP ни дава функциии, с които да преобразуване всяка променлива към някой тип.
Когато потребителя е длъжен да въведе число, използвате int cast или intval
PHP:
<?php
$strength = (int) $_POST['strength'];
$agility = (int) $_POST['agility'];
// Каквото и да е въвел потребителят, стойностите ще бъдат преобразувани в числа.
// Дори да е въвел някакъв супер-мега-ултра хакерски скрипт... ще ви бъде върнато числото 0
// Отново валидация
if ($strength < 0 && $strength > 1000) {
// Стойността на strength трябва да е по-голяма или равна на 0 и по-малка или равна на 1000
exit;
}
Повече информация:
PHP: Type Juggling - Manual
Prepared Statements
Prepared statements спират 99% от SQL инжекциите, но все пак трябва да е последното ниво на защита, не трябва да предавате никакви стойности без да са минали през валидация и типизиране (типове данни). Филтрацията не е задължителна, ако имате валидация и типизиране.
Ако трябва да ги обяснявам тук... поста ще стане километричен. Има доста материали по темата и можете да ги погледнете:
PHP Prepared Statements
�5.4.MySQLi - Базово използване - част 2 - Prepared Statements (Gatakka �7.1�.2�13) - ��.34.32h - YouTube - на български!!
Често използвани Regex-и
Това са regex-и, които аз използвам често и смятам, че биха били от полза и за вас:
^[A-Za-z0-9\_\.\-]{1,20}@[A-Za-z0-9\_\-]{1,20}\.[A-Za-z]{2,7}$ - Валидация на email (максимална дължина 49 символа)
^[A-Za-z0-9\_]{4,10}$ - потребителско име
^([a-zA-Z\-\']{2,20}\s?){1,3}$ - лично име (максимална дължина 62 символа, преди това трябва да мине през trim()). Ако сайтът ви е интернационален препоръчавм добавянето и на единична кавичка (') за имена като O'Sullivan. После трябва да бъде преобразувана в "
Надявам се поста да е от полза, ако имате допълнителни въпроси пишете в темата!
Поздрави,
RaFa