safe-redirect

Try in Playground
python-djangoSecurityWarning

0

No tags

No CWE or CVE

It is important to remember that any data provided by the user, such as through URLs, payloads, or cookies, should be treated as unreliable and potentially harmful. This can lead to security issues if an attacker is able to redirect users to a harmful site through the use of tainted data. To prevent this, it is suggested to either validate user-provided data using a pre-approved list and rejecting any input that does not match, or to revise the application so it does not rely on user-provided data for redirects.

See more

  • MITRE, CWE-20 - Improper Input Validation
  • MITRE, CWE-601 - URL Redirection to Untrusted Site ('Open Redirect')
  • OWASP A5:2017-Broken Access Control
  • A01:2021 – Broken Access Control
  • SANS25

Ast Rule: function definition


safe-redirect

How to write a rule
function visit(node, filename, code) {
  if (!useImportFrom(node.context.imports, "django.http", "HttpResponseRedirect") && !useImportFrom(node.context.imports, "django.http", "HttpResponse")) {
    return;
  }

  if (!node.parameters) {
    return;
  }

  if (node.parameters.values.length !== 1) {
    return;
  }

  const firstParameterName = node.parameters.values[0].name;
  if (firstParameterName.astType !== "string") {
    return;
  }

  const requestVariableName = firstParameterName.value;

  const userVariablesFromRequest = [];
  const responsesVariables = [];

  const checkRedirectCall = (element, variables) => {
    if (element.astType === "return") {
      checkRedirectCall(element.value, variables);
    }

    if (element.astType === "assignment") {
      checkRedirectCall(element.right, variables);
    }


    if (element.astType === "functioncall") {
      if (element.functionName.value === "HttpResponseRedirect") {
        if (element.arguments.values && element.arguments.values.length === 1) {
          const firstArgument = element.arguments.values[0].value;
          if (firstArgument.astType === "string" && variables.includes(firstArgument.value)) {
            const error = buildError(element.start.line, element.start.col,
              element.end.line, element.end.col,
              "do not use user-input", "CRITICAL", "SECURITY");

            const edit = buildEditUpdate(firstArgument.start.line, firstArgument.start.col, firstArgument.end.line, firstArgument.end.col, `url_for(${firstArgument.value})`);

            addError(error);
          }
        }
      }
    }
  };


  node.content.elements && node.content.elements.forEach(e => {
    console.log(e.astType);
    // First, find variables inferred from the requests
    if (e.astType === "assignment") {
      if (e.left.astType === "string" && e.right.astType === "functioncall" && e.right.functionName.value === "get" && e.right.moduleOrObject && e.right.moduleOrObject.value === `${requestVariableName}.GET`) {
        userVariablesFromRequest.push(e.left.value);
        console.log(`pushing ${e.left.value}`);
      }
    }

    // Check if we build a response object
    if (e.astType === "assignment") {
      if (e.left.astType === "string" && e.right.astType === "functioncall" && e.right.functionName.value === "HttpResponse") {
        console.log("pushing");
        console.log(e.left.value);
        responsesVariables.push(e.left.value);
      }
    }

    // Check if we assign to a response
    if (e.astType === "assignment" && e.left.astType === "variableindex" && e.right.astType === "string" && userVariablesFromRequest.includes(e.right.value)) {
      const vi = e.left;
      if (responsesVariables.includes(vi.variable.value) && vi.index.value === "'Location'") {
        const error = buildError(e.start.line, e.start.col,
          e.end.line, e.end.col,
          "do not use user-input", "CRITICAL", "SECURITY");
        addError(error);

      }
    }

    // check if we have a redirect call
    checkRedirectCall(e, userVariablesFromRequest);
  });
}

error.py

Expected test result: no error

from django.http import HttpResponseRedirect

def http_responser_redirect(request):
    url = request.GET.get("next", "/")
    return HttpResponseRedirect(url)

def set_location_header(request):
    url = request.GET.get("next", "/")
    response = HttpResponse(status=302)
    response['Location'] = url
    return response
Add comment

Log in to add a comment


    Be the first one to leave a comment!

Codiga Logo
Codiga Hub
  • Rulesets
  • Playground
  • Snippets
  • Cookbooks
soc-2 icon

We are SOC-2 Compliance Certified

G2 high performer medal

Codiga – All rights reserved 2022.