8

Пользователь может оставлять в базе данных 2 типа ссылок.

  • ссылка на какой либо ресурс (именно сайт, блог, форум и тд)
  • ссылка на изображение ( баннер, картинка, скриншот и тп.)

Мне необходимо проверить валидность этих ссылок, что за первой находится именно реальный сайт, а за второй именно изображение, (расширения в принципе можно Ограничить конкретными gif, png и jpeg).

По сути я пытаюсь защититься от нехорошего пользователя, который решит поломать моё детище, отправив в БД какую-нибудь заразу.

Вот что имею в чистом виде без валидации:

//<!-- Ссылки на ресурс banURL1 + изображение banIMG1 -->

if ($_POST['banURL1'] != '') {
    if (isset($_POST['banURL1'])) {
        $banURL1 = $_POST['banURL1'];
        if ($banURL1 ==   '') { 
            unset($banURL1);
        }
    }

    $banURL1_SQL = mysql_real_escape_string($banURL1);
    mysql_query (" UPDATE `users` SET `banURL1` = '$banURL1_SQL' WHERE `users`.`user` = '".mysql_real_escape_string($_SESSION['userin'])."';") ;  

    if ($_POST['banIMG1'] != '') {

    }
    if (isset($_POST['banIMG1'])) {
        $banIMG1 = $_POST['banIMG1'];
        if ($banIMG1 == '') {
            unset($banIMG1);
        }
    }

    $banIMG1_SQL = mysql_real_escape_string($banIMG1);
    mysql_query (" UPDATE `users` SET `banIMG1` = '$banIMG1_SQL' WHERE `users`.`user` = '".mysql_real_escape_string($_SESSION['userin'])."';") ;  
}

!!!!!!!!!!!!! АПДЕЙТ !!!!!!!!!!!!!!

С помощью добрых людей, на сегодня получилась вот такая конструкция:

//////////////////////////////

if ($_POST['banURL1'] != ''){
if (isset($_POST['banURL1'])){ $banURL1 = $_POST['banURL1']; if ($banURL1 == '') { unset($banURL1);} }

// ВАЛИДАЦИЯ URL

ini_set('display_errors', 'Off'); // теперь сообщений НЕ будет
if (!filter_var($_POST['banURL1'], FILTER_VALIDATE_URL) === false) {
$headers = get_headers($_POST['banURL1'], 1);
if ($headers == false){

  echo '<div class="col-xs-12 col-md-8" style="margin-bottom:  3px;">
  <div style= "background-color: black; padding: 5px 20px;" class="bord-block" data-toggle="modal" data-target="#ban1">
  <a href="#" >Oops! ' . htmlspecialchars($banURL1) . ' ТАм сайта нет!</a>
  </div>
  </div>';
}
else
{
    $type = $headers["Content-Type"];  
    if ($type == 'text/html; charset=utf-8' )

    {
    $banURL1_SQL = mysql_real_escape_string($banURL1);
        mysql_query (" UPDATE `users` SET `banURL1` = '$banURL1_SQL' WHERE `users`.`user` = '".mysql_real_escape_string($_SESSION['userin'])."';") ;  
    }
    else
    { 

 echo '<div class="col-xs-12 col-md-8" style="margin-bottom:  3px;">
  <div style= "background-color: black; padding: 5px 20px;" class="bord-block" data-toggle="modal" data-target="#ban1">
  <a href="#" >Oops! ' . htmlspecialchars($banURL1) . ' Это НЕ САЙТ!</a>
  </div>
  </div>';
    //echo("$url is not a valid URL");
   }    
  }

} else {

  echo  '<div class="col-xs-12 col-md-8" style="margin-bottom:  3px;">
  <div style= "background-color: black; padding: 5px 20px;" class="bord-block" data-toggle="modal" data-target="#ban1">
  <a href="#" >Oops! ' . htmlspecialchars($banURL1) . ' Кривые символы! URL!</a>
  </div>
  </div>';
 //echo("$url is not a valid URL");
}

ini_set('display_errors', 'On'); // сообщения с ошибками будут показываться

///


// IMG 1Валидация

if ($_POST['banIMG1'] != ''){

if (isset($_POST['banIMG1'])){ $banIMG1 = $_POST['banIMG1']; if ($banIMG1 == '') { unset($banIMG1);} 
ini_set('display_errors', 'Off'); // теперь сообщений НЕ будет
$im = imagecreatefromstring(file_get_contents($banIMG1));
if ($im === false) {
 //echo $banIMG1.' = Invalid image<br/>';
 echo
 '<div class="col-xs-12 col-md-8" style="margin-bottom:  3px;">
  <div style= "background-color: black; padding: 5px 20px;" class="bord-block" data-toggle="modal" data-target="#ban1">
  <a href="#" >Oops! ' . htmlspecialchars($banIMG1) . ' Invalid URI for IMG! Please enter a valid URL!</a>
  </div>
  </div>';
//exit();
}
else{
 //echo $banIMG1.' = ok<br/>';
    $banIMG1_SQL = mysql_real_escape_string($banIMG1);
 mysql_query (" UPDATE `users` SET `banIMG1` = '$banIMG1_SQL' WHERE `users`.`user` = '".mysql_real_escape_string($_SESSION['userin'])."';") ;  
}

imagedestroy($im);

  } 
 }
}

 ini_set('display_errors', 'On'); // сообщения с ошибками будут показываться

/////////////////////////////

При ближайшем рассмотрении вроде как работает... Возможно у кого то будут полезные замечания и конструктивная критика

Извините за формат кода... пока не освоился с правильным способом размещения его на портале.....

!!!! АПДЕЙТ 2 !!!!

На локальном работает нормально.. На хостинге все URL признаёт как "Это НЕ САЙТ!" НЕ ИЗОБРАЖЕНИЯ в БД не пускает В общем вопрос актуален.

sanmai
  • 12,320
lavi
  • 81
  • 4
    Дело в том, что эти ссылки и картинки будут видеть другие пользователи, за их безопасность я беспокоюсь как за свою - тогда зачем используете оооооооочень старое расширение mysql_query которое НЕБЕЗОПАСНО? на дворе 2017 год и все перешли на mysqli и PDO. Советую. Нет. Я настаиваю, чтобы вы об этом почитали и сделали всё, как будет действительно правильно и безопасно – Алексей Шиманский May 09 '17 at 12:24
  • Ссылка проверяется загрузкой ссылки – Sergey May 09 '17 at 13:17
  • Спасибо Алексей, я об этом уже осведомлён. К сожалению когда начинал делать сайт - не знал об этом.... теперь боюсь всё испортить, слишком далеко возвращаться. А пройти второй круг "ада НУБ программера на пхп" я пока не готов....))) Если это всего лишь замена одного тега на другой, то ещё как то... но если придётся пределывать весь функционал... то... даже не знаю с какой стороны подходить(((((( – lavi May 09 '17 at 13:17
  • @lavi в вашем коде нужно просто изменить 2 строчку. Посмотрите как делаются запросы в PDO и сравните с mysql,и поймете что,нужно заменить) – tweeker May 09 '17 at 14:55
  • Да Алексей, скорей всего буду переделывать, но сначала решить бы текущий вопрос. – lavi May 12 '17 at 14:09
  • Автор, пишите пожалуйста насколько можете грамотно и связно, не нужно выливать поток мыслей в вопрос. Убрал всё, что не относилось к вопросу: Приветствия, подписи, мольбы, благодарности, лирические отступления, истории из жизни – Nick Volynkin May 13 '17 at 08:59
  • 'text/html; charset=utf-8' - с моей точки зрения это не правильно, а если у сайта другая кодировка? У многих сайтах стоит другая кодировка, допустим пикабу, там - text/html; charset=windows-1251. – Alexander Semikashev May 14 '17 at 07:12
  • На счёт лирики понял... – lavi May 14 '17 at 16:47
  • Александр Семикашев, с Вам согласен, кодировка может быть любой, что бы Вы добавили в эту строку? Это сравнение ответа на GET запрос по заявленному URL... что ещё такого обязательного, кроме 'text/html; должно быть в ответе у "нормального" сайта/блога/и тд??? – lavi May 14 '17 at 16:57
  • 1
    Чтоб иметь то или иное представление об вредоносных действий пользователей и защиты от них рекомендую https://ru.stackoverflow.com/a/664833/207445 – Vanya Avchyan May 15 '17 at 09:35
  • @VanyaAvchyan ваш ответ, без сомнений, полезен, но как он соотносится с заданным тут вопросом? – tym32167 May 15 '17 at 12:43
  • @tym32167 Там есть 8 пункт где описывается работа с файлами.По моему стоит учесть потому что автор обеспокоен безопасностью "По сути я пытаюсь защититься от нехорошего пользователя".Если учесть это упоминание автора то по сути все нынче ответы являются не полноценными – Vanya Avchyan May 15 '17 at 12:58
  • @VanyaAvchyan На самом деле у автора нет задачи сохранять файлы к себе на сервер. Проблема в том, что ему надо валидировать ресурсы, которые лежат по указанным URL. При этой валидации сохранять файлы не требуется, если можно удаленный ресурс просто загрузить в память, там проверить и затем выгрузить из памяти. – tym32167 May 15 '17 at 14:27
  • Vanya Avchyan, спасибо за ссыль, полезная информация для новичка, добавил в закладки буду изучать, но действительно на текущем этапе мою задачу, к сожалению не решает.... – lavi May 15 '17 at 15:01
  • Я добавил свой вариант ответа учитывая эту информацию – Vanya Avchyan May 17 '17 at 09:59
  • Ваша задача не имеет однозначного решения. Вы не можете знать точно что находится за ссылкой, только предполагать. Например, сегодня за ссылкой картинка такая, а завтра - совсем другая. Одумайтесь! – sanmai May 18 '17 at 02:15

5 Answers5

11

Для валидации данных в PHP предусмотрены встроенные функции.

<?php
$url = "https://ru.stackoverflow.com/";

if (filter_var($url, FILTER_VALIDATE_URL)) {
    echo "Ссылка настоящая";
} else {
    echo "Совсем не ссылка";
}
sanmai
  • 12,320
  • Спасибо Александр, мануал я конечно же читал. Я пока не могу понять какого способа проверки ссылок будет достаточно в моём случае.... ""за первой находится именно реальный сайт, блог, форум и тд, за второй именно изображение""" и никак иначе.... а так, как я понимаю, валидную ссылку можно дать на что угодно.... – lavi May 11 '17 at 17:22
  • @lavi допустим я захожу на любой форум, регистрируюсь и захочу кинуть ссылку на вирус. Запись со ссылкой будет опубликована, я редко видел форум, чтобы как-то себя защищали. В случай форум - это работа модератора. – Alexander Semikashev May 12 '17 at 06:54
  • Да, именно так, ссылки отправляются в БД и я хотел бы быть уверен, что эти ссылки соответствуют условиям: Первая ведёт на ресурс сайт, блог, форум и тд, Вторая на изображение, (формат можно ограничить png, gif, jpg) Потому и задаю этот вопрос специалистам: КАКОЙ способ валидации (хотя может я не тот термин использую) или какой способ защиты выбрать в моём случае? – lavi May 12 '17 at 09:00
  • 1
    @lavi допустим я опубликую ссылку вида www.pro.com/forum/. Человек нажал, а там вирус. Проверить это никак не сможем. Удостоверится что это изображение можно с помощью -http://php.net/manual/ru/function.get-headers.php. Я так думаю, что стоит сделать так: человек добавил запись в БД, следом модератор проверяет всё ли в порядке и публикует запись. – Alexander Semikashev May 12 '17 at 10:26
  • Хорошо бы если б модератор.... К сожалению таковая "должность" отсутствует по определению... ресурс должен работать в автоматическом режиме... Проверил URL - сеть такой сайт/блог/форум и тд - добавил запись в БД !! или !! Этот URL не направляет на ресурс "традиционно размещённый" в сети, а ведёт хрен пойми куда и на что - Выдаём ошибку: Ваша ссылка нам не нравится... То же и с изображением - есть изображение, нет лишних тегов - добавляем запись в БД !!или!! ссылка ведёт не к изображению, найдены сомнительные теги найдены в файле - Ошибка: У Вас кривое изображение.... Ну как то так.... – lavi May 12 '17 at 13:43
  • @lavi мне кажется тебе надо сформулировать требования более конкретно. Какой именно УРЛ и какой именно контент за ним ты считаешь валидным, Например, для странички можно сделать валидацию, что УРЛ похож на УРЛ, что по запросу по этому урлу приходит http код 200, что тип контента в заголовках HTML. Но вот что там - блог о пирожных или контент 18+ ты не определишь без человека. – tym32167 May 12 '17 at 15:05
  • ДА! именно так не более того: блог о пирожных, автомобильный форум или контент 18 - это на совести пользователя. Главное, что не файл или страница с вирусом, кодом и тд... я ведь НУБ!!! я не знаю какие ещё нехорошие варианты бывают. А про картинки то же самое, главное что б это было ИЗОБРАЖЕНИЕ, без скриптов внутри а тупо картинка. – lavi May 12 '17 at 15:32
  • Ну ты страницу с вирусом от просто страницы тоже не отличишь. Они обе могут вернуть одинаковый код ответа и одинаковые заголовки. По сути, что та, что та - обе обычные html страницы. – tym32167 May 12 '17 at 15:46
  • Что ж тогда выходит? Моя задача не решаема? да... читать мануалы о защите и валидации и прочие страшилки о злобных хакерах для меня новичка походу вредно ))))) У меня паранойя начинается... – lavi May 12 '17 at 18:12
5

В дополнение к предыдущему ответу, валидация URL с картинкой:

$url = 'https://images.unsplash.com/photo-1435777940218-be0b632d06db?dpr=1&auto=format&fit=crop&w=1500&h=844&q=80&cs=tinysrgb&crop=&bg=';

if(filter_var($url, FILTER_VALIDATE_URL)) {

$f = file_get_contents($url);
$fi = finfo_open(FILEINFO_MIME_TYPE);
$type = finfo_buffer($fi, $f);
finfo_close($fi);

switch($type)
{
    case 'image/jpeg': file_put_contents('image.jpg', $f); break;
    case 'image/png':  file_put_contents('image.png', $f); break;
    case 'image/gif':  file_put_contents('image.gif', $f); break;
    default: echo 'Not jpeg / png / gif';
}

} else echo 'Not valid URL';

Проверяется URL, скачивается его содержимое, проверяется вне зависимости от расширения файла на присутствие jpeg / png / gif. Если удовлетворяет условиям - сохраняется с расширением, зависящим от содержания файла.

При этом сервер должен быть настроен так, чтобы файлы с расширениями .jpg, .png и .gif не выполнялись, а просто отдавались.

  • Андрей спасибо, уже что то.... Только как понять "не выполнялись" ??? jpg, png они статичны, а gif - он же должен воспроизводиться.... Или это я как дилетант не правильно понимаю????? – lavi May 12 '17 at 18:16
  • @lavi Имеется в виду, чтоб не выполнялись как PHP-код. Анимашки будут воспроизводиться :) –  May 12 '17 at 18:30
4

Что-то такое придумал:

set_error_handler(function($errno, $errstr, $errfile, $errline, array $errcontext) {
    if (0 === error_reporting()) {
        return false;
    }
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});

function check_url($url) {
    try {
        get_headers($url);
        return true;
    } catch (Exception $e) {
        return false; 
    }
}

$urls = [
    'http://vk.com',
    'http://example.com',
    'http://exampless.com',
    'http://google.com',
    'http://youtube.com',
];

foreach ($urls as $url) {
    echo $url . ' - ' . vd(check_url($url), 1) . '<br>';
}

Картинка

phen0menon
  • 653
  • 5
  • 19
Enforcer
  • 346
  • Спасибо, к сожалению без доп комментариев мне довольно трудно разобраться в Вашем варианте. – lavi May 15 '17 at 18:05
  • Ну смотри первая часть, то как я понял отключение обработчика ошибок, для того чтобы можно было использовать конструкцию try..catch во второй части, вторая часть это наша функция для проверки собственно ссылки, в ней мы используем стандартную функцию get_headers – Enforcer May 28 '17 at 11:19
2

Рекомендую... стоп, нет... НАСТАИВАЮ использовать filter_var

filter_var($value, FILTER_VALIDATE_URL);

а еще НАСТАИВАЮ использовать PDO

Ruslan
  • 884
  • Спасибо за ответ, но Ваши рекомендации не только уже присутствуют в других ответах/комментариях/самом вопросе, но еще и не помогают автору решить проблему с валидацией контента. – tym32167 May 16 '17 at 06:39
  • @tym32167 валидировать фильтрованный URL не нужно – Alex78191 May 20 '17 at 17:06
  • @Alex78191 проблему с валидацией контента контента по урлу, не урла – tym32167 May 20 '17 at 18:42
  • @Alex78191, товарищ, уважаемый, а что вам нужно то? – Ruslan May 20 '17 at 18:44
2

Можно конечно проверять следующим образом:

<?php       
    $uri = "http://some/path/uri";
    //Создаём массив для хранения информации об URI
    $uriInfo = [
        'isValidUri' => false,       // храним инфо об инвалидности
        'isAccessibleUri' => false,  // храним инфо об доступности
        'isImage' => false,          // храним инфо изображение или нет
        'path' => $uri,              // храним путь к ресурсу
    ];
    // Проверяем URI на валидность
    if (filter_var($uri, FILTER_VALIDATE_URL))
    {
        $uriInfo['isValidUri'] = true;
        echo 'Да, это валидный uri!'.'<br>';
        echo 'Но не ясно доступен ли ресурс по нему'.'<br>';
    }

    // Проверить доступен ли ресурс по данной ссылке
    if (@file_get_contents($uri))
    {
        // можно проверить и на MIME types 'text/html'
        // https://developer.mozilla.org/ru/docs/Web/HTTP/Basics_of_HTTP/MIME_Types
        // Content-Type : text/html
        $uriInfo['isAccessibleUri'] = true;
        echo "Да, uri доступен !".'<br>';
    } 

    // Проверяем является ли URI картинкой
    if($uriInfo['isAccessibleUri'] && is_array(getimagesize($uri)))
    {
        $uriInfo['isImage'] = true;
        echo 'Да, и это  изображение!'.'<br>';
    }

    if($uriInfo['isImage'])
    {
        // Соответствующая логика
        // Можно проверять изображения на mime tupes http://php.net/manual/ru/function.exif-imagetype.php
        if (exif_imagetype($uri) != IMAGETYPE_GIF)
        {
            echo 'Картинка не gif'.'<br>';
        }
        // аналогичные действия если нужно 
    } elseif($uriInfo['isValidUri'] && $uriInfo['isAccessibleUri']) {
        // Соответствующая логика
    } else {
        echo 'Это не тот URI который мне нужен !';
    }
?>

Если хотите безопасно загружать файлы на сервер тут рекомендую ознакомиться с https://ru.stackoverflow.com/a/664833/207445 8 пунктом

Акцент данного ответа сделан больше на то чтоб показать то или иное действие который мог бы понадобиться автору для решения своих задач.

Удачи :)

  • Vanya Avchyan, спасибо, полезная информация. – lavi May 17 '17 at 10:05
  • @lavi ответ вас не удовлетворяет ? по моему учтены все ваши требования – Vanya Avchyan May 17 '17 at 10:16
  • @VanyaAvchyan Спасибо за ответ. По сути, это только половина требований - валидация, что за урлом изображение. Есть второе требование - валидация, что урл ведет на вебсайт. – tym32167 May 17 '17 at 16:02
  • @lavi @file_get_contents($uri) вы эту часть смотрели ,что она делает по вашему ? – Vanya Avchyan May 17 '17 at 16:31
  • @VanyaAvchyan Как этот код поможет понять, что именно по урлу находится и решить задачу https://ru.stackoverflow.com/questions/663750/%D0%9A%D0%B0%D0%BA-%D0%B2%D0%B0%D0%BB%D0%B8%D0%B4%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C-url#comment961972_663751 ? Суть в проверке самого ресурса, не только в доступности – tym32167 May 17 '17 at 21:59
  • @tym32167 Content-Type : text/html пользуйтесь этим заголовком. – Vanya Avchyan May 19 '17 at 06:41
  • @VanyaAvchyan так этим и пользуется автор вопроса. Единственно что, наверное, ему лучше бы не точно сравнивать значения, а проверять Contains или StartsWith, так как там ещё идет кодировка дальше, а она может быть разная. – tym32167 May 19 '17 at 09:12