Jak zmusić użytkowników do uzyskiwania dostępu do mojej strony przez HTTPS zamiast HTTP?

137

Mam tylko jedną stronę, którą chcę wymusić, aby była dostępna jako strona HTTPS (PHP na Apache). Jak to zrobić, aby cały katalog nie wymagał protokołu HTTPS? Lub, jeśli wysyłasz formularz na stronę HTTPS ze strony HTTP, czy wysyła go przez HTTPS zamiast HTTP?

Oto mój przykład:

http://www.example.com/some-page.php

Chcę, aby był dostępny tylko przez:

https://www.example.com/some-page.php

Jasne, mogę umieścić wszystkie linki do tej strony wskazujące na wersję HTTPS, ale to nie powstrzymuje jakiegoś głupca przed celowym dostępem do niej przez HTTP ...

Jedną z rzeczy, o których pomyślałem, było umieszczenie przekierowania w nagłówku pliku PHP, aby sprawdzić, czy mają dostęp do wersji HTTPS:

if($_SERVER["SCRIPT_URI"] == "http://www.example.com/some-page.php"){
  header('Location: https://www.example.com/some-page.php');
}

Ale to nie może być właściwy sposób, prawda?

Wiki
źródło
3
Czy jest jakiś powód, dla którego nie potrzebujesz tylko SSL dla wszystkich stron?
Tahaan

Odpowiedzi:

177

Sposób, w jaki zrobiłem to wcześniej, jest zasadniczo taki, jak napisałeś, ale nie ma żadnych wartości zakodowanych na stałe:

if ($ _ SERVER ["HTTPS"]! = "on")
{
    nagłówek ("Lokalizacja: https: //". $ _SERVER ["HTTP_HOST"]. $ _SERVER ["REQUEST_URI"]);
    wyjście();
}
Adam Rosenfield
źródło
15
Zapomniałeś wywołać metodę exit (), aby upewnić się, że skrypt zakończy działanie po przekierowaniu. Zwykle pakuję to w funkcję o nazwie requireSSL (). Mogę to nazwać u góry każdej strony, którą chcę, aby była zaszyfrowana.
Jesse Weigert
31
Zmień opcję if, (empty($_SERVER["HTTPS"]) || $_SERVER["HTTPS"] !== "on")aby naprawić powiadomienia PHP.
dave1010
Czy $_SERVER[]zmienne nie są zmienne / podatne na interwencję użytkowników?
Arian Faurtosh
Źródło @ArianFaurtosh?
John
3
@ArianFaurtosh niektóre są wyodrębniane z nagłówków klienta, na przykład HTTP_X_FORWARDED, i można nimi manipulować, ale inne lubią HTTPSlub SERVER_PORTsą ustawiane bezpośrednio z serwera internetowego i zwykle powinny być bezpieczne.
Mahn
46

Możesz to zrobić za pomocą dyrektywy i mod_rewrite na Apache:

<Location /buyCrap.php>
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
</Location>

Jeśli chcesz, możesz z czasem ulepszyć Lokalizację, używając wyrażeń regularnych .

thebigjc
źródło
6
Gdzie byś to położył? plik .htaccess?
1
Czy REQUEST_URI nie zawiera „ciągu zapytania” (np.? Page = 1 & id = 41 itd.)? Tak mówi dokumentacja apache ... Więc jeśli spróbuję wejść na stronę site.com/index.php?page=1&id=12, zostanę przekierowany site.com/index.php
Rolf
2
Z dokumentacji Apache: REQUEST_URI Składnik ścieżki żądanego identyfikatora URI, na przykład „/index.html”. To w szczególności wyklucza ciąg zapytania, który jest dostępny jako własna zmienna o nazwie QUERY_STRING. Musisz więc dodać QUERY_STRING po REQUEST_URI
Rolf,
2
Następnie musisz dodać flage [R], aby przekierować
Rolf,
40

Należy zmusić klienta do żądania protokołu HTTPS zawsze z nagłówkami HTTP Strict Transport Security (HSTS):

// Use HTTP Strict Transport Security to force client to use secure connections only
$use_sts = true;

// iis sets HTTPS to 'off' for non-SSL requests
if ($use_sts && isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') {
    header('Strict-Transport-Security: max-age=31536000');
} elseif ($use_sts) {
    header('Location: https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'], true, 301);
    // we are in cleartext at the moment, prevent further execution and output
    die();
}

Należy pamiętać, że HSTS jest obsługiwany w większości nowoczesnych przeglądarek, ale nie jest uniwersalny. W związku z tym powyższa logika ręcznie przekierowuje użytkownika niezależnie od obsługi, jeśli kończy się na HTTP, a następnie ustawia nagłówek HSTS tak, aby dalsze żądania klientów były przekierowywane przez przeglądarkę, jeśli to możliwe.

Jacob Swartwood
źródło
Dziwię się, że żadna z pozostałych odpowiedzi nie zawiera tego nagłówka, jest to dość ważne ... jakaś pułapka na połączenie tych dwóch?
keisar
Nie ... i tak możesz zdecydowanie ustawić ten nagłówek, jeśli chcesz zawsze korzystać z HTTPS. Ustawienie go przed i po wykonaniu przekierowania jest po prostu zbędne. Poprawiłem również powyższą odpowiedź, aby dokładniej wyjaśnić zgodność.
Jacob Swartwood
1
(Redagowanie oryginalnego komentarza) Nie zauważyłem szczególnego wymogu „tylko jednej strony”. HSTS będzie dotyczył wszystkich stron; moja odpowiedź jest technicznie niepoprawna.
Jacob Swartwood
4
FYI, standardowa sekcja 7.2 RFC 6797 mówi: „Host HSTS NIE MOŻE zawierać pola nagłówka STS w odpowiedziach HTTP przekazywanych za pośrednictwem niezabezpieczonego transportu”. więc nie ma potrzeby wysyłania go, gdy żądanie jest zwykłym http, należy je zignorować, jeśli przeglądarka jest zgodna ze standardem.
Frank Forte
1
@mark jeśli istnieje MITM, a Twoja witryna nie ma tego nagłówka, możesz zezwolić złożonej sesji na przejście do http, a ludzie wprowadzą swoje dane i prawdopodobnie nie zauważą, a przekierowanie nie pomoże (człowiek w środek połączy się ze stroną przez https i przedstawi ją jako http). Użycie tego nagłówka wymusi HTTPS po stronie klienta. Jest to szczególnie ważne ze względu na niedawną lukę w zabezpieczeniach Wi-Fi WPA2.
Rudiger
19

Właśnie utworzyłem plik .htaccess i dodałem:

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

Prosty !

MatHatrik
źródło
9
// Force HTTPS for security
if($_SERVER["HTTPS"] != "on") {
    $pageURL = "Location: https://";
    if ($_SERVER["SERVER_PORT"] != "80") {
        $pageURL .= $_SERVER["SERVER_NAME"] . ":" . $_SERVER["SERVER_PORT"] . $_SERVER["REQUEST_URI"];
    } else {
        $pageURL .= $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"];
    }
    header($pageURL);
}
Jeff
źródło
8

Musiałem zrobić coś takiego podczas pracy za modułem równoważenia obciążenia. Wskazówka dotycząca kapelusza https://stackoverflow.com/a/16076965/766172

function isSecure() {
    return (
        (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
     || $_SERVER['SERVER_PORT'] == 443
     || (
            (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
         || (!empty($_SERVER['HTTP_X_FORWARDED_SSL'])   && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on')
        )
    );
}

function requireHTTPS() {
    if (!isSecure()) {
        header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], TRUE, 301);
        exit;
    }
}
syvex
źródło
6

Sposób PHP:

$is_https=false;
if (isset($_SERVER['HTTPS'])) $is_https=$_SERVER['HTTPS'];
if ($is_https !== "on")
{
    header("Location: https://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
    exit(1);
}

Sposób mod_rewrite Apache:

RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Sójka
źródło
Wolę sposób PHP, ponieważ są przypadki, w których chciałbym zastąpić wymaganie SSL. Na przykład lokalny dostęp do interfejsów API i rzeczy, takich jak automatyczna konfiguracja Mozilla Thunderbird itp.
Jay
Metoda PHP działa idealnie u mnie, gorąco polecam.
Moxet
5

http://www.besthostratings.com/articles/force-ssl-htaccess.html

Czasami może zajść potrzeba upewnienia się, że użytkownik przegląda witrynę, korzystając z bezpiecznego połączenia. Łatwy sposób na zawsze przekierowanie użytkownika do bezpiecznego połączenia (https: //) można osiągnąć za pomocą pliku .htaccess zawierającego następujące wiersze:

RewriteEngine On 
RewriteCond %{SERVER_PORT} 80 
RewriteRule ^(.*)$ https://www.example.com/$1 [R,L]

Należy pamiętać, że plik .htaccess powinien znajdować się w głównym folderze witryny internetowej.

Jeśli chcesz wymusić HTTPS dla określonego folderu, możesz użyć:

RewriteEngine On 
RewriteCond %{SERVER_PORT} 80 
RewriteCond %{REQUEST_URI} somefolder 
RewriteRule ^(.*)$ https://www.domain.com/somefolder/$1 [R,L]

Plik .htaccess należy umieścić w folderze, w którym chcesz wymusić HTTPS.

itsazzad
źródło
5

Ok .. Teraz jest na ten temat mnóstwo rzeczy, ale nikt tak naprawdę nie odpowiada na pytanie „Bezpieczne”. Dla mnie używanie czegoś, co jest niepewne, jest absurdalne.

Chyba że użyjesz go jako przynęty.

Propagacja $ _SERVER może zostać zmieniona na życzenie kogoś, kto wie jak.

Również, jak stwierdzili Sazzad Tushar Khan i thebigjc, możesz również użyć httaccess, aby to zrobić, a tutaj jest wiele odpowiedzi, które go zawierają.

Poprostu dodaj:

RewriteEngine On
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://example.com/$1 [R,L]

do końca tego, co masz w swoim .httaccess i to wszystko.

Wciąż nie jesteśmy tak bezpieczni, jak to tylko możliwe dzięki tym 2 narzędziom.

Reszta jest prosta. Jeśli brakuje atrybutów, tj ...

if(empty($_SERVER["HTTPS"])){ // SOMETHING IS FISHY
}

if(strstr($_SERVER['HTTP_HOST'],"mywebsite.com") === FALSE){// Something is FISHY
}


Powiedz również, że zaktualizowałeś swój plik httaccess i sprawdziłeś:

if($_SERVER["HTTPS"] !== "on"){// Something is fishy
}

Jest o wiele więcej zmiennych, które możesz sprawdzić, np.

HOST_URI (Jeśli istnieją statyczne atrybuty tego do sprawdzenia)

HTTP_USER_AGENT (Różne wartości w tej samej sesji)

Więc wszystko, co mówię, to nie poprzestawać na jednym lub drugim, gdy odpowiedź leży w kombinacji.

Aby uzyskać więcej informacji na temat przepisywania httaccess, zobacz dokumentację-> http://httpd.apache.org/docs/2.0/misc/rewriteguide.html

Some Stacks here -> Force SSL / https using .htaccess and mod_rewrite
and
Getting the full URL of the bieżąca strona (PHP),
aby nazwać parę.

JSG
źródło
2
ale teraz pojawia się następujący błąd: ERR_TOO_MANY_REDIRECTS. Jak to naprawić?
Pathros
3

Użyj, $_SERVER['HTTPS']aby sprawdzić, czy jest to SSL , i przekieruj we właściwe miejsce, jeśli nie.

I pamiętaj, strona, na której jest wyświetlany formularz, nie musi być zasilana przez HTTPS, to adres zwrotny postu, który najbardziej tego potrzebuje.

Edycja : tak, jak wskazano poniżej, najlepiej jest mieć cały proces w HTTPS. To o wiele bardziej uspokajające - zwracałem uwagę, że post jest najbardziej krytyczną częścią. Musisz również uważać, aby wszystkie pliki cookie były ustawione jako bezpieczne, więc będą wysyłane tylko przez SSL. Rozwiązanie mod_rewrite jest również bardzo sprytne, użyłem go do zabezpieczenia wielu aplikacji na mojej własnej stronie internetowej.

DGM
źródło
To prawda, że ​​sam formularz nie musi być https, chociaż jest to dobry pomysł dla większości osób, które tego nie wiedzą. Jeśli mają zamiar przesłać formularz i zauważą, że nie ma tam ikony kłódki, mogą błędnie założyć, że formularz jest niezabezpieczony.
Graeme Perrow
1
@Graeme: dodatkowo nikt nie może być pewien, że formularz kiedykolwiek zostanie wysłany przez https. Cały formularz (wyświetlany za pośrednictwem protokołu http) może być fałszywy, przesyłany do nieznanej witryny zawierającej zwykły tekst http. Https to nie tylko szyfrowanie, ale także uwierzytelnianie serwera.
Olaf Kock,
3

zastosowanie htaccess:

#if domain has www. and not https://
  RewriteCond %{HTTPS} =off [NC]
  RewriteCond %{HTTP_HOST} ^(?i:www+\.+[^.]+\.+[^.]+)$
  RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [QSA,L,R=307]

#if domain has not www.
  RewriteCond %{HTTP_HOST} ^([^.]+\.+[^.]+)$
  RewriteRule ^(.*)$ https://www.%{HTTP_HOST}%{REQUEST_URI} [QSA,L,R=307]
siavash bagheri
źródło
1

Nie mieszaj HTTP i HTTPS na tej samej stronie. Jeśli masz stronę formularza, która jest obsługiwana przez HTTP, będę się denerwować przesyłaniem danych - nie mogę sprawdzić, czy przesyłanie przechodzi przez HTTPS lub HTTP bez wykonania widoku źródła i szukania go.

Dostarczanie formularza przez HTTPS wraz z linkiem do przesyłania nie jest tak dużą zmianą na korzyść.

JBB
źródło
1

Jeśli używasz Apache lub czegoś takiego jak LiteSpeed, który obsługuje pliki .htaccess, możesz wykonać następujące czynności. Jeśli nie masz jeszcze pliku .htaccess, powinieneś utworzyć nowy plik .htaccess w swoim katalogu głównym (zwykle tam, gdzie znajduje się plik index.php). Teraz dodaj te linie jako pierwsze reguły przepisywania w twoim .htaccess:

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Potrzebujesz instrukcji "RewriteEngine On" tylko raz w swoim .htaccess dla wszystkich reguł przepisywania, więc jeśli już ją masz, po prostu skopiuj drugą i trzecią linię.

Mam nadzieję, że to pomoże.

Antonio
źródło
To właśnie zadziałało w moim przypadku i czego używam we własnych skryptach na frameworku podobnym do Zend 2 - nie rozumiem negatywnej opinii.
Antonio
Może dostałeś negatywną opinię, ponieważ odpowiedź jest praktycznie taka sama jak ta z @MatHatrik, która została opublikowana ponad rok wcześniej?
Wilt
1

Korzystanie z tego nie wystarczy:

if($_SERVER["HTTPS"] != "on")
{
    header("Location: https://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]);
    exit();
}

Jeśli masz jakiekolwiek treści http (np. Zewnętrzne źródło obrazu http), przeglądarka wykryje potencjalne zagrożenie. Więc upewnij się, że wszystkie ref i src w kodzie to https

webmaster bourax
źródło
1

Przeszedłem przez wiele rozwiązań sprawdzających stan $ _SERVER [HTTPS], ale wydaje mi się, że nie jest on niezawodny, ponieważ czasami nie włącza się, wyłącza itp., Powodując przekierowanie skryptu do wewnętrznej pętli.

Oto najbardziej niezawodne rozwiązanie, jeśli Twój serwer obsługuje usługę $ _SERVER [SCRIPT_URI]

if (stripos(substr($_SERVER[SCRIPT_URI], 0, 5), "https") === false) {
    header("location:https://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]");
    echo "<meta http-equiv='refresh' content='0; url=https://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]'>";
    exit;
}

Zwróć uwagę, że w zależności od twojej instalacji, twój serwer może nie obsługiwać $ _SERVER [SCRIPT_URI], ale jeśli tak, to jest to lepszy skrypt.

Możesz sprawdzić tutaj: Dlaczego niektóre instalacje PHP mają $ _SERVER ['SCRIPT_URI'], a inne nie

Tarik
źródło
1

Dla osób korzystających z usług IIS dodanie tego wiersza w pliku web.config pomoże:

<httpProtocol>
    <customHeaders>
        <add name="Strict-Transport-Security" value="max-age=31536000"/>
    </customHeaders>
</httpProtocol>
<rewrite>
    <rules>
        <rule name="HTTP to HTTPS redirect" stopProcessing="true">
              <match url="(.*)" />
              <conditions>
                 <add input="{HTTPS}" pattern="off" ignoreCase="true" />
              </conditions>
              <action type="Redirect" redirectType="Found" url="https://{HTTP_HOST}/{R:1}" />
         </rule>
    </rules>
</rewrite>

Pełny przykładowy plik

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <httpProtocol>
            <customHeaders>
                <add name="Strict-Transport-Security" value="max-age=31536000"/>
             </customHeaders>
        </httpProtocol>
        <rewrite>
            <rules>
                <rule name="HTTP to HTTPS redirect" stopProcessing="true">
                      <match url="(.*)" />
                      <conditions>
                         <add input="{HTTPS}" pattern="off" ignoreCase="true" />
                      </conditions>
                      <action type="Redirect" redirectType="Found" url="https://{HTTP_HOST}/{R:1}" />
                 </rule>
            </rules>
       </rewrite>
   </system.webServer>
</configuration>
Tschallacka
źródło
Pytanie dotyczy konkretnie Apache, a nie IIS.
Quentin,
1
A) to nie otagowany apache. Apache znajduje się w cudzysłowie. B) jest to ogólne pytanie, które nie ma nic wspólnego z apache w ogóle. Ma zastosowanie do wielu serwerów WWW obsługujących php
Tschallacka
0

Nie powinieneś ze względów bezpieczeństwa. Zwłaszcza jeśli w grę wchodzą pliki cookie. Powoduje to, że jesteś otwarty na ataki typu „powtórka” oparte na plikach cookie.

Tak czy inaczej, powinieneś użyć reguł kontroli Apache, aby go dostroić.

Następnie możesz przetestować, czy protokół HTTPS jest włączony i w razie potrzeby przekierować.

Powinieneś przekierowywać do strony płatności tylko za pomocą FORMULARZA POSTU (bez POST), a dostęp do strony bez POST powinien być kierowany z powrotem na inne strony. (To złapie ludzi po prostu skaczących.)

http://joseph.randomnetworks.com/archives/2004/07/22/redirect-to-ssl-using-apaches-htaccess/

To dobre miejsce na rozpoczęcie, przepraszam, że nie zapewniam więcej. Ale naprawdę powinieneś wszystko przepchnąć przez SSL.

To nadmierna ochrona, ale przynajmniej masz mniej zmartwień.

Kent Fredric
źródło
0

może ten może pomóc, ty, tak zrobiłem dla mojej strony, działa jak marzenie:

$protocol = $_SERVER["HTTP_CF_VISITOR"];

if (!strstr($protocol, 'https')){
    header("Location: https://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]);
    exit();
}
Héouais Mongars
źródło
0

Jeśli chcesz użyć do tego PHP, ten sposób zadziałał dla mnie naprawdę dobrze:


<?php

if(!isset($_SERVER["HTTPS"]) || $_SERVER["HTTPS"] != "on") {
    header("Location: https://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"], true, 301);
    //Prevent the rest of the script from executing.
    exit;
}
?>

Sprawdza zmienną HTTPS w superglobalnej tablicy $ _SERVER, aby zobaczyć, czy jest równa „on”. Jeśli zmienna nie jest równa on.

squiremaldoon
źródło
-1

Użyłem tego skryptu i działa dobrze na stronie.

if(empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == "off"){
    $redirect = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
    enter code hereheader('HTTP/1.1 301 Moved Permanently');
    header('Location: ' . $redirect);
    exit();
}
Esteban Gallego
źródło
-2
<?php 
// Require https
if ($_SERVER['HTTPS'] != "on") {
    $url = "https://". $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
    header("Location: $url");
    exit;
}
?>

Tak proste.

Zaklęcie
źródło