Хеширане на пароли

Ако вие сте начинаещ програмист, ако вие искате да се развивате, то този пост може да се окаже полезен за вас. Защо да хешираме паролите и какво всъщност е хеширането. Най-просто казано хеширането е преобразуването на даден стринг в друг по сложен или не чак толкова сложен математически модел. Защо да го правите? Защото ако някой се добере до вашата база данни и открадне всички пароли в чист вид, имейли, никове, то биха станали много бели – крадене на акаунти, онлайн сметки, имейли и какво ли още не. Когато една парола е хеширана, да се върне обратно е изключително трудно. Особено в нея, ако има някакви специални знаци от рода на $ / * + – ! ? & : ; | ‘ ” схващате идеята :) В днешно време разбирането на един прост хеш стринг е изключително лесна задача – брют форс или пък рейнбоу таблици. Т.е. имат ви всички хешове, сравняват в таблица с милиони записи от хешове, при познаване на само един хеш стринг всичко друго става много по-бързо – т.е. бързо ще се доберат до останалите пароли. Тези рейнбоу таблици за предназначени главно за md5 хеширане. Разбира се вие можете да си създадете свои начини за хеширане на паролите, но те със сигурност ще могат да се разбият изключително лесно от някой добър математик. Малко инфо за md5: MD5 е 128 битово хеширане на низ, който бива превърнат в 32 символен стринг от числа и букви. Друг вид хеширане е SHA1 (както и SHA0, SHA2 и в бъдеще SHA3) – то е 160 битово и е значително по-сигурно. При sha1 хеширането рейнбоу таблиците автоматично отпадат, но все още се ползва рядко.
Таааа как да си хешираме паролите? Аз лично ползвам md5, понеже ми върши перфектна работа, при md5 хеширането освен парола използвам и сол. Какво е сол – прибавен стринг към паролата. Така хеша става още по-труден за разбиване. Препоръчително е този стринг да е сравнително дълъг и да съдържа множество различни знаци – числа, букви и специални знаци.
Пример:
$salt=”ReallyLongString|:;.,<>/@$&^*()_=+-*/!”;
$hash=md5($salt.$pass);
Идеята е да знаете солта за да можете да подсигурете успешното влизане на юзъра в системата. Солта може да е неговото име, време на регистрация, имейл или каквото друго се сетите.
Дано съм ви бил полезен :)

Вземане тип на изображение / image type php

Тъй като сега имам проект, в който трябва да се работи повече със снимки – да се копират, преместват, създават, водни знаци, thumb размери, се наложи естествено да знам какъв е техният тип – jpeg, png, gif, bmp.
Аз съм малко тъп програмист от време на време и първоначално написах нещо такова.
if (end(explode('.', $name)) == 'png') {
$newimg = imagecreatefrompng($pic);
} elseif (end(explode('.', $name))== 'gif') {
$newimg = @imagecreatefromgif($pic);
} elseif (end(explode('.', $name)) == 'bmp') {
$newimg =imagecreatefromwbmp($pic);
} else {
$newimg = imagecreatefromjpeg($pic);
}

И някои си викате – е ко, бачка. И тогава идва проблема, когато някой тръгва да качва снимка от друг сайт, където картинката е от рода : imagnamee.jpeg?asd=asdasa или нещо друго. Тогава тръгват сумати и грешки.

Тогава си викам – еми тогава ще съкратя. Какво може да се случи…

if (substr(end(explode('.', $name)), 0, 3) == 'png') {
$newimg = @imagecreatefrompng($pic);
} elseif (substr(end(explode('.', $name)), 0, 3) == 'gif') {
$newimg = @imagecreatefromgif($pic);
} elseif (substr(end(explode('.', $name)), 0, 3) == 'bmp') {
$newimg = @imagecreatefromwbmp($pic);
} else {
$newimg = @imagecreatefromjpeg($pic);
}

Всичко работи естествено. Този проблем го няма! И тогава идва картинка, чието име е image.jpeg, но реално е png. Някой просто си я е променил така. Тогава пак грешки.

И в крайна сметка имало готово решение, което спестява доста главоболия, а имено функцията getimagesize, която връща в масив със съдържание – височина, ширина, битс, майм тайп на снимката :)


$picinfo=getimagesize($pic);

// Сега викаме широчина
echo $picinfo[0];
//сега викаме височина
echo $picinfo[1];
// сега викаме тип
echo $picinfo['mime'];
// Ще върне image/jpeg примерно.

Прост начин за обохждане на масив и връщане на резултатите в обратен ред


//Имаме прост стинг с числата от 0 до 10
$replace='0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,
49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,
73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,
98,99,100';

// Разделяме $replace с функцията explode, която съдържа минимум два показателя. Първият - разделител и вторият - стрингът, в който ще се търси. В нашия случай ще раздели стринга на $explode на , и ще създаде масив от резултатите
$replace=explode(',',$replace);

//След това обохждаме масива с цикъла for. Вземаме броя на елементите в масива, който в нашия случай е 101, ако броим и нулата. Първоначално $i е равно на 100, а с всяко следващо завъртване намалява понеже сме сложили на $i-- - т.е. декрементация (обратното на икрементация хах). Цикълът ще се завъртва докато $i не е по-голямо от 0 или равно.
for ($i=count($replace);$i>=0;$i--)
{
echo $replace[$i].',';
}


Нищо особено не е, но ако се чудите примерно как да го направите по бързия начин вместо да си играете да го правите ръчно, ето ви един добър вариант :)

Брояч на онлайн потребители – ООП урок

Направих един брояч за това колко онлайн потребителя има сега:

Ето таблицата:

CREATE TABLE IF NOT EXISTS `visitors` (
`visit_id` int(11) NOT NULL AUTO_INCREMENT,
`visit_ip` varchar(100) NOT NULL,
`visit_time` int(11) NOT NULL,
PRIMARY KEY (`visit_id`),
KEY `visit_ip` (`visit_ip`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;

Ето целия php код:

// връзка към база данни
$conn = mysql_connect('localhost', 'root', '');
mysql_select_db('visits', $conn);

//създаваме клас
class Visits {

// Определяме на колко минути искаме да се ъпдейтва брояча
public $maxtime = '5';

/създаваме конструктора. В нашия случай не приема параметри
public function __construct() {
// променливата ip приема ip на посетителя. Примерно 127.0.0.1
$ip = $_SERVER['REMOTE_ADDR'];
// трием всички посетители, които са посетили сайта ни преди повече от 5 минути

$this->RemVisit($ip);

// правим проверка дали съществува това ip в нашата база данни
if ($this->VisitExists($ip)) {

// ако съществува ъпдейтваме времето му
$this->UpdateVisits($ip);
} else {
// ако не съществува добавяме ip и време към базата данни
$this->AddVisit($ip);
}
// визуализираме колко посетителя има в момента
echo 'ima '.$this->ShowVisits().' posetitelq';
}

//създаваме публичен метод AddVisit, която приема параметъра $ip
public function AddVisit($ip) {
// sql заявка за вкарване на ip и време в базата данни
$sql = 'INSERT INTO visitors (visit_ip, visit_time) VALUES ("' . $ip . '",' . time() . ')';
mysql_query($sql);
}

// създаваме публичен метод за премахване на записи в базата данни, където visit_time е минало преди определеното от нас време
public function RemVisit($ip) {
mysql_query('DELETE FROM visitors WHERE visit_time>=' . time() + $this->maxtime * 60);
}

// проверка дали същестува посетител с това ip в базата данни
public function VisitExists($ip) {
$sql = mysql_query('SELECT visit_id FROM visitors WHERE visit_ip="' . $ip . '"');
if (mysql_num_rows($sql) == 1) {
return true;
} else {
return false;
}
}

// публичен метод заа ъпдейт на базата данни
public function UpdateVisits($ip) {
mysql_query('UPDATE visitors SET visit_time=' . time() . ' WHERE visit_ip="' . $ip . '"');
}

// публичен метод за визуализиране на броя посетители (резултатите в базата данни)
public function ShowVisits()
{
$sql=mysql_query('SELECT COUNT(visit_id) as allvisitors FROM visitors');
$rs=mysql_fetch_assoc($sql);
return $rs['allvisitors'];
}

}

// правим инстанция на класа Visits. Понеже сме задали магическия метод __construct няма смисъл да го присвояваме на променлива, после да визуализираме, да викаме методи от него и така нататък
new Visits();

ООП урок 2 – създаване на клас

След първия урок за OOP, който написах снощи, реших да пусна и втори, а имено нещо като второ въведение в OOP, тъй като предишният беше главно терминология. В този урок ще създадем първия ни клас и впоследствие обект.

Ето примерен клас:

class MathOperations {

public $var=6;

public function subirane($a,$b)
{
return $a+$b;
}

public function izvajdane($a,$b)
{
return $a-$b;
}

}

Това е най-обикновен клас с два метода – subirane и izvajdane. В случая метода subirane събира две числа и връща резултата, а метода izvajdane извжда $a-$b и връща резултата. Ние капсулирахме двата метода като public – т.е. те могат да бъдат викани в класа и извън класа. Можете и да не пишете public, ако смятате те да са публични, но е хубаво да се пише – просто в случай, че някой друг програмист седне да проверява да му е по-лесно четенето.
Сега ще създадем и обект. Както споменах в предния урок това става с ключовата дума new. В част от случаите (и в нашия) някаква променлива трябва да си присвои този обект. Ето и как става.

$myObject = new MathOperations();

Сега вече в $myObject се съдържа всичката информация за обекта MathOperations. Защо има () след MathOperations? Ами просто обектите също могат да приемат някакви стойности. По подразбиране не приема, но с магическия метод __construct, който се вика попринцип от php, но вие можете да си го напишете и конфигурирате по свои усмотрения, можете да избирате някакви параметри да се приемат от този обект. Нещо като при функциите. Всъщност вие ще го правите това, но когато напреднете още малко. В следващите уроци ще ви обясня за магическия метод __construct – какво прави, за какво е полезен и т.н.
Така… създадохме обект, $myObject го присвои, но нищо не излиза на екрана. Нормално, защото не сме извикали никой от методите (можем и свойства да викаме по същия начин).
Сега ще извикаме метода subirane, при което ще съберем 5 и 6. Как става това? Ето:

echo $myObject->subirane(5,6);

На вашият екран би трябвало да се покаже 11. Сега ще извикаме метода izvajdane

echo $myObject->izvajdane(10,7);

На вашият екран би трябвало да се покаже 3.

Сега ше извикаме и свойство.

echo $myObject->var;

Ше се покаже 6.
Как ги изкарахме. Пишем си стандартно едно echo за да визуализираме информация, после пишем промеливата, която е присвоила даден обект, после пишем ->, после пишем метода и вече ако има някакви параметри, които трябва да въведем ( в нашия случай $a и $b ) ние ги въвеждаме като стандартна функция. Т.е. izvajdane(10,7) примерно. Защо е -> не мога да обясня – свържете се с пичовете, които са създали php :) В един файл може да има безброй много класове и могат да се създават колкото си искате обекти дори от един клас стига да ви позволява компютъра, на който сте.

Примерно:

$mySecondObject = new MathOperations();

И няма да излезе абсолютно никаква грешка.

Можем и да напишем

$myThirdObject = new MathOperations();

и пак няма да има грешка.
Надявам се, че съм поднесал достатъчно добре информацията :) Критики и съвети в коментарите :)
Очаквайте още уроци за ООП и разни нещица, които са ми станали интересни в PHP :)

Въведение в OOP (Обектно ориентирано програмиране)

Както обещах ще почна и серия от уроци по ООП ( Обектно ориентирано програмиране ). Ще се опитам да ви разясня какво е ООП, кога, къде, защо да го ползвате, а пък вземе да дойде някой по-вещ и да драсне едно коментарче да допълни или да ме поправи в случай, че съм допуснал някаква грешка. :)
Какви са плюсовите на ООП?
– С него лесно разграничавате простия html код от php. Т.е. визуалността и логиката са разделени почти напълно и така лесно може да се осъществи работя между програмист и уеб дизайнер. Програмистът си пише класовете, осъщестява връзки между тях. В тези класове може да има абсолютно всичко – вземане на информация от база данни, добавяне, пресмятане, проверки и всичко друго, за което можете да се сетите. Може да има и html, но една от идеите е кодът да е по-четим, така че по-добре да няма html.
В този урок ще получите базисна информация, АМА АБСОЛЮТНО БАЗИСНА!
Сега ще ви дам понятията, а после ще напишем един клас :) Ако това беше ВЛОГ, щеше да е супер яко, ама си е блог.
Клас (Class) – както вече споменах класовете са така да се каже дефиниция на темплейт. Приемете го като мегафункция, ако искате, в която може да се пишат други функции.
Обект (Object) – обектът е активната част на един клас. Тук ще въведа и терминът инстанциране (от инстанция). Това е копирането на клас в паметта на php, даване на уникално име. Как да инстанцираме даден клас? С ключовата дума new. След малко ще видете жив пример.
Метод (Method) – сходно на функция, но написана в самия клас. Не е баш функция и никога не я наричайте функция, а метод.
Свойство (Property) – сходно на променлива. Разликата е, че е в класа и има някои особености, които ще ви покажа по-долу или в друг урок (за повече информация).
Полиморфизъм (Polymorphism) – възможността на ООП в два или повече класа да има два или повече метода или свойства с едно и също име. Примерно имаме онлайн магазин и при писането сме сложили в класа Product свойство Price, а при класа Category сме сложили пак свойство Price. Дори тези два класа да са в един файл тези две свойства няма да бъдат презаписани. Класовете не се интересуват какъв е кодът около тях, а само какво е при тях.
Капсулация – възможността да защитаваш информацията си извън класа. Примерно в класа Users имаш свойство или метод с име Password. При задаване на това свойство/метод, че е private, то никой извън класа не може да се добере до информацията в това свойство метод. Да кажем, че имаме сайт, в който има потребители – ник, имейл, парола и т.н. Някой иска да се добере до тази информация и да прави после бели. Няма значение как ще се добере – да хакне сайта, да се добере до някой файл, в който се инклуудват разни файлове с класове, на сървъра и да промени малко кода в този файл, така че да се опита да повика примерно свойството Pass от клас Users. Ако сме капсулирали Pass като private, просто няма да може, ама по никакъв начин. Ето и видовете капсулация – public, private, protected. В някой от следващите уроци ще обясня за тях и малко допълнително инфо за връзката между един клас с друг ( от друг!! не повече. В php не е възможно да се осъщестява връзка между повече от два класа наведнъж).
Наследяване (inheritance) – възможността на даден клас да приема свойства и методи от друг клас. Не е възможно един клас да наследява информация от два класа или повече, поне в PHP. В един клас може да има свойства и методи от повече от един клас, но това ще се получи така, ако в класа Class1 има дадени свойства и методи, Class2 наследява класа Class1, а пък Class3 наследява класа Class2. Така в Class3 ще има методите и свойствата от Class2 и Class1.
Това беше с терминологията, поне за този урок :) След някой друг ден, ако не съм зает, ще пусна следващ урок, в който ще създадем първия си клас, ще се опитам да обясня това-онова, кое защо и т.н.

Включване/инклуудване на файлове в php

Както знаете в php можете да включвате други файлове. Например във файла index.php може да инклуднете news.php или пък games.php въз основа на някаква зависимост, ако искате. Да добавите файлове към php файл можете да направите с готовите функции include и require (include_once и require_once). Разликата между include и require е, че при require ако не бъде добавен файла то ще спре и останалия код, а при include ще се появи просто грешка. include_once и reuiqre_once както разбирате по самото име го включва само веднъж. Т.е. ако вече някъде по-нагоре в кода е бил включен няма да го включи отново, а при include и require ще го включи отново и е възможно да има леко забавяне. Лично ви препоръчвам да не използвате include_once и require_once, тъй като има забавяне – php се забавя за прегледа дали вече е бил включен този файл. Затова основните файлове си ги добавяйте в началото на кода, а второстепенни като header.php footer.php и файлове, в които показват различни страници от сайта ви на необходимото място.
Та в тази публикация ми се искаше да ви дам просто една насока – използвайте пълния път до файла, а не примерно domain.com/files/includes/header.php или пък files/include/header.php. По този начин php трябва да се ориентира къде е, какво има в предходна директория, следваща и като цяло има малко забавяне. Това забавяне няма да се усети при примерно 100 посещения на ден, но при 100 посещения в минута вече може да усети. Та как да вземем пълния път до файл?
Ето как:
$path = dirname(__FILE__) . DIRECTORY_SEPARATOR;
__FILE__ ще върне пълния път до файла + името на файла. Функцията dirname() връща само пътя от целия път + файла. Примерно, ако __FILE__ ни връща /root/martox/project/config.php то с dirname ще ви върни само /root/martox/project без наклонена черта накрая както виждате! След това викам така да се каже вградената константа в php DIRECTORY_SEPARATOR (винаги с главни букви), която ще върне в зависимост под какво е сървъра за Windows или / за линукс, мак…
После при добавянето на файлове пишете
include $path.’my_file.php’; и сте готови
можете да напишете и include($path.’my_file.php’); Не съм забелязал разлика между без скоби и със скоби. Скоро предполагам ще почна и уроци за ООП. Поздрави!

Функция за заявка към база данни

Това е втората ми публикация от серията “Уроци“. Този път съм приготвил една проста функция за sql заявка:
function db_query ($sql)
{
mysql_query(‘SET NAMES utf8’);
return mysql_query($sql);
if (mysql_error())
{
$log=fopen($GLOBALS[‘path’].’logs.txt’,’r+’);
fwrite($log, mysql_error().’|||’.$sql.’|||’.time().”n”);
fclose($log);
}
}

Тъй като голяма част от проектите на програмистите трябва да поддържат и други символи освен тези от латиницата им се налага да използват някакъв по-специален енкодинг. В случая на нас ни върши перфектна работа utf8.
Тъй като php има така да се каже проблем с енкодинга се налага да се търсят някакви решения на този проблем. Примерно ‘б’ php ще го сметне като 2 символа, а ‘b’ като 1 символ. В предния си пост показах една сравнително елегантна функция за съкращаване на текст, която работи с всякакви символи. При работата с бази данни php прави проблем с енкодинга и всичко различно от латиница би се счупило.
Решение на този проблем е да се пише mysql_query(‘SET NAMES utf8’); преди всяка друга sql заявка, но много по-добро решение е това да се вмъкне във функция.
Ето и какво прави нашата функция за sql заявки.
Първо задава подходящия енкодинг, след това изпълнява и заявката с код $sql.
Примерно $sql е равно на ‘SELECT * FROM news’ или пък на ‘INSERT INTO users (username, pass) VALUES (“‘.mysql_real_escape_string($username).'”,”‘.md5($username.$pass).'”)’ – всякакъв sql код.
SQL грешки може да има поради много възможности – опит за хак, ваша грешка или потребител, който си играе или не знае какво прави.
Хубаво е да се записват тези грешки.
if (mysql_error())
{
$log=fopen($GLOBALS[‘path’].’logs.txt’,’r+’);
fwrite($log, mysql_error().’|||’.$sql.’|||’.time().”n”);
fclose($log);
}
Ако има sql грешка изпълняваме функцията fopen с път $path ($GLOBALS[‘path’]) и име на файла logs.txt. Задаваме правя върху файла r+ – т.е. може да се чете и да се пише в него. Файлът logs.txt трябва да е записан в същата директория, където ще се ползва и функцията. В този файл записваме
sql грешката, sql кода + времето и задаваме нов ред с n. Можете да запишете и ip на потребителя, който е направил грешката с $_SERVER[‘REMOTE_ADDR’].
След това затваряме файла с fclose.
Всичко би трябвало да работи.

Съкращаване на текст с php

Тъй като се чудих какво да правя сега и реших да пусна една публикация, в която да покажа една простичка, но елегантна функция за съкращаване на текст:
function trimtext($text, $max=100) {
if (mb_strlen($text, 'UTF-8')>$max) {
$text=mb_substr($text, 0, $max, 'UTF-8');
$text=mb_substr($text,0,mb_strrpos($text,' ','UTF-8'),'UTF-8').'…';
}
return $text;
}

И да обясня: mb_strlen – проверява дължината. Препоръчвам да използвате mb_strlen, тъй като само strlen няма да работи с кирилица и другите “special chars”. Ако дължината е по-голяма от $max (150) ще съкрати текста.
функцията mb_substr съкращава текста. Пак препоръчвам да се използва за “special chars”. Задължително му задайте енкодинг “UTF-8”, иначе може и да не сработи както очаквате. Първият показател ($text) е мястото, вторият показател е началото, а третият показател е краят. В нашия случай краят е променливата $max, която по-подразбиране е равна на 150.
функцията strrpos – намира мястото на зададения $needle. В нашия случай е просто интервал.
И така какво се случва. Да кажем, че в $text ще има 455 символа. Ние ще проверим дали символите са повече от 150. 455>150, следователно ще съкрати до 150-ия символ. След което ще проверим последното място на празното поленце. Да кажем, че ще ни върни, че това е 143 символ. След това отново ще съкратим като пак ще съкращаваме полученият резултат от първото съкращаване. След това пак избираме да е от началото до последният интервал, който се намира на 143 място. Така текстът ще бъде съкратен до 143 символ. В друг случай ще бъде съкратен до 145, в друг до 147 и т.н., но няма да надминава 150 и така дизайнът на вашата система няма да се развали по никакъв начин.

Това беше първият ми пост от серията “Уроци”. Надявам се, че съм ви бил полезен. Ако имате някакви идеи, напишете коментар :)