Wywołanie skryptów z przeglądarki

0

Załóżmy, że w kodzie źródłowym mojej strony ktoś kumaty dojrzał:
<form action="doSomething.php" method="POST">
Czyli jak wpisze w przeglądarkę doSomething.php to coś się może stać… No właśnie, jak się przed tym zabezpieczyć? Pomysł mam taki jak poniżej. Tylko czy to jest sposób pewny i profesjonalny?

Sprawdzam po prostu, czy poprzez POST dostałem wszystko. A jak nie, to przekierowanie:

if (isset($_POST[$myVariable])) {   
     header("refresh:1; url=form.html");
 }
Else {
// 
}

Druga sprawa to skrypty załączane poprzez require_once("doSomethingAdditional.php");. O ile użytkownik ich nie widzi i nie wie o nich, to może odgadnąć ich nazwę. Czy jest sens i takie skrypty zabezpieczać?

4
MrocznyRycerz napisał(a):

Czyli jak wpisze w przeglądarkę doSomething.php to coś się może stać…

PHP nie znam, ale przeglądarka domyślnie wywołuje adresy przez GET, a nie POST. Ale zawsze można taki adres przepisać do postmana lub wywołać z CURLa

curl -X POST twoja-strona/doSomething.php
3

Ogólnie po co miałbyś się zabezpieczać? Jeśli ktoś chce wywołać stronę "w dedykowany sposób" to co za różnica czy zrobi to cURLem, przeglądarką, Postmanem czy ręcznie napisanym kodem. Jeśli chodzi Ci o to, że w formularzu masz np. 4 pola a ktoś wpisze wartość 3 no to po prostu waliduj przesłanie dane i wykonuj akcję tylko gdy są wszystkie i są poprawne. Możesz dodać jakieś uwierzytelnianie. Jeśli z kolei chcesz próbować w jakiś sposób zachować ciągłość działań możesz sprawdzić skąd przychodzi użytkownik poprzez $_SERVER['HTTP_REFERER'], możesz też np. zapisać coś w sesji.

0

Mam wydzielony skrypt do przepisywania danych z POST na zmienną (z walidacją oczywiście). Co się stanie, jak wywołamy ten skrypt "na dziko" pomimo tego, że z zewnątrz nie jest widoczny? User dostanie po twarzy takim błędem:

Notice: Undefined index: PhoneNumber in C:\xampp\doSomething.php on line 4

2

To znaczy, że nie walidujesz czy klucz PhoneNumber w POST jest. Względnie w innej części kodu.
Po prostu wykonuj jakąś akcję i dla takiego przypadku np.

if(isset('someNeededKey') && isset('otherImportnatKey')) {
    doTherightJob();
}
else {
    header("Location: http://www.myAwsomeSecureWebsite/home"); // przekieruj na jakąś stronę domową
    // http_response_code(400); // zamiast przekierowania zwróć 400 - bad request
}
2
MrocznyRycerz napisał(a):

Czyli jak wpisze w przeglądarkę doSomething.php to coś się może stać… No właśnie, jak się przed tym zabezpieczyć? Pomysł mam taki jak poniżej. Tylko czy to jest sposób pewny i profesjonalny?

Generalnie masz uzasadnioną obawę, poczytaj sobie o CSRF.
Natomiast Twój scenariusz jest bardzo typowy i prosty (koledzy już wyżej o tym pisali): jeśli dane formularza przyjmujesz metodą POST, a zależy Ci jedynie by zabezpieczyć się przed przypadkowym wejściem przeglądarką na adres i wykonaniem akcji, to tu nic nie musisz robić, bo przeglądarka domyślnie odpytuje serwer metodą GET.

Druga sprawa to skrypty załączane poprzez require_once("doSomethingAdditional.php");. O ile użytkownik ich nie widzi i nie wie o nich, to może odgadnąć ich nazwę. Czy jest sens i takie skrypty zabezpieczać?

Zależy co tam w tym kodzie masz. Jeśli same definicje struktur (co polecam), to niczym się martwić nie musisz, bo po wejściu na taki plik nic się nie wyświetli jeśli serwer jest poprawnie skonfigurowany (jeśli nie jest, to nic Cię nie uratuje). Natomiast jeśli masz tam odpalany kod w globalu, to lepiej zabezpieczyć się na taką okoliczność. W starych CMS-ach robiło się to jednolinijkowcem sprawdzającym istnienie stałej: defined('IN_APP') or die('Access denied'); a w pliku nadrzędnym przed includem: const IN_APP = true;.

0

Albo wyobraźcie sobie, że ktoś odgaduje nazwę skryptu, który ogarnia połączenie z bazą danych. Komunikat z błędem poda na tacy kilka ciekawych informacji. Błędy można oczywiście wyłączyć (jeżeli się nie mylę, to error_reporting(0);), ale...

3

Widzę masz złe podejście. W momencie ustawienia php.ini na produkcji masz zrobić tak, żeby użytkownik dostał błąd typu 403, 400 czy 500 itd. Będzie to błąd HTTP który nie zdradzi szczegółów czy haseł.
Poza tym, to tu nie ma co odgadywać. URLe są zapisane w kodzie HTML i może Cię zaskoczę ale są odwiedzane przez mnóstwo robotów, skryptów szukających luk bezpieczeństwa itd. Jeśli ściągniesz sobie paczki z PHP dla Windows to nie znajdziesz w nich pliku php.ini tylko php.ini.production i php.ini.development i w zależności od tego co robisz dopiero używasz sobie właściwego pliku przez np. zmianę nazwy. Proponuję edukacyjnie porównać ustawienia i poczytać w dokumentacji które co robi.

0

Czyli po stronie skryptu mogę tylko sprawdzić, czy POSTem dostałem co chciałem. Reszta to konfiguracja serwera tj. php.in?

5
Szado napisał(a):

Zależy co tam w tym kodzie masz. Jeśli same definicje struktur (co polecam), to niczym się martwić nie musisz, bo po wejściu na taki plik nic się nie wyświetli jeśli serwer jest poprawnie skonfigurowany (jeśli nie jest, to nic Cię nie uratuje). Natomiast jeśli masz tam odpalany kod w globalu, to lepiej zabezpieczyć się na taką okoliczność. W starych CMS-ach robiło się to jednolinijkowcem sprawdzającym istnienie stałej: defined('IN_APP') or die('Access denied'); a w pliku nadrzędnym przed includem: const IN_APP = true;.

Za "moich czasów" (czyli "ach, ta dzisiejsza młodzież") to takie includowane / requirowane fragmenty PHP się po prostu umieszczało poza przestrzenią dostępną dla serwera http (o piętro krótsza ścieżka), w lżejszym przypadku w podkatalkogu, dla którego plik .htacces blokował dostęp sieciowy

0

W kwestii pokazania szczegółów kodu czy haseł tak. Chociaż widziałem cuda w kodzie gdzie ktoś jawnie pokazywał wrażliwe dane w ramach błędu, zapewne pozostawiając coś po procesie tworzenia kodu, a co nie powinno przejść review.

4

@MrocznyRycerz Powinieneś to zrobić wielopoziomowo:

  • Po pierwsze, spodziewasz się że w Twoim $_POST jest konkretny zestaw kluczy, i jak widać oprogramowanie całkowicie nie jest przygotowane na obsłużenie tego że tych kluczy nie ma, że jest tylko ich pozdbiór, albo jest request inną metodą. Najpierw, powinieneś zacząć od tego żeby Twoje oprogramowanie umiało ohandlować taki przypadek: czyli np odpowiedzieć odpowiednim statusem błędu HTTP (np 400) lub 405, wysyłając odpowiedni header (np z przekierowaniem) albo wysyłając odpowiednie body (z komunikatem o brakujących polach). Błędy które widzisz, wynikają tylko i wyłacznie z tego ze nie obsłużyłeś jakiegoś przypadku. To powinieneś zrobić ASAP.
  • Po drugie, jeśli Twój widok w HTML strzela pod Twój plik PHP, to znaczy że ten plik PHP jest cześcią Twojego interfejsu, i nie ważne jak bardzo byś chciał go "zabezpieczyć", to do niego musi się być dać dostać przez HTTP, bo jak inaczej miałby działać Twój formularz. A skoro można się dostać przez HTTP, to nie za bardzo jesteś w stanie wymusić czy z przeglądarki, czy z formularza, czy z curla czy postmana czy jakiegokolwiek innego klienta HTTP ktoś trzela, także takie zabezpieczenia raczej nie mają sensu.
  • Po trzecie, u Ciebie jest wystawionych kilka plików .php, ale tylko pod jeden chcesz strzelać. Pisałem już w poprzednim temacie: Walidacja imienia i nazwiska dla pola tekstowego że to i tak trochę głupio robisz żę dzielisz to na pliki, bo w Twoim przypadku i tak wystarczyłby jeden. Ale jeśli koniecznie chcesz mieć kilka to powinieneś zrobić dwie rzeczy:
    • Po pierwsze, w tych plikach "ukrytych" nie powinieneś mieć kodu, a jedynie funkcje, czyli nawet jak ktoś odpali taki skrypt, to jedyne co zrobi to funkcje zostaną zadeklarowane, ale żaden kod się nie odpali.
    • Po drugie, dlaczego masz ustawiony server HTTP tak że odpala Ci każdy możliwy plik? powinieneś mieć whitelistę, że tylko plik dajmy index.php jest odpalany, a wszystkie inne pliki .php są niewidoczne, i mają zwracać 404, tak jakby ich nie było.

Co do odpowiedzi z CSRF, nie sądzę żeby to był dobry pomysł, bo to wymaga persystencji i pamiętania klucza, a z tym widać ze user ma problem. Raczej zacząłbym od dwóch punktów wyżej.

3

Jak ktoś kumaty z dostateczną ilością wolnego czasu zobaczy to w kodzie twojej strony, to się przed tym nie zabezpieczysz . <- ta kropka jest wyboldowana, ale nie widać.

Podstawowa zasada pisania czegokolwiek po stronie serwera, to nie wolno ufać, że cokolwiek dostajesz od klienta/przeglądarki pochodzi z uprawnionej aplikacji/strony internetowej.

Jeżeli masz skrypt, który nie powinien być wywoływany z zewnątrz, to nie powinien być widoczny z zewnątrz. Udostępnianie przypadkowych endpointów to taka trochę masakra (nic osobistego, zwyczajne stwierdzenie faktu).

Jeżeli masz jakiś endpoint, który ma być widoczny z zewnątrz, jest widoczny z zewnątrz i powinien wymagać uwierzytelniania, bo robi coś ważnego (dajmy na to powoduje wykonanie przelewu...) to powinien zostać jakoś zabezpieczony przed atakami CSRF. Czym jest CSRF? Jesteś zalogowany do swojego banku i bank ci ufa. Na innej stronie (nawet nie lewej, wystarczy, że jest podatna na ataki XSS lub podobne) ktoś wstawia kawałek kodu, który wywołuje:

POST https://twojbank.com
{
  "targetAccount":"numer konta atakującego",
  "amount":""
}

Ten kod wykona się w innej zakładce, ale w przeglądarce, do której jesteś zalogowany, zostaną więc dołożone wszystkie cookies, które powinny pójść pod ten adres.
Żeby temu zapobiec możesz wprowadzić jakiś tam typ tokenów anty-CSRF, czyli masz zabezpieczony endpoint, z którego pobierasz token, oraz sprawdzasz poprawność tego tokenu (tutaj nie zgadzam się z @Riddle, że trzeba ten token pamiętać, wystarczy, że jest się w stanie potwierdzić jego autentyczność).

Ps.
Ogólnie, to jestem trochę przerażony powszechną wśród programistów ignorancją w zakresie bezpieczeństwa. Szczególnie, że obecnie jakieś 99.999% aplikacji z tej łączności przez internet korzysta. I nie jest to przytyk pod kątem OP'a, bo ostatnio kandydat na stanowisko architekta nie był w stanie opowiedzieć czym jest XSS czy CSRF. Znaczy powiedział, że "są skanery, które sprawdzają kod pod kątem podatności na ataki".

0

Pobawiłem się trochę Curlem i zauważyłem 2 rzeczy:

  • jeżeli odgadnę nazwy zmiennych w polach formularza (a to nie problem), które wysyłam POSTem, to za pomocą Curl (parametr -X POST) mój skrypt można uruchomić (spoko, htmlspecialchars na ratunek)
  • jeżeli wykonam komendę curl -v localhost/walidacja/sendForm.php to dostaję taką odpowiedź, a tymczasem powinno mnie przekierować na form.html z kodem 30x?
    screenshot-20221224224856.png
1

Pokaż kod który przekierowuje. Ustawisz tam jakiś status 30X ?

1
MrocznyRycerz napisał(a):

Pobawiłem się trochę Curlem i zauważyłem 2 rzeczy:

  • jeżeli odgadnę nazwy zmiennych w polach formularza (a to nie problem), które wysyłam POSTem, to za pomocą Curl (parametr -X POST) mój skrypt można uruchomić (spoko, htmlspecialchars na ratunek)

Nie chwytam zwiazku logicznego jednego z drugim, pachnie jak magiczne uwielbienie htmlspecialchars (co jest dośc masowe)

1

Na temat odpowiadaj w postach.

header("refresh:1; url=form.html"); —

https://www.php.net/manual/en/function.header.php
Location żeby mieć 302. Lub podajesz response code jako parametr i taki trafi do przeglądarki.

2

W sumie nic dziwnego, że poniższe daje kod 200:
header("refresh:1; url=form.html");
a to już 302:
header("Location: http://localhost/walidacja/form.html");

3

Ogólnie, to to pytanie jest trochę odwrotne.

Jak masz serwer, z jakimiś plikami i skryptami, to one nie są domyślnie "otwarte", one są domyślnie zamknięte, do takiego servera nie może nic wejść samo z siebie, pewnie nawet żaden port nie jest otwarty. Tylko dopiero dodając jakieś głupie reguły, typu "jeśli ktoś strzeli pod .php to uruchom go jako CGI" to wtedy są takie cyrki, że można strzelić pod arbitralny plik i go wykonać.

Więc schemat wygląda tak:

  1. Miej bezpieczny server
  2. Postaw na nim server
  3. skonfiguruj server tak żeby pozwalał odpalać arbitralne pliki .php
  4. załóż post na forum z pytaniem "jak zabezpieczyć skrypty".

Oczywiście odpowiedź nasuwa się sama - wywal zasadę w serverze (tam apache czy nginx) która pozwala Ci odpalać randomowe pliku, i zmiast tego ustaw jakiś entry point, że tylko strzelajac po index.php się go wykonuje, a wszystkie inne są traktowane jako 404.

1 użytkowników online, w tym zalogowanych: 0, gości: 1