Oddzwanianie na Facebooku dołącza „# _ = _” do zwracanego adresu URL

479

Oddzwanianie na Facebooku zaczęło dodawać #_=_podkreślenie skrótu do zwrotnego adresu URL

Czy ktoś wie dlaczego? Jakie jest rozwiązanie?

zing ming
źródło
35
Wiesz, jak Facebook dołącza te postacie? Facebook przekierowuje do mojego modułu obsługi, gdzie następnie obsługuję przekierowanie na adres zwrotny, ale znaki są nadal dołączane do adresu URL.
Ben Foster
4
@BenFoster Myślę, że przekonasz się, że jeśli użyjesz Fiddlera lub podobnego, gdy FB przywróci do twojego programu obsługi, #_=_jest na miejscu, a nawet jeśli robisz to w miejscu, Response.Redirectw którym naprawdę chcesz to zrobić, przeglądarka zachowuje skrót , dlatego działają tylko sugerowane poniżej obejścia po stronie klienta .
AakashM
17
2017, co do cholery
Ejonas GGgg
6
Maj 2017, wciąż ....
Mirko
5
Marzec 2018 .. wciąż się dzieje
John Rogerson

Odpowiedzi:

239

za pośrednictwem aktualizacji platformy Facebook :

Zmiana zachowania przekierowania sesji

W tym tygodniu zaczęliśmy dodawać fragment # ____ = ____ do redirect_uri, kiedy to pole jest puste. Upewnij się, że aplikacja obsługuje ten problem.

Aby temu zapobiec, ustaw redirect_uri w żądaniu adresu URL logowania w następujący sposób: (używając Facebooka php-sdk)

$facebook->getLoginUrl(array('redirect_uri' => $_SERVER['SCRIPT_URI'],'scope' => 'user_about_me'));

AKTUALIZACJA

Powyżej jest dokładnie tak, jak mówi dokumentacja , aby to naprawić. Udokumentowane rozwiązanie Facebooka nie działa. Rozważ zostaw komentarz na blogu na temat aktualizacji platformy Facebook i śledź ten błąd, aby uzyskać lepszą odpowiedź. Do tego czasu dodaj następujący tag do tagu head, aby rozwiązać ten problem:

<script type="text/javascript">
    if (window.location.hash && window.location.hash == '#_=_') {
        window.location.hash = '';
    }
</script>

Lub bardziej szczegółowa alternatywa (dzięki niftylettuce ):

<script type="text/javascript">
    if (window.location.hash && window.location.hash == '#_=_') {
        if (window.history && history.pushState) {
            window.history.pushState("", document.title, window.location.pathname);
        } else {
            // Prevent scrolling by storing the page's current scroll offset
            var scroll = {
                top: document.body.scrollTop,
                left: document.body.scrollLeft
            };
            window.location.hash = '';
            // Restore the scroll offset, should be flicker free
            document.body.scrollTop = scroll.top;
            document.body.scrollLeft = scroll.left;
        }
    }
</script>
Ryan
źródło
10
które pole jest puste? To jest bardzo tajemnicze
user210504
11
@Ryan Update prawie dla mnie działa, wciąż mam skrót (/ #) na końcu. Niezadowolony z FB.
LenPopLilly,
2
Nadal dostaję także / #. ktoś tu aktualizuje? usunąć #
Tian Loon,
6
To rozwiązanie usunie skrót: <script type = "text / javascript"> var idx = window.location.toString (). IndexOf ("# _ = _"); if (idx> 0) {window.location = window.location.toString (). substring (0, idx); } </script> Upewnij się, że jest to pierwszy znacznik w elemencie head.
Gorgi Rankovski
1
Zauważyłem twój raport o błędzie tutaj: developers.facebook.com/bugs/1424488987806270 Próbowałem też swoich sił w wyszukiwaniu „fragmentu request_uri” z tymi samymi wynikami.
Ryan
115

TL; DR

if (window.location.hash === "#_=_"){
    history.replaceState 
        ? history.replaceState(null, null, window.location.href.split("#")[0])
        : window.location.hash = "";
}

Pełna wersja z instrukcjami krok po kroku

// Test for the ugliness.
if (window.location.hash === "#_=_"){

    // Check if the browser supports history.replaceState.
    if (history.replaceState) {

        // Keep the exact URL up to the hash.
        var cleanHref = window.location.href.split("#")[0];

        // Replace the URL in the address bar without messing with the back button.
        history.replaceState(null, null, cleanHref);

    } else {

        // Well, you're on an old browser, we can get rid of the _=_ but not the #.
        window.location.hash = "";

    }

}

Krok po kroku:

  1. Wchodzimy do bloku kodu tylko wtedy, gdy fragmentjest #_=_.
  2. Sprawdź, czy przeglądarka obsługuje window.replaceStatemetodę HTML5 .
    1. Wyczyść adres URL, dzieląc #i biorąc tylko pierwszą część.
    2. Powiedz, historyaby zastąpić bieżący stan strony czystym adresem URL. Zmienia to bieżący wpis historii zamiast tworzyć nowy. Oznacza to, że przyciski Wstecz i Dalej będą działać tak, jak chcesz. ;-)
  3. Jeśli przeglądarka nie obsługuje wspaniałych metod historii HTML 5, po prostu najlepiej wyczyść adres URL, ustawiając skrót na pusty ciąg znaków. Jest to kiepski powrót, ponieważ wciąż pozostawia końcowy skrót (przykład.com/#), a także dodaje pozycję historii, więc przycisk Wstecz zabierze Cię z powrotem #_-_.

Dowiedz się więcej o history.replaceState.

Dowiedz się więcej o window.location.

Paul Schwarz
źródło
2
Dla mnie też działało idealnie. Drugie rozwiązanie pozbywa się parametrów zapytania.
AdeelMufti
Robi to samo w przypadku Google Omniauth, więc pojawia się błąd, że żadna trasa nie pasuje, dołącza # (hashtag) na żądanie https: //.....herokua‌ pp.com/auth/google_oa‌ uth2 / callback? state = 1‌ 9feaacfe23423jh5jhhGS‌ DFwb419049ebb18dabdf8‌ i kod = 4 / glrY3-mSlTzwe‌ rwERTEG334eXcn3hOSxGu‌ c51BAlglPa4AU #
Shalafister's
Pracowało dla mnie lepiej niż rozwiązanie @Ryan, ponieważ nie usuwa zapytania.
olivmir
To rozwiązanie działało lepiej niż rozwiązanie Ryana. Przekazuję niektóre parametry do adresu URL po przejściu przez uwierzytelnianie na Facebooku i rozwiązanie Ryana, z jakiegoś powodu po prostu usuwam każdy parametr z adresu URL. To rozwiązanie działa idealnie w moim przypadku.
BlueSun3k1
59

jeśli chcesz usunąć pozostałe „#” z adresu URL

$(window).on('load', function(e){
  if (window.location.hash == '#_=_') {
    window.location.hash = ''; // for older browsers, leaves a # behind
    history.pushState('', document.title, window.location.pathname); // nice and clean
    e.preventDefault(); // no page reload
  }
})
likebeats
źródło
6
$ (window) .on ('load', funkcja (e) {/ * kod likebeats * /} działa.
ISHITOYA Kentaro
1
używam tego kodu przez zmianę e.preventDefault (); to event.preventDefault ();
printf
Ten kod zakłada, że ​​jQuery i detektor zdarzeń onWindowReady przejmują argument e.
Jason Sperske,
49

Zostało to wdrożone przez Facebooka ze względów bezpieczeństwa. Oto wyjaśnienie Erica Osgooda, członka zespołu Facebooka:

Zostało to oznaczone jako „zgodnie z projektem”, ponieważ zapobiega potencjalnej luce w zabezpieczeniach.

Niektóre przeglądarki dołączają fragment skrótu z adresu URL na końcu nowego adresu URL, do którego zostały przekierowane (jeśli ten nowy adres URL sam nie ma fragmentu skrótu).

Na przykład, jeśli example1.com zwraca przekierowanie do example2.com, to przeglądarka przechodząca do example1.com # abc przejdzie do example2.com # abc, a zawartość fragmentu skrótu z example1.com będzie dostępna dla skryptu na example2 .com.

Ponieważ możliwe jest przekierowanie jednego uwierzytelnienia do drugiego, możliwe byłoby, aby wrażliwe dane uwierzytelniające z jednej aplikacji były dostępne dla innej.

Aby temu zaradzić, należy dodać nowy fragment skrótu do adresu URL przekierowania, aby zapobiec temu zachowaniu przeglądarki.

Jeśli niepokojąca jest estetyka lub zachowanie wynikowego adresu URL po stronie klienta, możliwe byłoby użycie window.location.hash (lub nawet własnego przekierowania po stronie serwera), aby usunąć obrażające znaki.

Źródło: https://developers.facebook.com/bugs/318390728250352/

Mark Murphy
źródło
9
To jedyna odpowiedź, która tak naprawdę wyjaśnia, dlaczego tak się dzieje, dzięki, myślę, że teraz zostawiam obrażające znaki w moich adresach URL, ponieważ wiem, że to nie problem.
stephenmurdoch
1
Jest to również realizowane przez Tumblr w ich przekierowaniach. (od połowy 1919 r.) Dzięki za wskazanie wyjaśnienia FB. Łatwo rozwiązany w uproszczonej aplikacji Paszport, po prostu wskazując udane przekierowanie na „/ #” zamiast po prostu „/” (co tłumaczy, dlaczego widzę więcej tylnych oktorpsów w sieci, myślę ...)
RL Brown
10

Nie jestem pewien, dlaczego to robią, ale możesz to obejść, resetując skrót u góry strony:

if (window.location.hash == "#_=_")
  window.location.hash = "";
mixmasteralan
źródło
9

Możesz również określić swój skrót w redirect_uriparametrze wywołania zwrotnego na Facebooku, co może być pomocne w pewnych okolicznościach, np /api/account/callback#home. Gdy zostaniesz przekierowany z powrotem, będzie to przynajmniej skrót, który odpowiada znanej trasie, jeśli używasz backbone.js lub podobnego (nie jestem pewien co do jquery mobile).

pkiddie
źródło
8

Facebook używa ramki, a wszystko wewnątrz niej działa przy użyciu komunikacji AJAX. Największym problemem w tym przypadku jest zachowanie bieżącego stanu strony. O ile rozumiem, Facebook postanowił użyć symulowanych kotwic. Oznacza to, że jeśli gdzieś kliknąłeś, symulują to jako zakotwiczenie na twojej stronie, a kiedy rozpoczyna się komunikacja AJAX, zmieniają również bit zakotwiczenia twojego adresu URL.

To rozwiązanie pomaga normalnie przy ponownym ładowaniu strony (nie ENTER, naciśnij F5), ponieważ przeglądarka wysyła cały adres URL z kotwicami do serwera Facebook. Dlatego Facebook wybiera najnowszy stan (to, co widzisz) i możesz odtąd kontynuować.

Gdy funkcja zwrotna powraca z #_=_nią, oznacza to, że strona była w stanie podstawowym przed opuszczeniem. Ponieważ ta kotwica jest analizowana przez przeglądarkę, nie musisz się o nią martwić.

Sándor Tóth
źródło
2
Jeśli masz framework javascript, taki jak szkielet lub ember, jest to problem, ponieważ wszystko po skrócie jest interpretowane przez router
Rudi
1
Identyfikatory fragmentów adresów URL („kotwice”) niewysyłane do przeglądarki na żądanie. To pytanie dotyczy także OAuth, a nie głównej witryny na komputery. Powodem tego jest bezpieczeństwo OAuth - zapobieganie atakom w wyniku spreparowania złośliwego identyfikatora URI przekierowania.
AndrewF
8

Duże irytujące, szczególnie dla aplikacji, które analizują URI, a nie tylko czytają $ _GET ... Oto hack, który rzuciłem razem ... Ciesz się!

<html xmlns:fb='http://www.facebook.com/2008/fbml'>
<head>
        <script type="text/javascript">
        // Get rid of the Facebook residue hash in the URI
        // Must be done in JS cuz hash only exists client-side
        // IE and Chrome version of the hack
        if (String(window.location.hash).substring(0,1) == "#") {
                window.location.hash = "";
                window.location.href=window.location.href.slice(0, -1);
                }
        // Firefox version of the hack
        if (String(location.hash).substring(0,1) == "#") {
                location.hash = "";
                location.href=location.href.substring(0,location.href.length-3);
                }
        </script>
</head>
<body>
URI should be clean
</body>
</html>
Jeremy Whitt
źródło
Zachowaj ostrożność przy przyjmowaniu założeń podczas analizowania danych, których nie tworzysz. Identyfikatory fragmentów URI podano już w RFC 1738 (w 1994 r.), Więc jeśli użyjesz poprawnego parsera URI, nigdy nie powinno to stanowić problemu.
AndrewF
6

Może to stać się poważnym problemem, jeśli używasz frameworka JS z adresami URL mieszania (/ #! /), Np. Angular. Rzeczywiście, Angular uzna adresy URL z fragmentem nie będącym hashbangiem za nieprawidłowe i zgłosi błąd:

Error: Invalid url "http://example.com/#_=_", missing hash prefix "#!".

Jeśli jesteś w takim przypadku (i przekierowujesz do katalogu głównego domeny), zamiast robić:

window.location.hash = ''; // goes to /#, which is no better

Po prostu wykonaj:

window.location.hash = '!'; // goes to /#!, which allows Angular to take care of the rest
neemzy
źródło
1.2+, to działa niesamowicie. Dla wersji 1.0 i niższych użyj window.location.hash = '';
Pradeep Mahdevu,
1
Tak, przetestowałem to tylko na 1.2, dzięki za specyfikację!
neemzy
A potem jest tryb
HTML5
5

Nie widzę związku tego problemu z Facebookiem AJAX. W rzeczywistości problem występuje również przy wyłączonym JavaScript i logowaniu opartym wyłącznie na przekierowaniach.

Przykładowa wymiana z Facebookiem:

1. GET <https://www.facebook.com/dialog/oauth?client_id=MY_APP_ID&scope=email&redirect_uri=MY_REDIRECT_URL> RESPONSE 302 Found Location: <https://www.facebook.com/connect/uiserver.php?[...]>  
2. GET <https://www.facebook.com/connect/uiserver.php?[...]> RESPONSE 302 Found MY_REDIRECT_URL?code=FB_CODE#_  
3. GET MY_REDIRECT_URL?code=FB_CODE#_  

Zdarza się tylko dla Firefoksa.

Sebastian Tusk
źródło
4

Dodanie tego do mojej strony przekierowania naprawiło dla mnie problem ...

if (window.location.href.indexOf('#_=_') > 0) {
    window.location = window.location.href.replace(/#.*/, '');
}
neokio
źródło
1
powoduje to zmianę lokalizacji okna, inicjując odświeżenie strony
rpearce
3

Za pomocą kątowego i kątowego routera interfejsu użytkownika możesz to naprawić

    app.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {

      // Make a trailing slash optional for all routes
      // - Note: You'll need to specify all urls with a trailing slash if you use this method.
      $urlRouterProvider.rule(function ($injector, $location) {
        /***
        Angular misbehaves when the URL contains a "#_=_" hash.

        From Facebook:
          Change in Session Redirect Behavior
          This week, we started adding a fragment #_=_ to the redirect_uri when this field is left blank.
          Please ensure that your app can handle this behavior.

        Fix:
          http://stackoverflow.com/questions/7131909/facebook-callback-appends-to-return-url#answer-7297873
        ***/
        if ($location.hash() === '_=_'){
          $location.hash(null);
        }

        var path = $location.url();

        // check to see if the path already has a slash where it should be
        if (path[path.length - 1] === '/' || path.indexOf('/?') > -1) {
          return;
        }
        else if (path.indexOf('?') > -1) {
          $location.replace().path(path.replace('?', '/?'));
        }
        else {
          $location.replace().path(path + '/');
        }
      });

      // etc ...
    });
});
rebeliant
źródło
nie działa tutaj - trasa zmienia się przed zastosowaniem reguły ()
Maël Nison
3

Jeśli korzystasz z routera vue, możesz dołączyć do listy tras:

{
  path: '/_=_',
  redirect: '/', // <-- or other default route
},
Sławomir
źródło
2

Niedawno wprowadzono zmianę w sposobie, w jaki Facebook obsługuje przekierowania sesji. Zobacz „Zmiana w zachowaniu przekierowania sesji” w poście na blogu Operation Developer Love w tym tygodniu .

Dhiren Patel
źródło
1
Nie jestem pewien, o czym on tu
mówi
2

Dla mnie przekierowuję JavaScript na inną stronę, aby się go pozbyć #_=_. Poniższe pomysły powinny zadziałać. :)

function redirect($url){
    echo "<script>window.location.href='{$url}?{$_SERVER["QUERY_STRING"]}'</script>";        
}
Eng Cy
źródło
myślę, że to nie jest dobry pomysł, ponieważ tworzysz wiele bezużytecznych żądań
Jacek Pietal
1

Obejściem, które zadziałało dla mnie (przy użyciu Backbone.js), było dodanie „# /” na końcu adresu URL przekierowania przekazywanego do Facebooka. Facebook zachowa podany fragment i nie doda własnego „_ = _”.

Po zwrocie Backbone usunie część „# /”. W przypadku AngularJS, dołączając „#!” na zwrotny adres URL powinien działać.

Zauważ, że identyfikator fragmentu oryginalnego adresu URL jest zachowywany podczas przekierowania (przez kody stanu HTTP 300, 301, 302 i 303) przez większość przeglądarek, chyba że adres URL przekierowania również ma identyfikator fragmentu. To wydaje się być zalecanym zachowaniem .

Jeśli używasz skryptu procedury obsługi, który przekierowuje użytkownika w inne miejsce, możesz dodać „#” do adresu URL przekierowania, aby zastąpić identyfikator fragmentu pustym ciągiem.

Ivo Smits
źródło
1

Wiem, że ta odpowiedź jest spóźniona, ale jeśli używasz paszportów, możesz chcieć to zobaczyć.

return (req, res, next) => {
    console.log(req.originalUrl);
    next();
};

Napisałem to oprogramowanie pośrednie i zastosowałem je do ekspresowej instancji serwera, a oryginalny adres URL, który mam, jest bez "#_=_". Wygląda na to, że gdy zastosujemy instancję passporJS jako oprogramowanie pośrednie do instancji serwera, nie przyjmuje ona tych znaków, ale jest widoczna tylko na pasku adresu naszych przeglądarek.

Kryszna
źródło
3
„# _ = _” jest dostępny tylko na kliencie. Recenzja: en.wikipedia.org/wiki/Fragment_identifier
alditis
1

Używam tego, aby również usunąć symbol „#”.

<script type="text/javascript">
    if (window.location.hash && window.location.hash == '#_=_') {
        window.location.href = window.location.href.split('#_=_')[0];
    }
</script>
Szymon
źródło
0

Używam Angular 2 (RC5) i tras opartych na haszowaniu, robię to:

const appRoutes: Routes = [
  ...
  {path: '_', redirectTo: '/facebookLoginSuccess'},
  ...
]

i

export const routing = RouterModule.forRoot(appRoutes, { useHash: true });

O ile rozumiem, =znak na trasie jest interpretowany jako część opcjonalnej definicji parametrów trasy (patrz https://angular.io/docs/ts/latest/guide/router.html#!#optional-route-parameters ) , więc nie bierze udziału w dopasowywaniu trasy.

rcomblen
źródło
0

Dla użytkowników PHP SDK

Rozwiązałem problem po prostu usuwając dodatkową część przed przekazaniem.

 $loginURL = $helper->getLoginUrl($redirectURL, $fbPermissions);
 $loginURL = str_replace("#_=_", "", $loginURL);
 header("Location: " . $loginURL);
Nanoripper
źródło
0

Spowoduje to usunięcie dołączonych znaków do Twojego adresu URL

<script type="text/javascript">
 var idx=window.location.toString().indexOf("#_=_"); 
   if (idx > 0) { 
     window.location = window.location.toString().substring(0, idx); 
   } 
</script>
Rotimi
źródło
0

Najprostszym i czystym rozwiązaniem do usunięcia „# _ = _” (PHP):

Zamiast „nagłówka („ Lokalizacja: xxx.php ”);” użyć „echo (” location.href = 'xxx.php'; ”);”

użytkownik3806621
źródło