Broken Access Control

Insecure Direct Object Reference in Python

Vulnerability: Insecure Direct Object Reference

Vulnerable Code:

pythonCopy code@app.route('/profile/<user_id>')
def profile(user_id):
    user = db.get_user(user_id)
    return render_template('profile.html', user=user)

Reason for vulnerability: Directly accessing user data by user_id without verifying that the current user has permission to view that data.

Fixed Code:

pythonCopy code@app.route('/profile/<user_id>')
@login_required
def profile(user_id):
    if user_id != current_user.id:
        abort(403)
    user = db.get_user(user_id)
    return render_template('profile.html', user=user)

Reason for fix: Adding access control checks ensures that users can only access their own data, preventing unauthorized access.


Vulnerable Code:

javaCopy codeString userId = request.getParameter("userId");
User user = userService.getUserById(userId);

Reason for vulnerability: No access control check, allowing any user to access any user data.

Fixed Code:

javaCopy codeString userId = request.getParameter("userId");
if (!currentUser.getId().equals(userId)) {
    throw new UnauthorizedException();
}
User user = userService.getUserById(userId);

Reason for fix: Check if the current user is authorized to access the requested user data.


$user_id = $_GET['user_id'];
$user_data = $db->query("SELECT * FROM users WHERE id = '$user_id'");

Reason for vulnerability: The code uses user-input to directly access a database record without proper authorization. Fixed Code 1 (PHP):

$user_id = $_GET['user_id'];
if (isset($_SESSION['user_id']) && $_SESSION['user_id'] == $user_id) {
$user_data = $db->query("SELECT * FROM users WHERE id = '$user_id'");
} else {
// handle unauthorized access
}

Reason for fix: The code checks if the user is authenticated and authorized to access the requested user data. Vulnerable Code 2 (Java):

public void doGet(HttpServletRequest request, HttpServletResponse response) {
String userId = request.getParameter("userId");
User user = userDao.getUser(userId);
// ...
}

Reason for vulnerability: The code uses user-input to directly access a user object without proper authorization. Fixed Code 2 (Java):

public void doGet(HttpServletRequest request, HttpServletResponse response) {
String userId = request.getParameter("userId");
if (request.isUserInRole("admin") || userId.equals(request.getRemoteUser())) {
User user = userDao.getUser(userId);
// ...
} else {
// handle unauthorized access
}
}

Reason for fix: The code checks if the user is authenticated and authorized to access the requested user data.


Vulnerable Code

@RestController
public class UserController {
    @GetMapping("/api/users/{userId}/profile")
    public ResponseEntity<UserProfile> getUserProfile(@PathVariable Long userId) {
        UserProfile profile = userService.getUserProfile(userId);
        return ResponseEntity.ok(profile);
    }
}

Reason for Vulnerability:

This endpoint doesn't check if the requesting user has permission to access the profile for the given userId, allowing any authenticated user to access any profile.

Fixed Code:

javaCopy@RestController
public class UserController {
    @GetMapping("/api/users/{userId}/profile")
    public ResponseEntity<UserProfile> getUserProfile(@PathVariable Long userId, Authentication authentication) {
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        if (!userService.canAccessProfile(userDetails.getUsername(), userId)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }
        UserProfile profile = userService.getUserProfile(userId);
        return ResponseEntity.ok(profile);
    }
}

Reason for Fix:

The fixed code checks if the authenticated user has permission to access the requested profile before returning it, preventing unauthorized access.

Java Example

Vulnerable Code:

javaCopy@WebServlet("/downloadFile")
public class FileDownloadServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fileName = request.getParameter("file");
        File file = new File("/path/to/files/" + fileName);
        // Code to send file as response
    }
}

Reason for Vulnerability:

This servlet allows downloading any file by specifying its name, potentially exposing sensitive files.

Fixed Code:

javaCopy@WebServlet("/downloadFile")
public class FileDownloadServlet extends HttpServlet {
    private static final String FILE_PATH = "/path/to/files/";
    private static final Set<String> ALLOWED_FILES = Set.of("public1.pdf", "public2.pdf");

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fileName = request.getParameter("file");
        if (!ALLOWED_FILES.contains(fileName)) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return;
        }
        File file = new File(FILE_PATH + fileName);
        if (!file.getCanonicalPath().startsWith(FILE_PATH)) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return;
        }
        // Code to send file as response
    }
}

Reason for Fix:

The fixed code checks if the requested file is in the allowed list and uses canonical path checking to prevent path traversal, ensuring only authorized files can be downloaded.

Python Example

Vulnerable Code:

pythonCopyfrom flask import Flask, request, jsonify
import json

app = Flask(__name__)

@app.route('/api/notes/<int:note_id>', methods=['GET'])
def get_note(note_id):
    with open('notes.json', 'r') as f:
        notes = json.load(f)
    if str(note_id) in notes:
        return jsonify(notes[str(note_id)])
    return jsonify({'error': 'Note not found'}), 404

if __name__ == '__main__':
    app.run(debug=True)

Reason for Vulnerability:

This endpoint allows any user to access any note by its ID without checking ownership or permissions.

Fixed Code:

pythonCopyfrom flask import Flask, request, jsonify
import json
from functools import wraps

app = Flask(__name__)

def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not request.headers.get('Authorization'):
            return jsonify({'error': 'No authorization token provided'}), 401
        # Implement proper token verification here
        return f(*args, **kwargs)
    return decorated_function

@app.route('/api/notes/<int:note_id>', methods=['GET'])
@login_required
def get_note(note_id):
    user_id = get_user_id_from_token(request.headers.get('Authorization'))
    with open('notes.json', 'r') as f:
        notes = json.load(f)
    if str(note_id) in notes and notes[str(note_id)]['owner_id'] == user_id:
        return jsonify(notes[str(note_id)])
    return jsonify({'error': 'Note not found or access denied'}), 404

def get_user_id_from_token(token):
    # Implement token verification and return user_id
    pass

if __name__ == '__main__':
    app.run(debug=True)

Reason for Fix:

The fixed code adds authentication and checks if the requesting user is the owner of the note before returning it, preventing unauthorized access.


Open Redirect

Example 1: Java

Vulnerable Code:

javaCopy codeString url = request.getParameter("url");
response.sendRedirect(url);

Reason for vulnerability: No validation of the URL, allowing open redirect attacks.

Fixed Code:

javaCopy codeString url = request.getParameter("url");
if (!isValidRedirectUrl(url)) {
    throw new IllegalArgumentException("Invalid URL");
}
response.sendRedirect(url);

Reason for fix: Validate the URL before redirecting to ensure it is an allowed destination.

Example 2: Python

Vulnerable Code:

pythonCopy code@app.route('/redirect')
def redirect():
    url = request.args.get('url')
    return redirect(url)

Reason for vulnerability: No validation of the URL, allowing open redirect attacks.

Fixed Code:

pythonCopy code@app.route('/redirect')
def redirect():
    url = request.args.get('url')
    if not is_valid_redirect_url(url):
        abort(400)
    return redirect(url)

Reason for fix: Validate the URL before redirecting to ensure it is an allowed destination.


Java Example

Vulnerable Code:

javaCopy@Controller
public class RedirectController {
    @GetMapping("/redirect")
    public String redirect(@RequestParam("url") String url) {
        return "redirect:" + url;
    }
}

Reason for Vulnerability:

This code allows redirection to any URL specified in the parameter, which can be exploited for phishing attacks.

Fixed Code:

javaCopy@Controller
public class RedirectController {
    private static final List<String> ALLOWED_DOMAINS = Arrays.asList("example.com", "safe-site.com");

    @GetMapping("/redirect")
    public String redirect(@RequestParam("url") String url) {
        if (isValidRedirectUrl(url)) {
            return "redirect:" + url;
        }
        return "redirect:/error";
    }

    private boolean isValidRedirectUrl(String url) {
        try {
            URL parsedUrl = new URL(url);
            return ALLOWED_DOMAINS.contains(parsedUrl.getHost());
        } catch (MalformedURLException e) {
            return false;
        }
    }
}

Reason for Fix:

The fixed code validates the redirect URL against a whitelist of allowed domains, preventing redirection to potentially malicious sites.

Java Example

Vulnerable Code:

javaCopy@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String redirectUrl = request.getParameter("redirect");

        if (isValidUser(username, password)) {
            response.sendRedirect(redirectUrl);
        } else {
            response.sendRedirect("/login?error=1");
        }
    }

    private boolean isValidUser(String username, String password) {
        // Authentication logic
    }
}

Reason for Vulnerability:

This servlet allows redirection to any URL after successful login, which can be exploited for phishing attacks.

Fixed Code:

javaCopy@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    private static final String DEFAULT_REDIRECT = "/dashboard";

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String redirectUrl = request.getParameter("redirect");

        if (isValidUser(username, password)) {
            if (isValidRedirectUrl(redirectUrl)) {
                response.sendRedirect(redirectUrl);
            } else {
                response.sendRedirect(DEFAULT_REDIRECT);
            }
        } else {
            response.sendRedirect("/login?error=1");
        }
    }

    private boolean isValidUser(String username, String password) {
        // Authentication logic
    }

    private boolean isValidRedirectUrl(String url) {
        return url != null && url.startsWith("/") && !url.contains("://");
    }
}

Reason for Fix:

The fixed code validates that the redirect URL is a relative path within the same domain, preventing open redirects to external sites.

Python Example

Vulnerable Code:

pythonCopyfrom flask import Flask, request, redirect

app = Flask(__name__)

@app.route('/redirect')
def redirect_url():
    url = request.args.get('url')
    return redirect(url)

if __name__ == '__main__':
    app.run(debug=True)

Reason for Vulnerability:

This Flask route allows redirection to any URL specified in the query parameter, which can be exploited for phishing attacks.

Fixed Code:

pythonCopyfrom flask import Flask, request, redirect, abort
from urllib.parse import urlparse, urljoin

app = Flask(__name__)

def is_safe_url(target):
    ref_url = urlparse(request.host_url)
    test_url = urlparse(urljoin(request.host_url, target))
    return test_url.scheme in ('http', 'https') and ref_url.netloc == test_url.netloc

@app.route('/redirect')
def redirect_url():
    url = request.args.get('url')
    if url and is_safe_url(url):
        return redirect(url)
    return abort(400)

if __name__ == '__main__':
    app.run(debug=True)

Reason for Fix:

The fixed code validates that the redirect URL is within the same domain as the application, preventing open redirects to external sites.

Last updated