2016-06-06 12 views
5

Próbuję zapobiec otwartemu atakowi przekierowania. Proszę spojrzeć na kod poniżej i sprawdzić bezpieczeństwo:Czy zapobieganie atakom otwartych przekierowań w węzłach nodej jest bezpieczne?

var = require('url'); 

// http://example.com/login?redirect=http://example.com/dashboard 
app.route('/login', function (req, res, next) { 
    var redirect = req.query.redirect, 
     paths = url.parse(redirect); 

    if (paths.host !== req.headers.host) { 
     return next(new Error('Open redirect attack detected')); 
    } 

    return res.redirect(redirect); 
}); 

Czy to wystarczy, aby zapobiec otwartemu atakowi przekierowania lub czy powinienem dodać coś jeszcze?

+1

Mogłoby być łatwiejsze w użyciu 'redirect = dashboard' a następnie w użyciu serwera' res.redirect ('http://example.com/' + req.query.redirect) 'W ten sposób będzie przekierowanie nigdy nie oddalaj się od swojego serwera. – Molda

+0

Dzięki. Jak nie kodować hasłem 'http: // example.com /' w 'res.redirect' ale używać' req.host' lub czegoś podobnego? – Erik

+0

'res.redirect ('http: //' + req.host + '/' + req.query.redirect);' – Molda

Odpowiedz

4

CWE-601: URL Redirection to Untrusted Site ('Open Redirect')

Opis Open Redirect:

parametru HTTP może zawierać wartość URL i może spowodować aplikację internetową, aby przekierować żądanie do określonego adresu URL. Modyfikując wartość adresu URL do złośliwej witryny, osoba atakująca może z powodzeniem uruchomić wyłudzanie informacji i wykraść dane uwierzytelniające użytkownika. Ponieważ nazwa serwera w zmodyfikowanym łączu jest identyczna z oryginalną witryną, próby wyłudzania informacji są bardziej wiarygodne.

Sugestia input validation strategii zapobiegania otwarty atak przekierowania:

siebie wszelkie wejście jest złośliwy. Użyj strategii sprawdzania danych wejściowych "zaakceptuj dobrze znane", tj. Użyj białej listy dopuszczalnych danych wejściowych ściśle zgodnych ze specyfikacjami. Odrzuć wszelkie dane wejściowe, które nie są ściśle zgodne ze specyfikacjami lub przekształć je w coś, co robi. Nie należy polegać wyłącznie na poszukiwaniu złośliwych lub zniekształconych danych wejściowych (tzn. Nie należy polegać na czarnej liście). Czarna lista prawdopodobnie ominie co najmniej jeden niepożądany sygnał wejściowy, zwłaszcza jeśli zmieni się środowisko kodu. To może dać napastnikowi wystarczająco dużo miejsca, aby ominąć zamierzoną walidację. Jednak czarne listy mogą być przydatne do wykrywania potencjalnych ataków lub określania, które dane wejściowe są tak źle sformułowane, że powinny zostać odrzucone całkowicie. Użyj białej listy zatwierdzonych adresów URL lub domen do przekierowania.

Zastosowanie req.headers.host, req.host lub req.hostname jest niepewny, ponieważ req.headers mogą być sfałszowane (np. Żądanie HTTP mają zwyczaj Host nagłówek dostępu do wyraźnej aplikacja napisana w kodzie poniżej)

var url = require('url'); 

app.get('/login', function (req, res, next) { 
    var redirect = req.query.redirect, 
     targetUrl = url.parse(redirect); 
    console.log('req.headers.host: [%s]', req.headers.host); 
    console.log('req.host: [%s]', req.host); 
    console.log('req.hostname: [%s]', req.hostname); 
    if (targetUrl.host != req.headers.host) { 
     return next(new Error('Open redirect attack detected')); 
    } 
    return res.redirect(redirect); 
}); 

Korzystając curl aby złożyć zamówienie:

$ curl -H 'Host: malicious.example.com' 'http://localhost:3012/login?redirect=http://malicious.example.com' -i 
HTTP/1.1 302 Found 
X-Powered-By: Express 
Location: http://malicious.example.com 
Vary: Accept 
Content-Type: text/plain; charset=utf-8 
Content-Length: 54 
Date: Mon, 13 Jun 2016 06:30:55 GMT 
Connection: keep-alive 

$ #server output 
req.headers.host: [malicious.example.com] 
req.host: [malicious.example.com] 
req.hostname: [malicious.example.com] 

proponuję użyć białej listy, aby sprawdzić poprawność danych wejściowych, kod przykład poniżej:

const WHITELIST_TO_REDIRECT = new Set(["localhost:3012", "www.realdomain.com"]); 

app.get('/login', function (req, res, next) { 
    var redirect = req.query.redirect, 
     targetUrl = url.parse(redirect); 
    console.log("req.hostname: [%s]", req.hostname); 
    console.log("url.host: [%s]", targetUrl.host); 
    if (!WHITELIST_TO_REDIRECT.has(targetUrl.host)) { 
     return next(new Error('Open redirect attack detected')); 
    } 

    return res.redirect(redirect); 
}); 
+0

Dzięki za odpowiedź. Czy możliwe jest nie używanie białej listy, ale pewne sprawdzanie poprawności za pomocą wyrażenia regularnego? – Erik

+0

Pewnie. Wyrażenie regularne reprezentuje także zakres białych list białych liter, "whitelist", ale jaśniej, gdy biała lista nie jest zbyt długa. – Shawyeok

+0

Jeśli nagłówek hosta został sfałszowany przez przeglądarkę użytkownika, ten użytkownik ma znacznie większe problemy z zabezpieczeniami. Chociaż jest to rozsądne podejście, nie widzę, co jest ofertą w porównaniu z nagłówkiem 'Host', z perspektywy bezpieczeństwa. – Brendon

1

W tej sytuacji użyłbym HMAC. Umożliwi to kontrolerowi logowania sprawdzenie, czy parametr redirect został wygenerowany przez osobę znającą tajny klucz.

Podczas generowania adresu URL "logowania" dodaje się do adresu url HMAC parametru redirect wraz z samym parametrem przekierowania.

Program do obsługi logowania może używać HMAC, aby zapewnić, że parametr redirect został wygenerowany przez zaufany serwer, który zna klucz tajny HMAC, zapobiegając tym samym otwartym atakom przekierowania.

np.

var crypto = require('crypto'); 
var secretKey = 'change-me'; 
var loginUrl = 'http://example.com/login' 

// called to work out where to redirect to for login 
function getLoginUrl(redirectBackUrl) { 
    var sig = crypto.createHmac('sha1', secretKey) 
        .update(redirectBackUrl) 
        .digest('hex'); 

    return loginUrl 
     + '?redirect=' 
     + encodeURIComponent(redirectBackUrl) 
     +'&sig=' + sig; 
} 

// called by the login function to ensure that the 
// redirect parameter is valid 
function isRedirectUrlValid(url, sig) { 
    var expectedSig 
      = crypto.createHmac('sha1', secretKey) 
        .update(url) 
        .digest('hex'); 

    return expectedSig === sig; 
} 
Powiązane problemy