Built-in protections
- The Public Area and the Administration Area of TCExam are physically separated on the filesystem. This allows to add further access restrictions based on the filesystem and the web server, and the application ships
.htaccessaccess controls. - To access the various TCExam resources you need a valid account (user/password) and a level equal to or greater than the requested resource level. The level of test takers and Public areas is 1 by default. TCExam also supports RADIUS, LDAP and CAS authentication, as well as custom authentication methods.
- Passwords are stored using a modern password hashing algorithm.
- Test takers may access a particular test only if they belong to a group enabled for that test.
- Test takers may access a particular test only if their IP address is enabled for that test.
- Test takers may execute a particular test only one time (unless authorized by an administrator, or the test is explicitly marked as repeatable).
- TCExam may generate a unique test for each user by randomly selecting and ordering questions and alternative answers. This drastically reduces or eliminates the risk of copying between test takers.
- The test time is kept by the server. At the end of the available time the test is automatically closed.
- The full activity of the test taker is logged on the server, including statistical data (e.g. reaction time) and security data (e.g. IP address).
- Another security benefit comes from TCExam being Open Source Software, because it is widely known that security by obscurity is not a secure practice in the long run.
- If you use TCExam in a controlled environment (e.g. a computer room) you can also add all the traditional security measures such as keyloggers, video cameras and so on.
Security hardening in version 17
Version 17 includes a dedicated security-hardening pass:
- Per-installation random secret. The shipped
K_RANDOM_SECURITYvalue is now a placeholder; the installer generates a unique random secret for every install. The secret is used to generate session IDs, client fingerprints, CSRF tokens and SSO passwords. - CSPRNG-based codes. Verification codes and one-time values are generated with a cryptographically secure pseudo-random number generator.
- Safe file/URL access. File and URL access is restricted through configurable path/host allow-lists (
K_FILE_ALLOWED_PATHS/K_FILE_ALLOWED_HOSTS, and theK_PDF_ALLOWED_*options for the PDF engine). - Restricted registration groups. Self-registration can be limited to specific groups with
K_USRREG_ALLOWED_GROUPS.
Uploaded files, CSRF and backups (17.3)
- Upload checks. Media uploads reject markup and script types (such as
xml,html,jsandsvgz) even when they are listed inK_ALLOWED_UPLOAD_EXTENSIONS, and every upload rejects server script types. File content is checked against the extension, file names are sanitized and existing files are never overwritten. File Manager rename and new directory names follow the same rules. - Plain SVG only. SVG files are accepted only as well-formed UTF-8 documents with static graphics: no scripts, event handlers, links, animations, external references (including CSS
url(),@import,image(),image-set(),cross-fade()andsrc()), entity declarations, DTD subsets or editor-specific namespaces. See the Upgrade Guide for how to export plain SVG files from common editors. - Imports outside the cache. User, question and SSL certificate imports are read from the PHP temporary upload directory and are not copied into
cache/. - No embedded objects. Previews and
[object]tags display images, audio and video with<img>,<audio>and<video>, and every other file type as a download link. - CSRF tokens. Every
POSTrequest with form data or files must carry a validcsrf_token; otherwise the request stops with HTTP status 403 before any data is processed. - Backup password. Creating, restoring and downloading a backup requires the current user's password, with a 15-minute lockout after 5 wrong attempts. Users signed in through an SSL client certificate, CAS or Shibboleth are not asked for a password.
- Passive
cache/folder. The shippedcache/.htaccessand the Docker Apache configuration servecache/as passive content: no script execution,X-Content-Type-Options: nosniff, a sandboxedContent-Security-Policy, and markup types sent as downloads.
Access control (17.4)
- Page levels. A page that does not set its own level requires the administrator level. The results PDF, the test PDF generator and the user results export require the results or tests operator level.
- Operator scope. Operators see and change only the results of the tests they are authorized for, including the "all tests" view, the user filter and the bulk lock, unlock, extend-time and delete actions. Subjects, questions and answers can be added or moved only under a module, subject or question the operator is authorized for, and only authorized subjects can be added to a test.
- OMR imports. OMR answer sheets are accepted only for authorized tests and only when every question and answer belongs to the test. The QR-Code data is decoded without instantiating objects.
- No PDF email token. Emailed PDF reports are generated in-process; the
?email=token oftce_pdf_results.phphas been removed. - Session cookie only. The session ID is read only from the
PHPSESSIDcookie, never from a URL parameter. - Log and backup directories.
admin/log/.htaccess,public/log/.htaccessandadmin/backup/.htaccessdeny web access, with matching rules in the Docker Apache configuration.
Exam pages, LaTeX, OTP and installer (17.5)
- Exam test logs. The exam page displays and stores answers only for test logs of the examinee's active attempt: other examinees' questions and answers cannot be displayed, and answers of earlier attempts cannot be changed.
- LaTeX.
[tex]in free-text answers and test comments is not compiled. pdflatex runs with restricted file access and without shell escape, and pdfTeX primitives,\scantokens,\immediateand\shipoutare rejected. See the LaTeX rendering page. - Output escaping. Names and test names are HTML-escaped on the online users page, the results pages, the module editor and the public test list; answer text and test comments are escaped on the exam page and in the PDF report.
- One-Time-Password. The code is checked against the key of the user who logs in, codes with a leading zero are accepted, and a used code is blocked only for that user.
- User editor. Editors below the administrator level can only edit their own account and users with a lower level.
- Installer. The web installer refuses to run on an installed instance and accepts only
POST.install/phpinfo.phpandadmin/code/tce_update.phphave been removed, and the Docker image denies web access toinstall/. - Sessions and CSRF. A new session ID is issued at login. CSRF tokens are HMAC-SHA256 values that depend on the full session ID.
- Test IP ranges. The allowed IP ranges of a test are checked against the address of the current request.
- LDAP, CAS and SSL certificates. The LDAP user name is escaped in the search filter. CAS validates the server certificate when
K_CAS_SERVER_CA_CERTis set. SSL client certificate login accepts certificates that have not expired and does not repeat the login for a user who is already logged in.
Isolating the administration area
The /admin/ path manages users, questions, tests and results. Treat it as a separate, internal service, so that TCExam's own login is not the only barrier:
- Separate entry point. Serve
/admin/from a dedicated virtual host or hostname and deny/admin/on the public host. SetK_PATH_URL_ADMINinshared/config/tce_paths.phpto the base URL of the admin host, and leaveK_COOKIE_DOMAINempty so that the session cookie is host-only. - Network restriction. Make the admin host reachable only through a VPN or an identity-aware access gateway, and drop all other traffic with a firewall. A firewall cannot filter by URL path: give the admin area its own listener (hostname with its own IP address, port or node) first.
- Second authentication layer. Require TLS client certificates, a reverse-proxy SSO with MFA, or at least HTTP Basic authentication over HTTPS, with credentials separate from TCExam accounts.
- Serve only what is needed. Under
/admin/, serve onlyadmin/code/*.phpandadmin/styles/, and denyadmin/backup/,admin/log/,admin/doc/,admin/config*/andpublic/log/. The admin pages also load files fromshared/jscripts/, theshared/styles andcache/. - Rate limiting, headers and logging. Rate-limit
/admin/requests and loginPOSTs, sendX-Frame-Options: DENY,Referrer-Policy: no-referrerandStrict-Transport-Security, and write a dedicated access log for the admin host. - Split deployment (optional). On exam-delivery nodes that only serve
public/, do not installadmin/; run administration from an internal node against the same database.
The comparison of network options and the Apache, nginx, nftables and ufw examples are in the "Isolating the administration area" section of SECURITY.md.
Recommended hardening checklist
- After installation, delete the
install/directory and set restrictive file permissions (see the Installation page). - Log in with the default
admin/1234account, create a new level-10 administrator with a strong password and remove the defaultadminuser immediately. - Serve TCExam over HTTPS and isolate the
admin/folder as described in Isolating the administration area. - Keep the
admin/backup/folder unreachable from the web (or move it elsewhere and setK_PATH_BACKUPinshared/config/tce_paths.php), and keep the.htaccessfiles ofadmin/backup/,admin/log/andpublic/log/. On nginx, deny these three paths in the server block. - Keep
cache/.htaccessin place and enablemod_headerswithAllowOverride AuthConfig FileInfo(orAll) for the TCExam folder. On nginx, add the equivalentcache/rules listed in the Upgrade Guide. - When upgrading from a version older than 17.3.0, review the files already present in
cache/and the[object]tags in existing questions, as described in the Upgrade Guide. - When upgrading from a version older than 17.5.0, delete
cache/latex_*.pngto remove images generated from examinee text. - With CAS authentication, set
K_CAS_SERVER_CA_CERTinshared/config/tce_cas.phpso that the CAS server certificate is validated. - Keep PHP, the database and the Composer dependencies up to date.
For the full security policy and how to report a vulnerability, see SECURITY.md. Please report security issues privately instead of opening a public issue.