Skip to content

Security: Systemic Missing Role-Based Access Control — Students Can Modify Attendance and Marks #49

@lighthousekeeper1212

Description

@lighthousekeeper1212

Summary

During an authorized security audit, 16 security vulnerabilities (6 CRITICAL) were identified in College-ERP. The most severe issue is a systemic lack of role-based access control: all teacher endpoints use @login_required but never verify the user's role, allowing any authenticated student to perform teacher-level operations — including modifying attendance records and entering marks.


Findings

1. Systemic Role Bypass (CRITICAL)

All 16+ teacher views use @login_required but zero check whether the authenticated user actually holds the teacher role. A student can access every teacher endpoint simply by navigating directly to /teacher/... URLs.

This includes write operations:

  • Cancel classes
  • Submit and toggle attendance
  • Enter and confirm marks
  • Create extra class sessions

Root cause: @login_required only verifies "is someone logged in," not "is this person a teacher."

2. Student-to-Teacher Privilege Escalation (CRITICAL)

A student account can perform the following teacher-only actions:

Action Function Location
Cancel any class cancel_class views.py:80
Submit attendance for any class confirm views.py:113
Toggle any attendance record (present ↔ absent) change_att views.py:150
Enter marks for any student marks_confirm views.py:310
Create extra class sessions e_confirm views.py:169

Impact: Complete academic integrity compromise. A student can set their own attendance to 100% and assign themselves any marks.

3. IDOR on Student Data (HIGH)

The attendance(stud_id) and marks_list(stud_id) views accept a student ID from the URL but do not verify that request.user.student.USN == stud_id. Any authenticated user can view any student's attendance and marks records by enumerating student IDs.

4. Teacher-to-Teacher IDOR (HIGH)

Teacher views accept teacher_id from the URL without verifying it matches request.user.teacher.id. This allows Teacher A to operate on Teacher B's classes — viewing, modifying attendance, and entering marks for classes they do not teach.

5. Predictable Passwords (CRITICAL)

Passwords are generated as firstname_birthyear (e.g., john_1998) during user creation (views.py:367, 406). Both the user's name and date of birth are visible to other users through attendance and marks views. There is no forced password change on first login. This makes every account trivially compromisable.

6. Hardcoded SECRET_KEY (HIGH)

The Django SECRET_KEY is committed directly to source code in settings.py:23. Anyone with access to the repository can forge session cookies and escalate privileges, bypassing all authentication entirely.


Impact Summary

Severity Count Examples
CRITICAL 6 Role bypass on all teacher views, privilege escalation, predictable passwords
HIGH 10 Student data IDOR, teacher-to-teacher IDOR, hardcoded SECRET_KEY

Worst-case scenario: A student logs in, navigates to teacher URLs, sets their own attendance to 100%, enters maximum marks for themselves, and compromises other accounts using predictable passwords — all without any exploit tooling.


Recommended Fixes

1. Add Role-Checking Decorators

Create and apply @teacher_required and @student_required decorators that verify the user's role before granting access:

from functools import wraps
from django.http import HttpResponseForbidden

def teacher_required(view_func):
    @wraps(view_func)
    @login_required
    def wrapper(request, *args, **kwargs):
        if not hasattr(request.user, 'teacher'):
            return HttpResponseForbidden("Access denied.")
        return view_func(request, *args, **kwargs)
    return wrapper

Apply @teacher_required to all teacher views and @student_required to all student views.

2. Add Ownership Checks

In every view that accepts a resource ID (student ID, teacher ID, class ID), verify the resource belongs to the requesting user:

# Example for teacher views
if teacher_id != request.user.teacher.id:
    return HttpResponseForbidden("Access denied.")

3. Generate Random Passwords

Replace the predictable firstname_birthyear pattern with cryptographically random passwords and enforce a password change on first login:

from django.utils.crypto import get_random_string
password = get_random_string(12)

4. Move SECRET_KEY to Environment Variable

# settings.py
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')

Rotate the current key immediately, as it is already public.


Disclosure

This report was produced during an authorized security research audit. Findings are disclosed here because this repository does not have GitHub Private Vulnerability Reporting enabled or a SECURITY.md policy. No exploit code is included. The goal is to help improve the security of this project.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions