Modify rule S5146 - Support Location header (#315)

This commit is contained in:
Pierre-Loup 2021-11-05 14:12:29 +01:00 committed by GitHub
parent b1bebf13ec
commit 2378417fdd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 116 additions and 53 deletions

View File

@ -5,21 +5,18 @@ include::../description.adoc[]
---- ----
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace WebApplicationDotNetCore.Controllers public class HomeController : Controller
{ {
public class RSPEC5146OpenRedirectNoncompliantController : Controller
public IActionResult RedirectMe(string url)
{ {
public IActionResult Index() return Redirect(url);
{ }
return View();
}
private readonly string[] whiteList = { "https://www.sonarsource.com" }; public IActionResult SetLocationHeader(string url)
{
public IActionResult RedirectMe(string url) Response.Headers["Location"] = url; // Noncompliant
{ return StatusCode(302);
return Redirect(url);
}
} }
} }
---- ----
@ -27,30 +24,21 @@ namespace WebApplicationDotNetCore.Controllers
== Compliant Solution == Compliant Solution
---- ----
using System.Linq;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace WebApplicationDotNetCore.Controllers public class HomeController : Controller
{ {
public class RSPEC5146OpenRedirectCompliantController : Controller private readonly string[] whiteList = { "/", "/login", "/logout" };
public IActionResult RedirectMe(string url)
{ {
public IActionResult Index() // Match the incoming URL against a whitelist
if (!whiteList.Contains(url))
{ {
return View(); return BadRequest();
} }
private readonly string[] whiteList = { "https://www.sonarsource.com" }; return Redirect(url);
public IActionResult RedirectMe(string url)
{
// Match the incoming URL against a whitelist
if (!whiteList.Contains(url))
{
return BadRequest();
}
return Redirect(url);
}
} }
} }
---- ----

View File

@ -8,6 +8,13 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IO
resp.sendRedirect(location); // Noncompliant resp.sendRedirect(location); // Noncompliant
} }
---- ----
----
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String location = req.getParameter("url");
resp.setStatus(302);
resp.setHeader("Location", location); // Noncompliant
}
----
== Compliant Solution == Compliant Solution

View File

@ -3,11 +3,18 @@ include::../description.adoc[]
== Noncompliant Code Example == Noncompliant Code Example
---- ----
function (req, res) { function redirect(req, res) {
const url = req.query.url; // user controlled input const url = req.query.url; // user controlled input
res.redirect(url); // Noncompliant res.redirect(url); // Noncompliant
} }
function setLocationHeader(req, res) {
const url = req.query.url; // user controlled input
res.location(url); // Noncompliant
res.sendStatus(302);
}
---- ----
== Compliant Solution == Compliant Solution
@ -23,7 +30,7 @@ function isValidUrl(url) {
return false; return false;
} }
function (req, res) { function redirect(req, res) {
const url = req.query.url; // user controlled input const url = req.query.url; // user controlled input
if(isValidUrl(url)) { if(isValidUrl(url)) {

View File

@ -8,22 +8,70 @@ This problem could be mitigated in any of the following ways:
== Noncompliant Code Example == Noncompliant Code Example
Symfony
---- ----
$url = $this->request->getQuery("url"); public function redirect(Request $request)
return $this->redirect($url); // Noncompliant {
$url = $request->query->get('url');
return $this->redirect($url); // Noncompliant
}
public function setLocatioHeader(Request $request)
{
$url = $request->query->get('url');
$response = new Response('Redirecting...', 302);
$response->headers->set('Location', $url); // Noncompliant
return $response;
}
----
Laravel
----
public function redirect(Request $request)
{
$url = $request->input('url');
return $this->redirect($url); // Noncompliant
}
public function setLocatioHeader(Request $request)
{
$url = $request->input('url');
return response("", 302)
->header('Location', $url) // Noncompliant
}
---- ----
== Compliant Solution == Compliant Solution
Symfony
---- ----
$whitelist = array( public function redirect(Request $request)
"https://www.sonarsource.com/" {
); $url = $request->query->get('url');
$url = $this->request->getQuery("url"); $allowedUrls = ["/index", "/login", "/logout"];
if (in_array($url, $whitelist)) {
return $this->redirect($url); if (in_array($url, $allowedUrls, true)) {
} else { return $this->redirect($url);
throw new ForbiddenException(); } else {
$this->redirect("/");
}
}
----
Laravel
----
public function redirect(Request $request)
{
$url = $request->input('url');
$allowedUrls = ["/index", "/login", "/logout"];
if (in_array($url, $allowedUrls, true)) {
return $this->redirect($url);
} else {
return redirect("/");
}
} }
---- ----

View File

@ -5,12 +5,19 @@ include::../description.adoc[]
Flask Flask
---- ----
from flask import request, redirect from flask import request, redirect, Response
@app.route('move') @app.route('flask_redirect')
def move(): def flask_redirect():
url = request.args["next"] url = request.args["next"]
return redirect(url) # Noncompliant return redirect(url) # Noncompliant
@app.route('set_location_header')
def set_location_header():
url = request.args["next"]
response = Response("redirecting...", 302)
response.headers['Location'] = url # Noncompliant
return response
---- ----
Django Django
@ -18,9 +25,15 @@ Django
---- ----
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
def move(request): def http_responser_redirect(request):
url = request.GET.get("next", "/") url = request.GET.get("next", "/")
return HttpResponseRedirect(url) # Noncompliant return HttpResponseRedirect(url) # Noncompliant
def set_location_header(request):
url = request.GET.get("next", "/")
response = HttpResponse(status=302)
response['Location'] = url # Noncompliant
return response
---- ----
== Compliant Solution == Compliant Solution
@ -28,12 +41,12 @@ def move(request):
Flask Flask
---- ----
from flask import request, redirect, url_for from flask import request, redirect, Response, url_for
@app.route('move') @app.route('flask_redirect')
def move(): def flask_redirect():
endpoint = request.args["next"] endpoint = request.args["next"]
return redirect(url_for(endpoint)) # Compliant return redirect(url_for(endpoint)) # Compliant
---- ----
Django Django
@ -44,11 +57,11 @@ from urllib.parse import urlparse
DOMAINS_WHITELIST = ['www.example.com', 'example.com'] DOMAINS_WHITELIST = ['www.example.com', 'example.com']
def move(request): def http_responser_redirect(request):
url = request.GET.get("next", "/") url = request.GET.get("next", "/")
parsed_uri = urlparse(url) parsed_uri = urlparse(url)
if parsed_uri.netloc in DOMAINS_WHITELIST: if parsed_uri.netloc in DOMAINS_WHITELIST:
return HttpResponseRedirect(url) # Compliant return HttpResponseRedirect(url) # Compliant
return HttpResponseRedirect("/") return HttpResponseRedirect("/")
---- ----