CSRF на търсачка

Формата ти преди този код ли е?


Код:
if (isset($_GET['searchf'])) {
    $searchdata['search'] = $_GET['search'];
    $token = $_GET['tokens'];


    if (isset($_SESSION['toksearch']) && $token === $_SESSION['toksearch']) {
        echo 'ok';
    } else {
        echo 'try again';
    }
}
 
pro12 каза:

Значи проблема е следния:

Формата ти се извиква преди кода за проверката, а във формата РЕГЕНЕРИРАШ нов токен.

След това проверяваш дали в токена от урла е същия с този, който вече си регенерирал (възможно е някога да съвпаднат, но шанса е минимален).

При var_dump (видях, че е в <head>), което е преди формата и все още не е презаписан токена и затова съвпадат.
 
@dakata__92 много съм любопитен да видя как ще парснеш страницата така, че да вземеш ключът на потребителя. Ето ти код, върху който да размишляваш:

CSRFToken.php
PHP:
<?php

class CSRFToken
{
    const TOKEN_NAME    = 'csrf_token';
    const PATH          = '/';
    const DOMAIN        = 'localhost';
    const SECURE        = false;
    const HTTP_ONLY     = true;

    /**
     * @var string
     */
    private static $tokenValue = null;

    private function __construct()
    {
        // Doesn't need an instance
    }

    /**
     * Sets a cookie with randomly generated security token.
     */
    public static function setSecurityToken(): void
    {
        self::$tokenValue = uniqid();

        setcookie(
            self::TOKEN_NAME,
            self::$tokenValue,
            0,
            self::PATH,
            self::DOMAIN,
            self::SECURE,
            self::HTTP_ONLY

        );
    }

    /**
     * Returns the security token cookie, or null if this is
     * the first time the user hits a guarded area.
     *
     * @return null|string
     */
    public static function getSecurityToken(): ?string
    {
        return $_COOKIE[self::TOKEN_NAME];
    }

    /**
     * Returns the randomly generated security token, or null
     * if it's called before <b>setSecurityToken()</b>.
     *
     * @return null|string
     */
    public static function getTokenValue(): ?string
    {
        return self::$tokenValue;
    }

    /**
     * Checks if the given security token is valid.
     *
     * @param string $token
     * @return bool
     */
    public static function isTokenValid(string $token): bool
    {
        return self::getSecurityToken() === $token;
    }
}

query.php
PHP:
<?php

require 'CSRFToken.php';

CSRFToken::setSecurityToken();

if (isset($_GET[CSRFToken::TOKEN_NAME]) && CSRFToken::isTokenValid($_GET[CSRFToken::TOKEN_NAME])) {
    $path = explode('/', $_SERVER['PATH_INFO']);

    echo 'Deleting ' . $path[count($path) - 1];
} else {
    die('Invalid security token');
}

index.php
PHP:
<?php

require './CSRFToken.php';

CSRFToken::setSecurityToken();
?>
<a href="query.php/users/delete/admin?<?= CSRFToken::TOKEN_NAME; ?>=<?= CSRFToken::getTokenValue(); ?>">Query string test</a>
 
Fakeheal каза:
pro12 каза:

Значи проблема е следния:

Формата ти се извиква преди кода за проверката, а във формата РЕГЕНЕРИРАШ нов токен.

След това проверяваш дали в токена от урла е същия с този, който вече си регенерирал (възможно е някога да съвпаднат, но шанса е минимален).

Затова и в като var_dump-ваш в (видях, че е в <head>), което е преди формата и все още не си го презаписал, затова все още съвпадат.
Благодаря!
 
Fakeheal каза:
pix3l каза:

PHP е почнал да изглежда като красив език :)

PS: това $path[count($path) - 1]; май ще изглежда по-красиво така echo end($path), но не знам.
Да, с end() изглежда по-яко. :D
 
Ето ти пример за работещ CSRF от вида, който искаш да направиш:
PHP:
session_start();

class CSRF
{
	private $token;
	
	public function set()
	{
		$this->token = md5(uniqid(rand(), true));
		$_SESSION["token"] = $this->token;
		return $this;
	}
	
    public function get() 
	{
        return $this->token;
    }  
	
	public function clean()
	{
		$_SESSION["token"] = false;
		$this->token = false;
		return $this;
	}
}

$csrf = new CSRF;

if (isset($_POST['submit']) and $_POST['token'] === $_SESSION['token']) {
    $searchdata['search'] = $_POST['search'];
    print "It is ok!";
}

$token = $csrf->set()->get();

?>
<form action="" method="post">
<input type="text" id="searchpole" size="15" maxlength="25" name="search">
<br/><input type="hidden" name="token" value="<?php  print $token; ?>">
<input type="submit" name="submit" id="btnsearch" value="search" />
</form>
 
pix3l каза:
@dakata__92 много съм любопитен да видя как ще парснеш страницата така, че да вземеш ключът на потребителя. Ето ти код, върху който да размишляваш:
...

В index.php визуализираш следното:

<a href="query.php/users/delete/admin?<?= CSRFToken::TOKEN_NAME; ?>=<?= CSRFToken::getTokenValue(); ?>">Query string test</a>

което се равнява съответно на примерно:

<a href="query.php/users/delete/admin?blablabla">Query string test</a>
визуализирано в браузъра. Използваш Curl отиваш на страницата и виждаш въпросният изграден път: query.php/users/delete/admin?blablabla в тага <a>. Взимаш html-а на страницата парсваш <a href="query.php/users/delete/admin?(.+?)" с регулярен израз и директно за следваща заявка (до момента бях само с една заявка за отваряне и обработване на HTML-a) ми е посещение на http://domein.com/query.php/users/delete/admin?blablabla като там се изпълнява каквото трябва на страницата.
 
Дай работещо решение с реален код, който хаква кода по-горе. :D

PS: Търсачките не трябва да се защитават от CSRF, понеже линковете, които генерират трябва да могат да се шерват... както е търсачката на Google например.
 
pix3l каза:
Дай работещо решение с реален код, който хаква кода по-горе. :D
Ехх... Така, като кажете "хаква" и ми иде да извадя коректора. :mrgreen:
Колко по- добре звучи "заобиколи" или "манипулира", не става само със сила...
Колега, Curl не работи ли с бисквитки според теб? :D :D :D
Ще ти дам константите CURLOPT_COOKIEFILE и CURLOPT_COOKIEJAR над които да размишляваш. С него се създават ботове, които почти изцяло манипулират сървъра така, че все едно се е логнал потребител през браузъра и си сърфира спокойно. Няма какво да влизам в обяснения. Да ми кажете, че е CAPTCHA да кажа, че е не става, но в тези случаи има варянти да се заобиколи. Да прости ботове няма да работят, въпреки, че е възможно и JS бот да изманипулира тази системата, но за това трябва да имам и работещо демо, че да го пробвам дали с него е възможно. Все пак това не е форум за да си мерим ботовете, има други такива за тази цел. :wink:
 
Не случайно използвах "хак", понеже ако излъжеш CSRF защитата, електронното банкиране ще спре да съществува. :D
CSRF не защитава само GET заявки... POST заявките са по-критичната част.

Не знам ти какви ботове пишеш на PHP, аз съм използвал само Java и Python за подобно нещо. PHP не ми се струва подходящ.
 
pix3l каза:
Не случайно използвах "хак", понеже ако излъжеш CSRF защитата, електронното банкиране ще спре да съществува. :D
CSRF не защитава само GET заявки... POST заявките са по-критичната част.

Не знам ти какви ботове пишеш на PHP, аз съм използвал само Java и Python за подобно нещо. PHP не ми се струва подходящ.

Така, сега ще го обясня по друг начин. Имаш сайт с ограничен достъп или по-просто казано с регистрация. Нямаш CAPTCHA за защита на формата, но имаш някакъв token на нея, като моят прост пример по-нагоре. Не говоря за username и password, приемаме, че ги имаме. Та с Curl подавам POST заявката и се логвам с въпросните имена и пароли в акаунта и правя каквото си искам, защото при логването се създава бисквитка, сздава се сесия и си браузвам из страниците от локалната машина все едно съм аз. Естествено ще парсвам съдържанието на формите и ще взимам съдържанието на въпросният токен по вътрешните страници и ще правя каквото имам като права с акаунта. Игри като ImperiaOnline v4 съм ги автоматизирал изцяло докато ги играех. Та как да избегнеш целият проблем? Ами слага се на логин формата CAPTCHA и вече бота не може да се логне. Щом не може да се логне, то той не може да изпълнява всичките тези манипулации свободно във вътрешните страници. CSRF. Това, което се опитвам да обясня, е че заради CAPTCHA валидирането на потребителите при логин, не е необходимо да се защитават вътрешните форми след логин, защото ботове от външни места не могат да се логнат. От там и следва, че дори да има манипулиране на някакво действие, то то е постигнато по различен начин. Пример ще дам с кликър програми, с които казваш на мишката къде по монитора, през колко време да цъка. Идеята е, че щом веднъш си валидирал потребителя, че е човек, а не бот от там нататъка се поемат нещата под друг знаменател. Прекалено е трудно бот да използва OCR софтуер и да разпознае изображението, след което да се логне с потребителското име и парола, за да върши автоматизирани действия. CSRF е комплексен проблем на приложението, структурата и начина на достъп. Колко от формите му са публични и колко от тях са със след регистрационен контрол. Важно е да защитите логването на бот в приложението Ви, не да защитавате всяка вътрешна форма с ненужни генерирания на ключове. Те няма да Ви спасят от спама и автоматизираните действия в системата, ако даден бот се е логнал. За пример ще дам форумите. Те са често под спам атаки, точно защото не спират логването на ботовете в системата им. Те са външни системи, с перфектни лични карти. Защитавате регистрационната си форма с капча но не и логин формата си??? И двете трябва да бъдат правилно защитени. Злосторника регистрира акаунт и подава име и парола на бота, който се логва и започва да спами. А ако ботът не може да се логне дори да има име и парола, то той става неизползваем. Да форумите имт други защити, но не това е идеята ми. Искам да обясня, че CSRF не е само външна намеса в приложението или някаква кражба, а и логически проблем ако са объркани различни нива за структура на апликацията. Ако ще генерирате token на всяка форма в приложението, лошо няма, но сам по себе си той не решава проблема, и за това постоянно давам библиотеката CURL, като пример. Много от приложенията, които ползвате ще изпищят, на много от известните сайтове за въпросната уязвимост, но те се защитават с добра логика и структурираност. Да в много и от тях мога да се логна с бот и да си автоматизирам разни дивотии. Да накарам бота примерно да се логне във фейсбук и да пише на всичките ми приятели или да поства, но това не означава, че ако направя една multitasking атака спамейки стените на приятелите ми няма да получа бан. Всичко е до компромисите, на база желан резултат. Ако искате си слагайте генерирани ключове в скрити полета на формите, аз предпочитам да сложа капча на логин формата и да защитавам вътрешните страници с други приоми, според приложението. :D :D :D
 
Впрочем, електронното банкиране не се защитава само от CSRF, а и от много други неща. Не преизопачавай нещата, едва ли ще се разграничава потребителя бот ли е или не само по това дали има или няма сетната бисквитка (за пример). Той ако ти има данните не му е нужно да използва външно място за точка на достъп, при положение че директно ще може да въведе данните си. Говорим за кода на автора и за това, че във вида в който е, както и моят примерен код, то сами по себе си не са никаква защита за по-културните "нападатели".
 
Виж, препоръчвам ти да прочетеш какво е CSRF и после пак да прегледаш кода, който постнах.
CSRF защитата по никакъв начин не те пази от спамери - факт! Пази те от нещо къде-къде по-сериозно. :D

PS: Използвах бисквитка за да не слагам боклуци в сесията, а не защото е някаква допълнителна превенция. Бисквитката така или иначе ще се изтрие в момента, в който юзърът затвори браузъра.
 
pix3l каза:
Виж, препоръчвам ти да прочетеш какво е CSRF и после пак да прегледаш кода, който постнах.
CSRF защитата по никакъв начин не те пази от спамери - факт! Пази те от нещо къде-къде по-сериозно. :D
Тц, тц, тццц... Какво ли се занимавам. Изпрати ми линк, да ми смениш паролата, че явно GET формите са на мода и всеки втори пише глупости, защото чете глупости. Направи ми хипервръзка харакири (http://web-tourist.net/forum/account.php?delete=true) или по-добре ти го направи (http://web-tourist.net/forum/account.php?new_password=abc123). CSRF в пълната си красота... Не ти трябва XSS, SQLi или BruteForce, просто му прати линк който да отвори без да иска и да си смени паролата с поставената от теб, след което да се логнеш и да му смениш профилната снимка с такава на магаре. Добре, че token ще защити потребителя от това сам да извърши по невнимание действие с акаунта си позволено от кода на системата, подтикнато от злонамерен потребител. :arrow: :idea:
 
Кой е казал, че CSRF работи само с GET форми?
Съгласи се, че да караш потребител да попълва CAPTCHA всеки път, когато има потенциален риск от CSRF е най-малоумното нещо, което някой може да направи... тук вече говорим за UX.
 
pix3l каза:
Кой е казал, че CSRF работи само с GET форми?
Съгласи се, че да караш потребител да попълва CAPTCHA всеки път, когато има потенциален риск от CSRF е най-малоумното нещо, което някой може да направи... тук вече говорим за UX.
Цял ден обяснявам за GET и POST заявки, а ти чак сега ми задаваш този реторичен за мен въпрос. Толкова си и прочел какво съм написал. CAPTCHA само на входната точка при логване в акаунта, за да знаеш, че пред системата ти се е легитимирал човек, не бот. От там нататък се процедира по съвсем друг начини, но честно да ти кажа, не си заслужава ти да обяснявам какви. За теб аз не знам какво е CSRF, а за мен, ти изобщо не приемаш чуждото мнение за вярно.
dakata__92 каза:
pix3l каза:
Виж, препоръчвам ти да прочетеш какво е CSRF и после пак да прегледаш кода, който постнах.
CSRF защитата по никакъв начин не те пази от спамери - факт! Пази те от нещо къде-къде по-сериозно. :D
Тц, тц, тццц... Какво ли се занимавам. Изпрати ми линк, да ми смениш паролата, че явно GET формите са на мода и всеки втори пише глупости, защото чете глупости. Направи ми хипервръзка харакири (http://web-tourist.net/forum/account.php?delete=true) или по-добре ти го направи (http://web-tourist.net/forum/account.php?new_password=abc123). CSRF в пълната си красота... Не ти трябва XSS, SQLi или BruteForce, просто му прати линк който да отвори без да иска и да си смени паролата с поставената от теб, след което да се логнеш и да му смениш профилната снимка с такава на магаре. Добре, че token ще защити потребителя от това сам да извърши по невнимание действие с акаунта си позволено от кода на системата, подтикнато от злонамерен потребител. :arrow: :idea:
Вместо постоянното генериране на някакви ключове колко по-елегантно решение е да попиташ потребителя за потвърждение на заявката? Все пак ако е толкова важна за него то не пречи да се съгласи и да я потвърди. Това е за вътрешни страници за някои важни функциии примерно свързани с акаунта или нещо около него. Така потребителя ще бъде информиран, че нещо става, и може да вземе мерки, а не просто да му се изпише - "Паролата Ви беше сменена успешно". Генерирането на ключове е решение за някои частни случаи и не мога да го успоря, но никой не пожела да се замисли дали са правилното решение на проблема в който са попаднали. После се започва с едно питане защо приложението зарежда бавно, ами тук малко, там малко усложнения и накрая пренаписване на ново...
 
Явно не усещаш сърказма... :D
Смятам да си дам почивка вече. Това не беше мнението ми, а техническо решение на определен проблем.
Ако мислиш, че ти казвам как да си пишеш приложенията - разбрал си ме погрешно.
 

Горе